namespace Vrh.OneReport.Lib.Areas.OneReport.Models
{
    using System.Collections.Generic;

    #region ReportXMLModel
    public class ReportXMLModel
    {
        #region Report class
        public class Report
        {
            public string Id { get; set; }

            public string LCID { get; set; }

            public string FilePath { get; set; }

            public string ConnectionStringName { get; set; }

            public string DisplayName { get; set; }

            public OutputClass Output { get; set; } = new OutputClass();

            public ExportElement Export { get; set; }
        }
        #endregion Report class

        #region DataSet class
        public class DataSet
        {
            public string ConnectionString { get; set; }

            public string DataSetName { get; set; }

            public string InitCommandType { get; set; }

            public string InitCommandSql { get; set; }

            public string DataCommandType { get; set; }

            public string DataCommandSql { get; set; }
        }
        #endregion DataSet class

        #region OutputClass class
        public class OutputClass
        {
            public string Format { get; set; }

            public string Action { get; set; }

            public string Folder { get; set; }

            public string File { get; set; }
        }
        #endregion OutputClass class

        #region ExportElement class
        /// <summary>
        /// A riport XML <General> és <Report> elemében szereplő <Export> elemet reprezentáló osztály.
        /// </summary>
        public class ExportElement
        {
            /// <summary>
            /// Az export formátuma.
            /// </summary>
            public string Format { get; set; }

            /// <summary>
            /// Az export ebbe a fájlba készül el. Teljes elérési útvonalával együtt.
            /// </summary>
            public string File { get; set; }
        }
        #endregion ExportElement class

        #region ReportTimeOutsClass class
        /// <summary>
        /// Riporttal kapcsolatos Timeout-ok
        /// </summary>
        public class ReportTimeOutsClass
        {
            /// <summary>
            /// SQL query timeoutja (másodperc)
            /// </summary>
            public int SQLQuery = 60;

            /// <summary>
            /// Async postback timeout (másodperc)
            /// </summary>
            public int AsyncPostBackTimeOut = 60;

            /// <summary>
            /// http runtime execution timeout (másodperc)
            /// </summary>
            public int ExecutionTimeout = 120;
        }
        #endregion ReportTimeOutsClass class

        public string RequestLCID { get; set; }

        public Dictionary<string, string> Strings { get; set; } = new Dictionary<string, string>();

        public string DefaultConnectionStringName { get; set; }

        public string DefaultLCID { get; set; }

        public string InputRoot { get; set; }

        public OutputClass DefaultOutput { get; set; } = new OutputClass();

        public ReportTimeOutsClass ReportTimeOuts { get; set; } = new ReportTimeOutsClass();

        public ExportElement DefaultExport { get; set; } = new ExportElement();

        public Report CurrentReport { get; set; }

        public Dictionary<string, string> Parameters { get; set; } = new Dictionary<string, string>();

        public List<Preset> ExternalParameterPresets { get; set; } = new List<Preset>();

        public List<SimpleKeyValuePair> ExternalParameters { get; set; } = new List<SimpleKeyValuePair>();

        public List<InternalParameter> InternalParameters { get; set; } = new List<InternalParameter>();

        public ICollection<DataSet> DataSets { get; set; } = new List<DataSet>();
    }
    #endregion ReportXMLModel

    /// <summary>
    /// Kulcs értékpárok tárolására (egyszerűbb mint a KeyValuePair)
    /// </summary>
    public class SimpleKeyValuePair
    {
        /// <summary>
        /// Kulcs
        /// </summary>
        public string Key { get; set; }

        /// <summary>
        /// Érték
        /// </summary>
        public string Value { get; set; }
    }

    /// <summary>
    /// paraméter presetteket tartalmazó változó
    /// </summary>
    public class Preset
    {
        /// <summary>
        /// Preset neve
        /// </summary>
        public string PresetName { get; set; }

        /// <summary>
        /// Presetben meghatározott paraméterek (név/érték párok) listája
        /// </summary>
        public List<SimpleKeyValuePair> Parameters { get; set; } = new List<SimpleKeyValuePair>();
    }

    /// <summary>
    /// Belső paraméterek
    /// </summary>
    public class InternalParameter
    {
        /// <summary>
        /// Gyártófüggvény
        /// </summary>
        /// <param name="name">paraméter neve</param>
        /// <param name="conditions">feltételek listája</param>
        /// <param name="defaultValue">alapértelmezett érték</param>
        /// <returns>InternalParameter instance</returns>
        public static InternalParameter Factory(string name, List<ConditionDefination> conditions, string defaultValue)
        {
            return new InternalParameter(name, conditions, defaultValue);
        }

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="name">paraméter neve</param>
        /// <param name="conditions">feltételek listája</param>
        /// <param name="defaultValue">alapértelmezett érték</param>
        private InternalParameter(string name, List<ConditionDefination> conditions, string defaultValue)
        {
            Name = name;
            _conditions = conditions;
            _defaultValue = defaultValue;
        }

        /// <summary>
        /// Internal paraméter neve
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Visszadja a paraméter aktuális  értékét
        /// </summary>
        /// <param name="externalParameters">Figyelmebe vett külső paraméterek listája</param>
        /// <param name="strings">Figyelmebe vett string konstatnsok listája</param>
        /// <returns>Paraméter aktuális értéke</returns>
        public string GetValue(List<SimpleKeyValuePair> externalParameters, Dictionary<string, string> strings)
        {
            foreach (var condition in _conditions)
            {
                if (condition.Hit(externalParameters, strings))
                {
                    return condition.GetValue(externalParameters, strings);
                }
            }
            return ApplyChanges(_defaultValue, externalParameters, strings);
        }

        private string ApplyChanges(string input, List<SimpleKeyValuePair> externalParameters, Dictionary<string, string> strings)
        {
            foreach (var str in strings)
            {
                input = input.Replace($"{Constants.NameSeparator}{str.Key}{Constants.NameSeparator}", str.Value);
            }
            foreach (var extP in externalParameters)
            {
                input = input.Replace($"{Constants.NameSeparator}{extP.Key}{Constants.NameSeparator}", extP.Value);
            }
            return input;
        }

        /// <summary>
        /// Belső paraméter értékadási feltételek listája
        /// </summary>
        private List<ConditionDefination> _conditions;

        /// <summary>
        /// alapértelmezett érték
        /// </summary>
        private string _defaultValue;
    }

    /// <summary>
    /// Értékadási feltételt reprezentáló osztály
    /// </summary>
    public class ConditionDefination
    {
        /// <summary>
        /// Gyártó függvény
        /// </summary>
        /// <param name="type">Feltétel típusa</param>
        /// <param name="test">feltétel 1. bemenő paramétere (bal oldal)</param>
        /// <param name="with">feltétel 2. bemenő paramétere (jobb oldal)</param>
        /// <param name="value">a feltétel teljesülése esetén érvényes érték</param>
        /// <returns>ConditionDefination instance</returns>
        public static ConditionDefination Factory(ConditionType type, string test, string with, string value)
        {
            return new ConditionDefination(type, test, with, value);
        }

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="type">Feltétel típusa</param>
        /// <param name="test">feltétel 1. bemenő paramétere (bal oldal)</param>
        /// <param name="with">feltétel 2. bemenő paramétere (jobb oldal)</param>
        /// <param name="value">a feltétel teljesülése esetén érvényes érték</param>
        private ConditionDefination(ConditionType type, string test, string with, string value)
        {
            _type = type;
            _test = test;
            _with = with;
            _value = value;
        }

        /// <summary>
        /// Megmondja, hogy teljesül-e a feltétel, az átadott string konstansok és külső paraméter értékek mellett
        /// </summary>
        /// <param name="externalParameters">Figyelembe vett külső paraméter értékek listája</param>
        /// <param name="strings">Figyelembe vett stringkonstansok listája</param>
        /// <returns>A feltétele teljesül (true)/nem teljesül (false)</returns>
        public bool Hit(List<SimpleKeyValuePair> externalParameters, Dictionary<string, string> strings)
        {
            switch (_type)
            {
                default:
                case ConditionType.Else:
                    return true;
                case ConditionType.Equal:
                    if (_test.Contains("@") || _with.Contains("@"))
                    {
                        return ApplyChanges(_test, externalParameters, strings) == ApplyChanges(_with, externalParameters, strings);
                    }
                    else
                    {
                        return _test == _with;
                    }
            }
        }

        /// <summary>
        /// Visszadja a feltétel megadott külső paraméter értékek, és stringkonstansok melletti értékét (nem vizsgálja, hogy maga  adefiniált feltétel teljesül-e!!!)
        /// </summary>
        /// <param name="externalParameters">Figyelembe vett külső paraméter értékek listája</param>
        /// <param name="strings">Figyelembe vett stringkonstansok listája</param>
        /// <returns></returns>
        public string GetValue(List<SimpleKeyValuePair> externalParameters, Dictionary<string, string> strings)
        {
            return ApplyChanges(_value, externalParameters, strings);
        }

        /// <summary>
        /// Az inputon érvényre jutattja a megkapott stringkonstans és paraméter értékek cseréjét, és visszadja az ennek megfelelő eredményt
        /// </summary>
        /// <param name="input">bejövő string</param>
        /// <param name="externalParameters">Figyelembe vett külső paraméter értékek listája</param>
        /// <param name="strings">Figyelembe vett stringkonstansok listája</param>
        /// <returns>A cserék elvégzése utáni bejövő string</returns>
        private string ApplyChanges(string input, List<SimpleKeyValuePair> externalParameters, Dictionary<string, string> strings)
        {
            foreach (var str in strings)
            {
                input = input.Replace($"{Constants.NameSeparator}{str.Key}{Constants.NameSeparator}", str.Value);
            }
            foreach (var extP in externalParameters)
            {
                input = input.Replace($"{Constants.NameSeparator}{extP.Key}{Constants.NameSeparator}", extP.Value);
            }
            return input;
        }

        /// <summary>
        /// Feltétel típusa
        /// </summary>
        private ConditionType _type;

        /// <summary>
        /// A feltétel 1. bemenő paramétere (bal oldal)
        /// </summary>
        private string _test;

        /// <summary>
        /// feltétel 2. bemenő paramétere (jobb oldal)
        /// </summary>
        private string _with;

        /// <summary>
        /// A feltételhez tartozó érték
        /// </summary>
        private string _value;
    }

    /// <summary>
    /// Ismert feltétel típusok
    /// </summary>
    public enum ConditionType
    {
        /// <summary>
        /// mindig teljesül (igaz)
        /// </summary>
        Else = 0,
        /// <summary>
        /// Egyanlőség vizsgálat
        /// </summary>
        Equal = 1,
    }
}
