using C4IT.FASD.Base;
using C4IT.Logging;
using C4IT.Security;
using C4IT.XML;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;

using C4IT.DataHistoryProvider.Base.Modules.Citrix;

using static C4IT.Logging.cLogManager;

namespace C4IT.DataHistoryProvider
{
    public class cDataHistoryConfigInfrastructure : cFasdBaseConfig
    {
        private const string constFileNameF4sdConfig = "F4SD-Infrastructure-Configuration.xml";
        private const string constFileNameF4sdSchema = "F4SD-Infrastructure-Configuration.xsd";
        private const string constConfigRootElement = "F4SD-Infrastructure-Configuration";

        public static readonly Guid ConfigId = new Guid("21CECFEE-0944-403F-A13F-11372BEE6905");

        public Guid getId() { return ConfigId; }

        public Dictionary<string, cCredential> Credentials { get; private set; } = new Dictionary<string, cCredential>();

        public Dictionary<string, cDataHistoryConfigDbConnection> DbConnections { get; private set; } = new Dictionary<string, cDataHistoryConfigDbConnection>();

        public cDataHistoryConfigDatabase HistoryDB { get; private set; } = null;
        public cF4SDAnalyticsConfigDatabase F4SDAnalyticsDB { get; private set; } = null;
        public cDataHistoryConfigClientAgent ClientAgent { get; private set; } = null;

        public cDataHistoryConfigM42Wpm M42Wpm { get; private set; } = null;

        public cDataHistoryConfigCitrix Citrix { get; private set; } = null;

        public cDataHistoryConfigNexthink Nexthink { get; private set; } = null;

        public cDataHistoryConfigActiveDirectory ActiveDirectory { get; private set; } = null;

        public Dictionary<string, cDataHistoryAzureTenant> AzureTenants { get; private set; } = null;  //ToDo: create a class for the azure configuration instead of only the tenants

        public cF4SDAuthorisation Authorisation { get; private set; } = null;


        internal cDataHistoryConfigInfrastructure() :
            base(constFileNameF4sdConfig, constFileNameF4sdSchema, constConfigRootElement)
        {
        }

        public override bool InstantiateProperties(XmlElement XRoot, cXmlParser Parser)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {
                // get the credentials
                var Creds = cCredentialXml.LoadListFromXml(XRoot, Parser, "Credentials");
                if (Creds == null || Creds.Count == 0 || Parser.LogMessages.Count > 0)
                    return false;
                Credentials = Creds;

                // get the db connections
                var Conns = cDataHistoryConfigDbConnection.LoadListFromXml(XRoot, Parser, Creds);
                if (Conns == null || Conns.Count == 0 || Parser.LogMessages.Count > 0)
                    return false;
                DbConnections = Conns;

                // get the history db config
                var _db = new cDataHistoryConfigDatabase(XRoot, Conns, Parser);
                if (_db == null || !_db.IsValid || Parser.LogMessages.Count > 0)
                    return false;
                HistoryDB = _db;

                var _db2 = new cF4SDAnalyticsConfigDatabase(XRoot, Conns, Parser);
                if (_db2 != null && _db2.IsValid && Parser.LogMessages.Count == 0)
                    F4SDAnalyticsDB = _db2;

                // get the active directory config
                var _ad = new cDataHistoryConfigActiveDirectory(XRoot, Credentials, Parser);
                if (_ad?.IsValid == true)
                    ActiveDirectory = _ad;

                AzureTenants = cDataHistoryAzureTenant.LoadFromXml(XRoot, Credentials, Parser);

                // get the client agent config
                var _cla = new cDataHistoryConfigClientAgent(XRoot, Credentials, Conns, Parser);
                if (_cla?.IsValid == true)
                    ClientAgent = _cla;

                // get the M42 WPM config
                var _m42wpm = new cDataHistoryConfigM42Wpm(XRoot, Credentials, Parser);
                if (_m42wpm?.IsValid == true)
                    M42Wpm = _m42wpm;

                var _citrix = new cDataHistoryConfigCitrix(XRoot, Credentials, Parser);
                if (_citrix?.IsValid == true)
                    Citrix = _citrix;

                // get the nexthink config
                var _nxt = new cDataHistoryConfigNexthink(XRoot, Credentials, Parser);
                if (_nxt?.IsValid == true)
                    Nexthink = _nxt;

                var _auth = new cF4SDAuthorisation(XRoot, Parser, this);
                if (_auth?.IsValid == true)
                    Authorisation = _auth;

                IsValid = true;
                return true;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }

            return false;
        }
    }

    public class cDataHistoryConfigDatabase : IConfigNodeValidation
    {
        public bool IsValid { get; set; } = false;

        public cDataHistoryConfigSqlConnection Connection { get; private set; } = null;

        public bool SearchForPhoneNumbers { get; set; } = false;
        public bool SearchWithLike { get; set; } = false;

        public int DaysToCache { get; set; } = 60;

        public List<cDataHistoryCleanupTimeFrame> CleanupTimeFrames = null;

        internal cDataHistoryConfigDatabase(XmlElement XNode, Dictionary<string, cDataHistoryConfigDbConnection> DbConnections, cXmlParser Parser)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {

                var XNode2 = XNode.SelectSingleNode("DataHistory-DB");
                if (!(XNode2 is XmlElement XNode3))
                {
                    Parser.AddMessage(XNode, $"The root element <{XNode.Name}> does not have a <DataHistory-DB> element.");
                    return;
                }

                Parser.EnterElement("DataHistory-DB");

                try
                {
                    var strConnection = cXmlParser.GetStringFromXmlAttribute(XNode2, "DB-Connection");
                    if (string.IsNullOrWhiteSpace(strConnection))
                    {
                        Parser.AddMessage(XNode, $"The element <{XNode2.Name}> does not have a 'DB-Connection' attribute.");
                        return;
                    }

                    if (!DbConnections.TryGetValue(strConnection, out var Conn))
                    {
                        Parser.AddMessage(XNode, $"Could not find a DB connection '{strConnection}' in element <{XNode2.Name}>.");
                        return;
                    }

                    if (!(Conn is cDataHistoryConfigSqlConnection SqlConn))
                    {
                        Parser.AddMessage(XNode, $"The DB connection '{strConnection}' in element <{XNode2.Name}> has to be a  SQL connection.");
                        return;
                    }

                    Connection = SqlConn;

                    SearchForPhoneNumbers = cXmlParser.GetBoolFromXmlAttribute(XNode2, "SearchForPhoneNumbers");

                    SearchWithLike = cXmlParser.GetBoolFromXmlAttribute(XNode2, "SearchWithLike");

                    DaysToCache = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "DaysToCache", DaysToCache);

                    CleanupTimeFrames = cDataHistoryCleanupTimeFrame.LoadFromXml(XNode3, Parser);

                    IsValid = true;

                }
                finally
                {
                    Parser.LeaveElement("DataHistory-DB");
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }
    }

    public class cF4SDAnalyticsConfigDatabase : IConfigNodeValidation
    {
        public bool IsValid { get; set; } = false;
        public bool AnonymousUser { get; set; }
        public int SessionTimeout { get; set; } = 60;
        public int CaseTimeout { get; set; } = 5;
        public int SessionCheckInterval { get; set; } = 20;
        public int CaseCheckInterval { get; set; } = 1;
        public cDataHistoryConfigSqlConnection Connection { get; private set; } = null;

        internal cF4SDAnalyticsConfigDatabase(XmlElement XNode, Dictionary<string, cDataHistoryConfigDbConnection> DbConnections, cXmlParser Parser)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {

                var XNode2 = XNode.SelectSingleNode("F4SDAnalytics-DB");
                if (!(XNode2 is XmlElement XNode3))
                {
                    Parser.AddMessage(XNode, $"The root element <{XNode.Name}> does not have a <F4SDAnalytics-DB> element.", LogLevels.Info);
                    return;
                }

                Parser.EnterElement("F4SDAnalytics-DB");

                try
                {
                    var strConnection = cXmlParser.GetStringFromXmlAttribute(XNode2, "DB-Connection");
                    if (string.IsNullOrWhiteSpace(strConnection))
                    {
                        Parser.AddMessage(XNode, $"The element <{XNode2.Name}> does not have a 'DB-Connection' attribute.");
                        return;
                    }

                    if (!DbConnections.TryGetValue(strConnection, out var Conn))
                    {
                        Parser.AddMessage(XNode, $"Could not find a DB connection '{strConnection}' in element <{XNode2.Name}>.");
                        return;
                    }

                    if (!(Conn is cDataHistoryConfigSqlConnection SqlConn))
                    {
                        Parser.AddMessage(XNode, $"The DB connection '{strConnection}' in element <{XNode2.Name}> has to be a  SQL connection.");
                        return;
                    }

                    Connection = SqlConn;
                    AnonymousUser = !cXmlParser.GetBoolFromXmlAttribute(XNode2, "EnableUserId");
                    SessionTimeout = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "SessionTimeout", 60);
                    CaseTimeout = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "CaseTimeout", 5);
                    SessionCheckInterval = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "SessionCheckInterval", 20);
                    CaseCheckInterval = cXmlParser.GetIntegerFromXmlAttribute(XNode2, "CaseCheckInterval", 2);
                    IsValid = true;

                }
                finally
                {
                    Parser.LeaveElement("F4SDAnalytics-DB");
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }
    }

    public class cDataHistoryCleanupTimeFrame : IConfigNodeValidation
    {
        private const string constXmlTag = "Cleanup-Timeframe";
        public bool IsValid { get; set; } = false;

        public int DowFrom = 0;
        public int DowTo = 0;
        public TimeSpan TimeFrom = new TimeSpan(0, 0, 0);
        public TimeSpan TimeTo = new TimeSpan(23, 59, 59);
        public TimeZoneInfo TimeZone = TimeZoneInfo.Utc;

        internal cDataHistoryCleanupTimeFrame(XmlElement XNode, cXmlParser Parser)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {
                TimeZone = cXmlParser.GetTimeZoneInfoFromAttribute(XNode, "Timezone");
                if (TimeZone == null)
                    TimeZone = TimeZoneInfo.Utc;

                var strFromDay = cXmlParser.GetStringFromXmlAttribute(XNode, "StartDay");
                var fromTime = cXmlParser.GetTimeSpanFromAttribute(XNode, "StartTime", null);
                var strToDay = cXmlParser.GetStringFromXmlAttribute(XNode, "StopDay");
                var toTime = cXmlParser.GetTimeSpanFromAttribute(XNode, "StopTime", null);

                if (fromTime == null || toTime == null)
                    return;

                TimeFrom = (TimeSpan)fromTime;
                TimeTo = (TimeSpan)toTime;

                DowFrom = GetDayOfWeek(strFromDay);
                if (DowFrom < 0)
                    return;

                DowTo = GetDayOfWeek(strToDay);
                if (DowTo < 0)
                {
                    if (TimeTo <= TimeFrom)
                        DowTo = (DowFrom + 1) % 7;
                    else
                        DowTo = DowFrom;
                }

                IsValid = true;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }


        private int GetDayOfWeek(string Day)
        {
            try
            {
                int DayOfWeek = -1;
                if (Day != null)
                {
                    var strDow = Day.ToUpperInvariant();
                    switch (strDow)
                    {
                        case "MON":
                            DayOfWeek = 1;
                            break;
                        case "TUE":
                            DayOfWeek = 2;
                            break;
                        case "WED":
                            DayOfWeek = 3;
                            break;
                        case "THU":
                            DayOfWeek = 4;
                            break;
                        case "FRI":
                            DayOfWeek = 5;
                            break;
                        case "SAT":
                            DayOfWeek = 6;
                            break;
                        case "SUN":
                            DayOfWeek = 0;
                            break;
                    }
                }

                return DayOfWeek;
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(LogLevels.Error, E);
            }

            return -1;
        }

        internal static List<cDataHistoryCleanupTimeFrame> LoadFromXml(XmlElement XNode, cXmlParser Parser)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }

            var RetVal = new List<cDataHistoryCleanupTimeFrame>();

            try
            {
                var XList = XNode.SelectNodes(constXmlTag);
                if (XList != null && XList.Count > 0)
                {
                    Parser.EnterElement(constXmlTag);
                    try
                    {
                        foreach (var Entry in XList)
                        {
                            if (!(Entry is XmlElement XEntry))
                                continue;

                            var Node = new cDataHistoryCleanupTimeFrame(XEntry, Parser);
                            if (Node.IsValid)
                                RetVal.Add(Node);
                            Parser.SelectElementNext();
                        }
                    }
                    finally
                    {
                        Parser.LeaveElement(constXmlTag);
                    }
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }

            return RetVal;
        }

        private static int Compare(int DowFrom, TimeSpan TimeFrom, int DowTo, TimeSpan TimeTo)
        {
            if (DowFrom == DowTo)
            {
                if (TimeTo > TimeFrom)
                    return 1;
                else if (TimeTo < TimeFrom)
                    return -1;
                else
                    return 0;
            }
            else if (DowTo > DowFrom)
                return 1;
            else
                return -1;
        }

        public bool Check()
        {
            try
            {
                var Now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, this.TimeZone);
                var dow = (int)Now.DayOfWeek;
                var time = Now.TimeOfDay;

                var c0 = Compare(DowFrom, TimeFrom, DowTo, TimeTo);
                var c1 = Compare(DowFrom, TimeFrom, dow, time);
                var c2 = Compare(DowTo, TimeTo, dow, time);
                if (c0 <= 0)
                    return c1 >= 0 || c2 <= 0;
                else
                    return c1 >= 0 && c2 <= 0;
            }
            catch (Exception E)
            {
                cLogManager.DefaultLogger.LogException(LogLevels.Error, E);
            }

            return false;
        }

    }


    public class cDataHistoryScanTiming : IConfigNodeValidation
    {
        public bool IsValid { get; set; } = false;
        public TimeSpan Interval { get; set; }

        public TimeSpan Offset { get; set; }

        public TimeZoneInfo Timezone { get; set; }

        public cDataHistoryScanTiming()
        {
        }

        internal cDataHistoryScanTiming(XmlElement XNode, TimeSpan DefaultInterval, TimeSpan DefaultOffset, cXmlParser Parser)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {
                Interval = DefaultInterval;
                Offset = DefaultOffset;
                Timezone = TimeZoneInfo.Local;

                var XNode2 = XNode.SelectSingleNode("Scan-Timing");
                if (XNode2 == null)
                    return;

                Parser.EnterElement("Scan-Timing");


                try
                {
                    Interval = (TimeSpan)cXmlParser.GetTimeSpanFromAttribute(XNode2, "ScanInterval", Interval);
                    Offset = (TimeSpan)cXmlParser.GetTimeSpanFromAttribute(XNode2, "ScanOffset", Interval);
                    Timezone = cXmlParser.GetTimeZoneInfoFromAttribute(XNode2, "Timezone");
                    if (Timezone == null)
                        Timezone = TimeZoneInfo.Utc;
                    IsValid = true;
                }
                finally
                {
                    Parser.LeaveElement("Scan-Timing");
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }
    }

    public class cF4SDAuthorisation : IConfigNodeValidation
    {
        public bool IsValid { get; set; } = false;

        public cF4SDMembershipGroups MembershipGroups = null;

        public Dictionary<string, cF4SDRole> Roles = null;

        public cF4SDAuthorisation(XmlElement XNode, cXmlParser Parser, cDataHistoryConfigInfrastructure InfratructureConfig)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {
                if (!(XNode.SelectSingleNode("Authorization") is XmlElement _xmlAuth))
                    return;

                Parser.EnterElement("Authorization");

                try
                {
                    var _groups = new cF4SDMembershipGroups(_xmlAuth, Parser, InfratructureConfig);
                    if (_groups?.IsValid != true)
                        return;
                    MembershipGroups = _groups;

                    var _roles = cF4SDRole.LoadFromXml(_xmlAuth, Parser, MembershipGroups);
                    if (_roles?.Count > 0)
                        Roles = _roles;

                    IsValid = true;
                }
                catch (Exception E)
                {
                    LogException(E);
                }

                Parser.LeaveElement("Authorization");
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }
    }

    public class cF4SDMembershipGroups : Dictionary<string, cF4SDMembershipGroup>, IConfigNodeValidation
    {
        public bool IsValid { get; set; } = false;

        public Dictionary<string, cF4SDMembershipGroupsAd> GroupsAd = null;
        public cF4SDMembershipGroupsM42 GroupsM42 = null;

        public cF4SDMembershipGroups(XmlElement XNode, cXmlParser Parser, cDataHistoryConfigInfrastructure InfratructureConfig)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {
                if (!(XNode.SelectSingleNode("Membership-Groups") is XmlElement _xmlGroups))
                    return;

                Parser.EnterElement("Membership-Groups");


                try
                {
                    GroupsAd = cF4SDMembershipGroupsAd.LoadFromXml(_xmlGroups, Parser, InfratructureConfig?.ActiveDirectory?.Domains);
                    if (GroupsAd != null)
                        foreach (var _entry in GroupsAd.Values)
                            foreach (var _entry2 in _entry.Values)
                                this[_entry2.Name] = _entry2;

                    var _groupM42 = new cF4SDMembershipGroupsM42(_xmlGroups, Parser);
                    if (_groupM42?.IsValid == true)
                    {
                        GroupsM42 = _groupM42;
                        foreach (var _entry in GroupsM42)
                            this[_entry.Key] = _entry.Value;
                    }

                    IsValid = true;
                }
                catch (Exception E)
                {
                    LogException(E);
                }

                Parser.LeaveElement("Membership-Groups");

            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }
    }

    public abstract class cF4SDMembershipGroup : cConfigNodeNamed
    {
        public cF4SDMembershipGroup(XmlElement XNode, cXmlParser Parser) : base(XNode, Parser)
        {
        }

    }

    public class cF4SDMembershipGroupsAd : Dictionary<string, cF4SDMembershipGroupAd>, IConfigNodeValidation
    {
        public bool IsValid { get; set; } = false;
        public string strDomain { get; private set; } = null;
        public cDataHistoryAdDomain Domain { get; private set; } = null;


        public cF4SDMembershipGroupsAd(XmlElement XNode, cXmlParser Parser, Dictionary<string, cDataHistoryAdDomain> Domains)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {
                strDomain = cXmlParser.GetStringFromXmlAttribute(XNode, "Domain");
                if (!string.IsNullOrEmpty(strDomain))
                {
                    if (Domains.TryGetValue(strDomain, out var _dom))
                    {
                        Domain = _dom;

                        var _xList = XNode.SelectNodes("Membership-Group-AD");
                        if (_xList != null && _xList.Count > 0)
                        {
                            Parser.EnterElement("Membership-Group-AD");
                            foreach (var _x in _xList)
                            {
                                if (!(_x is XmlElement _xEl))
                                    continue;

                                var _grp = new cF4SDMembershipGroupAd(_xEl, Parser);
                                if (_grp?.IsValid == true)
                                    this[_grp.Name] = _grp;

                                Parser.SelectElementNext();
                            }
                            Parser.LeaveElement("Membership-Group-AD");
                        }

                        IsValid = true;
                    }
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }

        public static Dictionary<string, cF4SDMembershipGroupsAd> LoadFromXml(XmlElement XNode, cXmlParser Parser, Dictionary<string, cDataHistoryAdDomain> Domains)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }

            var _retVal = new Dictionary<string, cF4SDMembershipGroupsAd>();
            try
            {
                var _xmlList = XNode.SelectNodes("Membership-Groups-AD");
                if (_xmlList != null && _xmlList.Count > 0)
                {
                    Parser.EnterElement("Membership-Groups-AD");
                    foreach (var _xmlEntry in _xmlList)
                    {
                        if (!(_xmlEntry is XmlElement _xmlEl))
                            continue;
                        var _node = new cF4SDMembershipGroupsAd(_xmlEl, Parser, Domains);
                        if (_node?.IsValid == true)
                            _retVal[_node.Domain.Name] = _node;
                        Parser.SelectElementNext();
                    }
                    Parser.LeaveElement("Membership-Groups-AD");
                }

            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }

            return _retVal; ;
        }
    }

    public class cF4SDMembershipGroupsM42 : Dictionary<string, cF4SDMembershipGroupM42>, IConfigNodeValidation
    {
        public bool IsValid { get; set; } = false;

        public cF4SDMembershipGroupsM42(XmlElement XNode, cXmlParser Parser)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {
                if (!(XNode.SelectSingleNode("Membership-Groups-Matrix42") is XmlElement _xGroup))
                    return;

                Parser.EnterElement("Membership-Groups-Matrix42");

                var _xList = _xGroup.SelectNodes("Membership-Group-Matrix42");
                if (_xList != null && _xList.Count > 0)
                {
                    Parser.EnterElement("Membership-Group-Matrix42");
                    foreach (var _x in _xList)
                    {
                        if (!(_x is XmlElement _xEl))
                            continue;

                        var _grp = new cF4SDMembershipGroupM42(_xEl, Parser);
                        if (_grp?.IsValid == true)
                            this[_grp.Name] = _grp;

                        Parser.SelectElementNext();
                    }
                    Parser.LeaveElement("Membership-Group-Matrix42");
                }

                Parser.LeaveElement("Membership-Groups-Matrix42");

                IsValid = true;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }
    }

    public class cF4SDMembershipGroupAd : cF4SDMembershipGroup
    {
        public string Account { get; private set; } = null;

        public string SID { get; set; } = null;


        public cF4SDMembershipGroupAd(XmlElement XNode, cXmlParser Parser) : base(XNode, Parser)
        {
            try
            {
                if (!this.IsValid)
                    return;
                IsValid = false;

                Account = cXmlParser.GetStringFromXmlAttribute(XNode, "Account");

                if (string.IsNullOrEmpty(Account))
                    return;

                IsValid = true;
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

    }

    public class cF4SDMembershipGroupM42 : cF4SDMembershipGroup
    {
        public string RoleName { get; private set; } = null;
        public Guid RoleId { get; private set; } = Guid.Empty;

        public cF4SDMembershipGroupM42(XmlElement XNode, cXmlParser Parser) : base(XNode, Parser)
        {
            try
            {
                if (!this.IsValid)
                    return;
                IsValid = false;

                RoleName = cXmlParser.GetStringFromXmlAttribute(XNode, "RoleName");

                if (string.IsNullOrEmpty(RoleName) || string.IsNullOrEmpty(Name))
                    return;

                RoleId = cXmlParser.GetGuidFromXmlAttribute(XNode, "RoleID");

                IsValid = true;
            }
            catch (Exception E)
            {
                LogException(E);
            }

        }
    }

    public class cF4SDRole : cConfigNodeNamed
    {
        public List<cF4SDMembershipGroup> MembershipGroups { get; private set; } = null;

        public cF4SDRole(XmlElement XNode, cXmlParser Parser, cF4SDMembershipGroups _MembershipGroups) : base(XNode, Parser)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {
                if (!IsValid)
                    return;
                IsValid = false;

                var _lstGroupNames = new List<string>();
                var _strGroup = cXmlParser.GetStringFromXmlAttribute(XNode, "Group");
                if (!string.IsNullOrEmpty(_strGroup))
                    _lstGroupNames.Add(_strGroup);

                var _xmlGroups = XNode.SelectNodes("GroupRef");

                if (_xmlGroups?.Count > 0)
                {
                    Parser.EnterElement("GroupRef");

                    foreach (var _ref in _xmlGroups)
                    {
                        if (!(_ref is XmlElement _refGroup))
                            continue;

                        _strGroup = cXmlParser.GetStringFromXmlAttribute(_refGroup, "Name");
                        if (!string.IsNullOrEmpty(_strGroup) && !_lstGroupNames.Contains(_strGroup))
                            _lstGroupNames.Add(_strGroup);

                        Parser.SelectElementNext();
                    }

                    Parser.LeaveElement("GroupRef");
                }

                if (_lstGroupNames.Count > 0)
                {
                    var _lstMembers = new List<cF4SDMembershipGroup>();
                    foreach (var _entry in _lstGroupNames)
                    {
                        if (_MembershipGroups.TryGetValue(_entry, out var _grp))
                        {
                            if (!_lstMembers.Contains(_grp))
                                _lstMembers.Add(_grp);
                        }
                    }

                    if (_lstMembers.Count > 0)
                    {
                        MembershipGroups = _lstMembers;
                        IsValid = true;
                    }
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }
        }

        public static Dictionary<string, cF4SDRole> LoadFromXml(XmlElement XNode, cXmlParser Parser, cF4SDMembershipGroups MembershipGroups)
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }

            var _retVal = new Dictionary<string, cF4SDRole>();
            try
            {
                var _xmlNode2 = XNode.SelectSingleNode("Roles");
                if (!(_xmlNode2 is XmlElement _xmlRoles))
                    return null;

                Parser.EnterElement("Roles");

                try
                {
                    var _xmlListRoles = _xmlRoles.SelectNodes("Role");
                    if (!(_xmlListRoles?.Count > 0))
                        return null;

                    try
                    {
                        Parser.EnterElement("Role");
                        foreach (var _xmlEntry in _xmlListRoles)
                        {
                            if (!(_xmlEntry is XmlElement _xmlEl))
                                continue;

                            var _group = new cF4SDRole(_xmlEl, Parser, MembershipGroups);
                            if (_group?.IsValid == true)
                                _retVal[_group.Name] = _group;
                            Parser.SelectElementNext();
                        }
                    }
                    finally
                    {
                        Parser.LeaveElement("Role");
                    }
                }
                finally
                {
                    Parser.LeaveElement("Roles");
                }

            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }

            return _retVal; ;
        }


    }
}
