using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Kreta.Enums;
using Kreta.Framework;
using Kreta.KretaServer.Caching;
using Kreta.KretaServer.SystemSettings.SettingsTypes;
using Newtonsoft.Json;

namespace Kreta.KretaServer.SystemSettings
{
    public static class SystemSettingsManager
    {
        private static T DeserializeControlObject<T>(string json, SystemSettingsControlTypeEnum controlType)
        {
            Type typeinfo = null;
            switch (controlType)
            {
                case SystemSettingsControlTypeEnum.CheckBox:
                    typeinfo = typeof(SettingsCheckBox);
                    break;
                case SystemSettingsControlTypeEnum.CheckBoxGroup:
                    typeinfo = typeof(SettingsCheckBoxGroup);
                    break;
                case SystemSettingsControlTypeEnum.DatePicker:
                    typeinfo = typeof(SettingsDatePicker);
                    break;
                case SystemSettingsControlTypeEnum.TimePicker:
                    typeinfo = typeof(SettingsTimePicker);
                    break;
                case SystemSettingsControlTypeEnum.NumericTextBox:
                    typeinfo = typeof(SettingsNumericTextBox);
                    break;
                case SystemSettingsControlTypeEnum.DropDownList:
                    typeinfo = typeof(SettingsDropDownList);
                    break;
                case SystemSettingsControlTypeEnum.RadioButtonGroup:
                    typeinfo = typeof(SettingsRadioButtonGroup);
                    break;
                case SystemSettingsControlTypeEnum.TrueFalse:
                    typeinfo = typeof(SettingsSwitchButton);
                    break;
                case SystemSettingsControlTypeEnum.MultiSelect:
                    typeinfo = typeof(SettingsMultiSelect);
                    break;
                default:
                    break;
            }

            return (T)JsonConvert.DeserializeObject(json, typeinfo);
        }
        /// <summary>
        /// Visszaadja az adotr rendszerbeállítás értékét.
        /// </summary>
        /// <typeparam name="T">Enum, String, int, bool, List<int>, List<string>  List<Enum></typeparam>
        /// <param name="beallitasTipus"></param>
        /// <returns>
        /// Az adott beállítás értéke a megadott típusba vagy a megadott tipus alap értéke ha nincs érték PL: CheckBoxGroup.
        /// </returns>
        public static T GetSystemSettingValue<T>(RendszerBeallitasTipusEnum beallitasTipus, string intezmenyAzonosito, int tanevId)
        {
            var setting = SDAServer.Instance.CacheManager.AquireCache<SystemSettingsCache>().GetSystemSetting(beallitasTipus, intezmenyAzonosito, tanevId);
            var inputType = typeof(T);
            switch (setting.Type)
            {
                case SystemSettingsControlTypeEnum.CheckBox:
                    var checkBox = DeserializeControlObject<SettingsCheckBox>(setting.Json, setting.Type);
                    {
                        if (inputType.Equals(typeof(string)))
                        {
                            return (T)((object)checkBox.IsSelected.ToString());
                        }

                        if (inputType.Equals(typeof(bool)))
                        {
                            return (T)((object)checkBox.IsSelected);
                        }
                    }

                    throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                case SystemSettingsControlTypeEnum.CheckBoxGroup:
                    var checkBoxGroup = DeserializeControlObject<SettingsCheckBoxGroup>(setting.Json, setting.Type);
                    {
                        if (inputType.Equals(typeof(string)))
                        {
                            var selectedOptions = string.Join(",", checkBoxGroup.Options.Where(x => x.Selected).Select(y => y.Value).ToArray());
                            return (T)((object)selectedOptions);
                        }

                        if (inputType.Equals(typeof(List<string>)))
                        {
                            var stringList = new List<string>();
                            foreach (var box in checkBoxGroup.Options)
                            {
                                if (box.Selected)
                                {
                                    stringList.Add(box.Value);
                                }
                            }
                            return (T)((object)stringList);
                        }

                        if (inputType.Equals(typeof(List<int>)))
                        {
                            var intList = new List<int>();
                            foreach (var box in checkBoxGroup.Options)
                            {
                                int optionValue;
                                if (box.Selected)
                                {
                                    if (int.TryParse(box.Value, out optionValue))
                                    {
                                        intList.Add(optionValue);
                                    }
                                    else
                                    {
                                        throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                                    }
                                }

                            }
                            return (T)((object)intList);
                        }
                    }

                    var nestedTypes = inputType.GetGenericArguments();
                    if (nestedTypes.Length == 1 && nestedTypes[0].IsEnum)
                    {
                        var nestedType = nestedTypes[0];
                        var enumlist = (IList)Activator.CreateInstance(inputType);

                        foreach (var box in checkBoxGroup.Options)
                        {
                            int enumvalue;
                            if (int.TryParse(box.Value, out enumvalue) && Enum.IsDefined(nestedType, enumvalue))
                            {
                                enumlist.Add(Enum.Parse(nestedType, box.Value));
                            }
                        }

                        return (T)(enumlist);
                    }
                    throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                case SystemSettingsControlTypeEnum.DatePicker:
                    var datePicker = DeserializeControlObject<SettingsDatePicker>(setting.Json, setting.Type);
                    {
                        if (inputType.Equals(typeof(string)))
                        {
                            return (T)((object)datePicker.Date.ToString());
                        }

                        if (inputType.Equals(typeof(DateTime)))
                        {
                            return (T)((object)datePicker.Date);
                        }

                        if (inputType.Equals(typeof(DateTime?)))
                        {
                            if (datePicker.Date.HasValue)
                            {
                                return (T)((object)datePicker.Date.Value);
                            }

                            return (T)(object)null;
                        }
                    }

                    throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                case SystemSettingsControlTypeEnum.TimePicker:
                    var timePicker = DeserializeControlObject<SettingsTimePicker>(setting.Json, setting.Type);
                    {
                        if (inputType.Equals(typeof(string)))
                        {
                            return (T)((object)timePicker.Time.ToString());
                        }

                        if (inputType.Equals(typeof(TimeSpan)))
                        {
                            return (T)((object)timePicker.Time);
                        }
                    }

                    throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                case SystemSettingsControlTypeEnum.NumericTextBox:
                    var numericTextBox = DeserializeControlObject<SettingsNumericTextBox>(setting.Json, setting.Type);
                    {
                        if (inputType.Equals(typeof(string)))
                        {
                            return (T)((object)numericTextBox.Value.ToString());
                        }

                        if (inputType.Equals(typeof(int)) || inputType.Equals(typeof(double)))
                        {
                            return (T)((object)numericTextBox.Value);
                        }
                    }

                    throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                case SystemSettingsControlTypeEnum.DropDownList:

                    var dropDown = DeserializeControlObject<SettingsDropDownList>(setting.Json, setting.Type);
                    var value = dropDown.Options.First(x => x.Selected == true).Value;
                    {
                        if (inputType.Equals(typeof(string)))
                        {

                            return (T)((object)value);
                        }

                        if (inputType.Equals(typeof(int)))
                        {
                            int result;
                            if (int.TryParse(value, out result))
                            {
                                return (T)((object)result);
                            }

                            throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                        }

                        if (inputType.IsEnum)
                        {
                            int valueInt;

                            if (int.TryParse(value, out valueInt) && Enum.IsDefined(inputType, valueInt))
                            {
                                return (T)Enum.Parse(inputType, valueInt.ToString());
                            }

                        }
                    }

                    throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                case SystemSettingsControlTypeEnum.RadioButtonGroup:
                    var radiobuttonGroup = DeserializeControlObject<SettingsRadioButtonGroup>(setting.Json, setting.Type);
                    var selected = radiobuttonGroup.Options.First(x => x.Selected).Value;
                    {
                        if (inputType.Equals(typeof(string)))
                        {

                            return (T)((object)selected);
                        }

                        if (inputType.Equals(typeof(int)))
                        {
                            int result;
                            if (int.TryParse(selected, out result))
                            {
                                return (T)((object)result);
                            }

                            throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                        }

                        if (inputType.IsEnum)
                        {
                            int enumInt;

                            if (int.TryParse(selected, out enumInt) && Enum.IsDefined(inputType, enumInt))
                            {
                                return (T)Enum.Parse(inputType, enumInt.ToString());
                            }
                        }
                    }

                    throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                case SystemSettingsControlTypeEnum.TrueFalse:
                    var switchbutton = DeserializeControlObject<SettingsSwitchButton>(setting.Json, setting.Type);
                    {
                        if (inputType.Equals(typeof(string)))
                        {
                            return (T)((object)switchbutton.Value.ToString());
                        }

                        if (inputType.Equals(typeof(bool)))
                        {
                            return (T)((object)switchbutton.Value);
                        }
                    }

                    throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                case SystemSettingsControlTypeEnum.MultiSelect:
                    var multiSelect = DeserializeControlObject<SettingsMultiSelect>(setting.Json, setting.Type);
                    {
                        if (inputType.Equals(typeof(string)))
                        {
                            var selectedOptions = string.Join(",", multiSelect.Options.Where(x => x.Selected).Select(y => y.Value).ToArray());
                            return (T)((object)selectedOptions);
                        }

                        if (inputType.Equals(typeof(List<string>)))
                        {
                            var stringList = new List<string>();
                            foreach (var box in multiSelect.Options)
                            {
                                if (box.Selected)
                                {
                                    stringList.Add(box.Value);
                                }
                            }
                            return (T)((object)stringList);
                        }

                        if (inputType.Equals(typeof(List<int>)))
                        {
                            var intList = new List<int>();
                            foreach (var box in multiSelect.Options)
                            {
                                int optionValue;
                                if (box.Selected)
                                {
                                    if (int.TryParse(box.Value, out optionValue))
                                    {
                                        intList.Add(optionValue);
                                    }
                                    else
                                    {
                                        throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                                    }
                                }

                            }
                            return (T)((object)intList);
                        }
                    }

                    var nestedMultiTypes = inputType.GetGenericArguments();
                    if (nestedMultiTypes.Length == 1 && nestedMultiTypes[0].IsEnum)
                    {
                        var nestedType = nestedMultiTypes[0];
                        var enumlist = (IList)Activator.CreateInstance(inputType);

                        foreach (var box in multiSelect.Options)
                        {
                            int enumvalue;
                            if (int.TryParse(box.Value, out enumvalue) && Enum.IsDefined(nestedType, enumvalue))
                            {
                                enumlist.Add(Enum.Parse(nestedType, box.Value));
                            }
                        }

                        return (T)(enumlist);
                    }
                    throw new InvalidCastException(string.Format("Can not cast setting value string to a given Type of {0}", inputType.ToString()));
                default:
                    throw new NotSupportedException("The given SystemSettingsControl Type is unsupportd by the method!");
            }
        }
        /// <summary>
        /// Beállítja az adott beállítás értékét.
        /// </summary>
        /// <param name="settingType">Rendszerbeállítás tipus</param>
        /// <param name="settingObject">
        /// SystemSettingType object
        /// </param>
        public static void SetSystemSettings(RendszerBeallitasTipusEnum settingType, string intezmenyAzonosito, int tanevId, object settingObject, int? kovTanevId)
        {
            SDAServer.Instance.CacheManager.AquireCache<SystemSettingsCache>().SetSystemSettings(settingType, intezmenyAzonosito, tanevId, settingObject, kovTanevId);
        }
        /// <summary>
        /// Visszaállítja az alapértékeket a db-ben majd frissiti a cachet
        /// </summary>
        /// <returns></returns>
        public static bool ResetSystemSettings(string intezmenyAzonosito, int? intezmenyID, int? tanevID, List<int> exceptSystemSettings)
        {
            return SDAServer.Instance.CacheManager.AquireCache<SystemSettingsCache>().ResetSystemSettings(intezmenyAzonosito, intezmenyID, tanevID, exceptSystemSettings);
        }

        /// <summary>
        /// Visszaadja az összes rednszerbeállítás leíró objektumot.
        /// </summary>
        /// <returns></returns>
        public static List<SystemSettingsCache.SystemSettingsData> GetSystemSettings(string intezmenyAzonosito, int tanevId)
        {
            return SDAServer.Instance.CacheManager.AquireCache<SystemSettingsCache>().GetSystemSettings(intezmenyAzonosito, tanevId);
        }

        /// <summary>
        /// Visszaadja a rendszerbeállítás osztály. Tartalmazza a rendszerbeállítás tipusát, json stringet és a json object tipusát
        /// </summary>
        /// <param name="beallitasTipus"></param>
        /// <returns></returns>
        public static SystemSettingsCache.SystemSettingsData GetSystemSetting(RendszerBeallitasTipusEnum beallitasTipus, string intezmenyAzonosito, int tanevId)
        {
            return SDAServer.Instance.CacheManager.AquireCache<SystemSettingsCache>().GetSystemSetting(beallitasTipus, intezmenyAzonosito, tanevId);
        }
    }
}