using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using ImGuiNET;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.Scraper.Storage;
using skyscraper8.Skyscraper.Scraper.Storage;

namespace SDL2Demo.Forms
{
    internal class ConfigureDataStorageWindow : IRenderable
    {
        public Ini Ini { get; }
        public ReadOnlyCollection<KeyValuePair<int, DataStorageFactory>> StorageFactories { get; }
        private Dictionary<int, string> FactoryNames;
        private Dictionary<int, Guid> TempGuids;
        private StorageConnectionManager connectionManager;

		public ConfigureDataStorageWindow(Ini ini, ReadOnlyCollection<KeyValuePair<int, DataStorageFactory>> storageFactories)
        {
            Ini = ini;
            StorageFactories = storageFactories;
            IsOpen = true;
            storageId = ini.ReadValue("startup", "dataStorage", 0);
            settings = new List<Setting>();
            FactoryNames = new Dictionary<int, string>();
            connectionManager = StorageConnectionManager.GetInstance();
            TempGuids = new Dictionary<int, Guid>();
            

            foreach (KeyValuePair<int, DataStorageFactory> keyValuePair in storageFactories)
            {
	            FactoryNames.Add(keyValuePair.Key, connectionManager.GetName(keyValuePair.Value));
                TempGuids.Add(keyValuePair.Key, Guid.NewGuid());
                PropertyInfo[] propertyInfos = keyValuePair.Value.GetType().GetProperties();
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    Setting s = new Setting();
                    s.IniKey = keyValuePair.Key;
                    s.SettingName = propertyInfo.Name;
                    s.SettingControlUuid = Guid.NewGuid().ToString();
                    switch (propertyInfo.PropertyType.Name)
                    {
                        case nameof(String):
                            s.SettingMode = 1;
                            s.StringValue = ini.ReadValue(String.Format("dataStorage{0}", s.IniKey), s.SettingName, "");
                            break;
                        case nameof(Boolean):
                            s.SettingMode = 2;
                            s.BooleanValue = ini.ReadValue(String.Format("dataStorage{0}", s.IniKey), s.SettingName, false);
                            break;
                        case nameof(UInt16):
                            s.SettingMode = 3;
                            s.UshortValue = (ushort)ini.ReadValue(String.Format("dataStorage{0}", s.IniKey), s.SettingName, ushort.MinValue);
                            break;
                        default:
                            throw new NotImplementedException(propertyInfo.PropertyType.Name);
                    }

                    settings.Add(s);
                }
            }
        }

        public class Setting
        {
            public long IniKey;
            public string SettingName;
            public int SettingMode;
            public string StringValue;
            public bool BooleanValue;
            public int UshortValue;
            public string SettingControlUuid;
            public float FloatValue;
            public int IntegerValue;

            public bool Validate()
            {
                switch (SettingMode)
                {
                    case 1:
                        return !string.IsNullOrEmpty(StringValue) && !string.IsNullOrWhiteSpace(StringValue);
                    case 2:
                        return true;
                    case 3:
                        return UshortValue >= ushort.MinValue && UshortValue <= ushort.MaxValue;
                    case 4:
	                    return float.IsFinite(FloatValue);
                    default:
                        throw new NotImplementedException(String.Format("Setting mode {0}", SettingMode));
                }
            }
        }

        public bool IsOpen;
        private bool HasChanges;
        private int storageId;
        private List<Setting> settings;
        public bool RequireRestart;

        private void Save()
        {
            Ini.WriteValue("startup", "dataStorage", storageId);
            foreach (Setting setting in settings)
            {
                switch (setting.SettingMode)
                {
                    case 1:
                        Ini.WriteValue(String.Format("dataStorage{0}", setting.IniKey), setting.SettingName, setting.StringValue);
                        break;
                    case 2:
                        Ini.WriteValue(String.Format("dataStorage{0}", setting.IniKey), setting.SettingName, setting.BooleanValue);
                        break;
                    case 3:
                        Ini.WriteValue(String.Format("dataStorage{0}", setting.IniKey), setting.SettingName, setting.UshortValue);
                        break;
                    default:
                        throw new NotImplementedException(String.Format("Setting mode {0}", setting.SettingMode));
                }
            }

            Ini.Export(new FileInfo("skyscraper5.ini"));

            if (HasChanges)
                RequireRestart = true;
            HasChanges = false;

        }

        private bool CanApply()
        {
            foreach (Setting setting in settings.Where(x => x.IniKey == storageId))
            {
                if (!setting.Validate())
                {
                    return false;
                }
            }

            return true;
        }

        public void Render()
        {
            ImGuiWindowFlags flags = ImGuiWindowFlags.None;
            if (HasChanges)
                flags |= ImGuiWindowFlags.UnsavedDocument;

            ImGui.Begin("Configure ScraperStorage", ref IsOpen, flags);

            foreach (KeyValuePair<int, DataStorageFactory> keyValuePair in StorageFactories)
            {
                if (ImGui.RadioButton(FactoryNames[keyValuePair.Key], ref storageId, keyValuePair.Key))
                {
                    HasChanges = true;
                }

                ImGui.BeginTable(TempGuids[keyValuePair.Key].ToString(), 2);

                foreach (Setting setting in settings.Where(x => x.IniKey == keyValuePair.Key))
                {
                    ImGui.TableNextRow();
                    ImGui.TableSetColumnIndex(0);
                    ImGui.Text(setting.SettingName);
                    ImGui.TableSetColumnIndex(1);
                    ImGui.PushID(setting.SettingControlUuid);
                    switch (setting.SettingMode)
                    {
                        case 1:
                            if (ImGui.InputText("", ref setting.StringValue, 255)) 
                                HasChanges = true;
                            break;
                        case 2:
                            if (ImGui.Checkbox("", ref setting.BooleanValue))
                                HasChanges = true;
                            break;
                        case 3:
                            if (setting.UshortValue < ushort.MinValue)
                                setting.UshortValue = ushort.MinValue;
                            if (setting.UshortValue > ushort.MaxValue)
                                setting.UshortValue = ushort.MaxValue;
                            if (ImGui.InputInt("", ref setting.UshortValue))
                                HasChanges = true;
                            break;
                        default:
                            throw new NotImplementedException(String.Format("Setting mode {0}", setting.SettingMode));
                    }
                }
                ImGui.PopID();
                ImGui.EndTable();
            }

            bool canApply = CanApply();
            ImGui.BeginDisabled(!canApply);
            if (ImGui.Button("OK"))
            {
                Save();
                IsOpen = false;
            }
            ImGui.EndDisabled();

            ImGui.SameLine();
            if (ImGui.Button("Cancel"))
            {
                IsOpen = false;
            }

            ImGui.SameLine();
            ImGui.BeginDisabled(!canApply);
            if (ImGui.Button("Apply"))
            {
                Save();
            }
            ImGui.EndDisabled();
            ImGui.End();
        }
    }
}
