
using Microsoft.Xna.Framework;
using Monocle;
using System;
using System.Collections.Generic;
using YamlDotNet.Serialization;

namespace Celeste.Mod.Eevee_Skinmod {
    //this is my first mod, so maybe there is a better way than creating from scratch each setting's entry

    [SettingName("eevee_options")]
    public class EeveeSettings : EverestModuleSettings {

        [YamlIgnore]
        public bool Enabled { get; set; } = true;

        [SettingMaxLength(100)]
        [SettingSubText("Set the folder path where the mod will search for and save palettes")]
        public string palettePath { get; set; }
        public bool dashEnabled { get; set; } = true;
        public int portraitMode { get; set; } = 0;

        public bool useDirectionalDashSprites { get; set; } = true;
        [SettingIgnore]
        public bool useDemoDashSprite { get; set; } = true;
        [SettingIgnore]
        public int useWallBounceSprite { get; set; } = 2;
        [SettingIgnore]
        public int useSuperJumpSprite { get; set; } = 2;


        public int markerMode { get; set; } = 0;
        public int markerType { get; set; } = 0;

        public int InternalOpt { get; set; } = 0;
        public int ExternalOpt { get; set; }
        private string CreateOpt;
        public int Selection { get; set; } = 0;
        //Components for CelesteNet were taken from the Avali skin
        public bool cNetEnable { get; set; } = false;
        public int cNetSyncMode { get; set; } = 0;
        //public bool cNetAllEevee { get; set; } = false;
        [SettingIgnore]
        public bool eeveeShiny { get; set; } = false;
        [SettingIgnore]
        public bool updated { get; set; } = false;
        [SettingIgnore]
        public int markerNum { get; set; } = 0;

        public SkinPalette fluff;
        //Components for pet Settings
        [SettingIgnore]
        public bool updatePet { get; set; } = true;

        public bool overwritePet { get; set; } = true;
        public SkinPalette petFluff;

        public int hatDash { get; set; } = 1;



        [YamlIgnore]
        public Dictionary<string, Palette> paletteList = new Dictionary<string, Palette>();
        [YamlIgnore]
        public Dictionary<string, Palette> externalList = new Dictionary<string, Palette>();
        [YamlIgnore]
        public List<string> createList = new List<string>();
        [YamlIgnore]
        private List<string> holderList = new List<string>();

        private TextMenu.Slider intlist;
        private TextMenu.Slider extlist;
        private TextMenu.Slider crtlist;

        private TextMenuExt.SubMenu extraSpritesMenu;

        private TextMenuExt.OptionSubMenu submenu;
        private TextMenuExt.SubMenu submenuPet;

        private TextMenuExt.SubMenu secretMenu;
        private TextMenuExt.SubMenu petMenu;
        private TextMenu.Slider markerMenu;
        private TextMenu.OnOff d_enable;
        private TextMenu.OnOff c_enable;
        private TextMenu.Slider syncMode;

        //Components for Strawberry friend

        public int petSelection { get; set; } = 0;
        public int InternalPet{ get; set; } = 0;
        public int ExternalPet { get; set; }
        public int PetState { get; set; }

        private TextMenu.Slider petIntlist;
        private TextMenu.Slider petExtlist;
        private TextMenuExt.IntSlider petDash;

        //private TextMenu.OnOff allMode;
        //This is used to restrict the create button to one use only, originally you could create several skins at once, but
        //The chance of crashing the game was too high, and while i dont like it, this is better
        [YamlIgnore]
        private bool pressed = false;
        public void CreateEnabledEntry(TextMenu menu, bool inGame) {

            if (!EeveeReader.tryGet(palettePath)) {
                Selection = 0; 
                fluff = SkinPalette.StringtoColor(paletteList[getList1(InternalOpt)]);
                if(Everest.Loader.DependencyLoaded(EeveeModule.StrawberryPetMeta)) {
                    petSelection = 0;
                    petFluff = SkinPalette.StringtoColor(paletteList[getList1(InternalPet)]);
                    updatePet = true;
                }
            } else if (inGame && Selection > 1) {
                Selection = 1;
            } else if (!inGame) {
                externalList = EeveeReader.readContents(palettePath);
                createList = EeveeConverter.getNames(palettePath);
            }

            if (EeveeReader.tryGet(palettePath) && ExternalOpt > externalList.Count) {
                ExternalOpt = 0;
            }


        }

        public void CreatedashEnabledEntry(TextMenu menu, bool inGame) {
            d_enable = new TextMenu.OnOff("Custom Dash", dashEnabled);
            d_enable.Visible = true;
            d_enable.Disabled = false;
            d_enable.Change(enabled => {
                dashEnabled = enabled;
                fluff = Selection switch {
                    1 => SkinPalette.StringtoColor(externalList[getList2(ExternalOpt)]),
                    0 => SkinPalette.StringtoColor(paletteList[getList1(InternalOpt)]),
                    _ => fluff
                };
                updatePlayer();
            });
            menu.Add(d_enable);
            d_enable.AddDescription(menu,"Enable to use the Palette\'s dash color\nDisable to use vanilla's or Hyperline\'s, it affects the fluff too");
        }

        public void CreateuseDirectionalDashSpritesEntry(TextMenu menu, bool inGame) {
            extraSpritesMenu = new TextMenuExt.SubMenu("Extra Animations", false);

            TextMenu.OnOff directionalDash = new TextMenu.OnOff("Directional Dashes", useDirectionalDashSprites);
            directionalDash.Change(value => useDirectionalDashSprites = value);
            TextMenu.OnOff demoDash = new TextMenu.OnOff("Crouch Dash", useDemoDashSprite);
            demoDash.Change(value => useDemoDashSprite = value);
            TextMenu.Slider wallBounce = new TextMenu.Slider("Wallbounces", extraAnims, 0, 2, useWallBounceSprite);
            wallBounce.Change(value => useWallBounceSprite = value);
            TextMenu.Slider superJump = new TextMenu.Slider("Supers / Hypers", extraAnims, 0, 2, useSuperJumpSprite);
            superJump.Change(value => useSuperJumpSprite = value);

            extraSpritesMenu.Add(directionalDash);
            extraSpritesMenu.Add(demoDash);
            extraSpritesMenu.Add(wallBounce);
            extraSpritesMenu.Add(superJump);

            menu.Add(extraSpritesMenu);

            extraSpritesMenu.AddDescription(menu, "\"Rare\" means 5% chance of alternate animation");
        }

        public void CreateportraitModeEntry(TextMenu menu, bool inGame) {
            secretMenu = new TextMenuExt.SubMenu("Super Secret Settings", false);
            TextMenu.Slider portraits = new TextMenu.Slider("Force Portraits",skinModes,0,2,portraitMode);
            portraits.Change(value => portraitMode = value);
            TextMenu.Slider marker = new TextMenu.Slider("Force Marker", skinModes, 0, 2, markerMode);
            marker.Change(value => markerMode = value);
            secretMenu.Add(portraits);
            secretMenu.Add(marker);
            if (eeveeShiny) {
                menu.Add(secretMenu);
                secretMenu.AddDescription(menu,"Change the texture used for Portraits and Markers\nfor the shiny variation");
                
            }

        }

        public void CreatemarkerTypeEntry(TextMenu menu, bool inGame) {
            markerMenu = new TextMenu.Slider("Marker Type",markerSlider,0,3,markerType);
            markerMenu.Change(value => {
                markerType = value;
                markerNum = (value != 0) ? value - 1 : markerNum;
            });
            menu.Add(markerMenu);
            markerMenu.AddDescription(menu,"Change the mountain marker between three different variations\n or let the game decide randomly whenever it opens");
        }

        public void CreateoverwritePetEntry(TextMenu menu, bool inGame) {
            submenuPet = new TextMenuExt.SubMenu("Strawberry Friend Options",false);

            TextMenu.OnOff petType = new TextMenu.OnOff("Overwrite Strawberry Friend", overwritePet);
            petType.Change(value => overwritePet = value);

            bool loadedPet = Everest.Loader.DependencyLoaded(EeveeModule.StrawberryPetMeta);
            

            if(loadedPet) {
                TextMenuExt.EaseInSubHeaderExt sPetSub = new TextMenuExt.EaseInSubHeaderExt("-Strawberry Friend Settings", initiallyVisible: loadedPet, menu) {
                    TextColor = Color.Gray,
                    HeightExtra = 0f
                };
                menu.Add(sPetSub);

                submenuPet.Add(petType);
                //petType.AddDescription(menu, "Overwrite some of the berries in the SB Friend mod with a Mini Eevee!!");
                TextMenu.Slider petSkinType = new TextMenu.Slider("Palette type used",petModes,0,1,petSelection);
                if(ExternalPet > externalList.Count) {
                    ExternalPet = 0;
                }
                petIntlist = new TextMenu.Slider("Internal Palettes", getList1, 0, paletteList.Count - 1, InternalPet);
                petIntlist.Change(value => {
                    InternalPet = value;
                    if(petSelection == 0) {
                        updatePet = true;

                        petFluff = SkinPalette.StringtoColor(paletteList[getList1(InternalPet)]);
                    }


                });

                if(EeveeReader.tryGet(palettePath)) {
                    petSkinType.Visible = true;
                    if(ExternalPet > externalList.Count) {
                        ExternalPet = 0;
                    }
                    petExtlist = new TextMenu.Slider("External Palettes", getList2, 0, externalList.Count - 1, ExternalPet);
                    petExtlist.Change(value => {
                        ExternalPet = value;

                        if(externalList.Count > 0 && petSelection == 1) {
                            updatePet = true;
                            petFluff = SkinPalette.StringtoColor(externalList[getList2(ExternalPet)]);
                        }
                    });


                    petSkinType.Change(value => {
                        updatePet = true;
                        petSelection = value;
                        if(value == 1 && externalList.Count > 0 && ExternalOpt < externalList.Count) {
                            petFluff = SkinPalette.StringtoColor(externalList[getList2(ExternalPet)]);

                        } else if(value == 0) {
                            petFluff = SkinPalette.StringtoColor(paletteList[getList1(InternalPet)]);
                        }
                    });
                    submenuPet.Add(petSkinType);
                    submenuPet.Add(petExtlist);

                }
                submenuPet.Add(petIntlist);
                petDash = new TextMenuExt.IntSlider("Palette Dash", 0, 4, PetState);
                petDash.Change(value => {
                    PetState = value;
                    updatePet = true;

                });

                submenuPet.Add(petDash);
                menu.Add(submenuPet);

            }

                

            }

        public void CreatehatDashEntry(TextMenu menu, bool inGame) {
            bool loadedHat = Everest.Loader.DependencyLoaded(EeveeModule.HatelineMeta);
            if(loadedHat) {
                TextMenuExt.EaseInSubHeaderExt sHatSub = new TextMenuExt.EaseInSubHeaderExt("-Hateline Settings", initiallyVisible: loadedHat, menu) {
                    TextColor = Color.Gray,
                    HeightExtra = 0f
                };
                menu.Add(sHatSub);
                TextMenuExt.IntSlider dashCount = new TextMenuExt.IntSlider("Dash variant",0,4,hatDash);
                dashCount.Change(value => hatDash = value);
                menu.Add(dashCount);
                dashCount.AddDescription(menu,"Selecte the dash variant of the Current palette for the\nEevee hat");
            }
        }
        public void CreateSelectionEntry(TextMenu menu, bool inGame) {

            intlist = new TextMenu.Slider("Palette", getList1, 0, paletteList.Count - 1, InternalOpt);
            if (EeveeReader.tryGet(palettePath)) {
                if (ExternalOpt > externalList.Count) {
                    ExternalOpt = 0;
                }
                extlist = new TextMenu.Slider("Palette", getList2, 0, externalList.Count - 1, ExternalOpt);
                crtlist = new TextMenu.Slider("Images available", getList3, 0, createList.Count - 1, 0);
            }

            submenu = new TextMenuExt.OptionSubMenu("Selection Type");
            submenu.SetInitialSelection(Selection);
            submenu.Disabled = false;
            submenu.Visible = true;

            submenu.Add("Internal", new List<TextMenu.Item>
                  {
                     intlist.Change(value => {
                        InternalOpt = value;

                        fluff = SkinPalette.StringtoColor(paletteList[getList1(InternalOpt)]);
                        updatePlayer();
                         bool loadedCelestenet = Everest.Loader.DependencyLoaded(EeveeModule.CelesteNetMeta);
                         if (loadedCelestenet) { updatePaletteCelestenet(); }
                        })
                  }).Change(value => {
                      Selection = value;
                      if (value == 1 && externalList.Count > 0 && ExternalOpt < externalList.Count) {
                          fluff = SkinPalette.StringtoColor(externalList[getList2(ExternalOpt)]);

                      } else if (value == 0) {
                          fluff = SkinPalette.StringtoColor(paletteList[getList1(InternalOpt)]);
                      }
                      bool loadedCelestenet = Everest.Loader.DependencyLoaded(EeveeModule.CelesteNetMeta);
                      if (loadedCelestenet) { updatePaletteCelestenet(); }
                  });

            if (EeveeReader.tryGet(palettePath)) {
                submenu.Add("External", new List<TextMenu.Item> {
                    extlist.Change(value =>{
                        ExternalOpt = value;

                        if(externalList.Count > 0){
                            fluff = SkinPalette.StringtoColor(externalList[getList2(ExternalOpt)]);
                            updatePlayer();
                        }
                        bool loadedCelestenet = Everest.Loader.DependencyLoaded(EeveeModule.CelesteNetMeta);
                         if (loadedCelestenet) { updatePaletteCelestenet(); }
                        })
                    });
            }

            if (EeveeReader.tryGet(palettePath) && inGame == false) {
                if (createList.Count > 0) {
                    CreateOpt = getList3(0);
                    pressed = false;
                }
                submenu.Add("Create", new List<TextMenu.Item> {
                        crtlist.Change(value => CreateOpt = getList3(value)),
                        new TextMenu.Button("Create").Pressed(()=>{
                            if(createList.Count>0 && !pressed){
                                EeveeConverter.convert(palettePath,CreateOpt);
                                externalList = EeveeReader.readContents(palettePath);
                                foreach(string s in externalList.Keys){
                                    holderList.Add(s);
                                }
                                ExternalOpt = holderList.IndexOf(CreateOpt);
                                fluff = SkinPalette.StringtoColor(externalList[CreateOpt]);
                                Selection = 1;
                                pressed = true;
                            }
                        }),

                    });
            }

            menu.Add(submenu);
            if (EeveeReader.tryGet(palettePath) && inGame == false) {
                submenu.AddDescription(menu, "Swap your palette between premade or custom one's\nReopen mod settings between making palettes");
            } else if (!EeveeReader.tryGet(palettePath)) { 
                submenu.AddDescription(menu, "Choose between different palettes inspired by the Eeveelutions");
            } else {
                submenu.AddDescription(menu, "Swap your palette between premade or custom one's");
            }
        }

        public void CreatecNetEnableEntry(TextMenu menu, bool inGame) {
            c_enable = new TextMenu.OnOff("CelesteNet Enable", cNetEnable);
            syncMode = new TextMenu.Slider("Palette sharing", SendReceive, 0, 3, cNetSyncMode);
            //allMode = new TextMenu.OnOff("All Eevees", cNetAllEevee);

            bool loadedCelestenet = Everest.Loader.DependencyLoaded(EeveeModule.CelesteNetMeta);
            c_enable.Disabled = !loadedCelestenet;
            c_enable.Visible = loadedCelestenet;

            syncMode.Disabled = !loadedCelestenet && !cNetEnable;
            syncMode.Visible = loadedCelestenet && cNetEnable;

            //allMode.Disabled = !loadedCelestenet && !cNetEnable;
            //allMode.Visible = loadedCelestenet && cNetEnable;
            c_enable.Change(enabled => {
                cNetEnable = enabled;

                syncMode.Disabled = !enabled;
                syncMode.Visible = enabled;

                //allMode.Disabled = !cNetEnable;
                //allMode.Visible = cNetEnable;
                if (loadedCelestenet) { updatePaletteCelestenet(); }
            });
            if (loadedCelestenet) {
                TextMenuExt.EaseInSubHeaderExt cNetSub = new TextMenuExt.EaseInSubHeaderExt("-CelesteNet Support Menu", initiallyVisible: loadedCelestenet, menu) {
                    TextColor = Color.Gray,
                    HeightExtra = 0f
                };
                menu.Add(cNetSub);
            }

            menu.Add(c_enable);

            syncMode.Change(value => {
                cNetSyncMode = value;
            });
            menu.Add(syncMode);
            syncMode.AddDescription(menu, "Syncronizes the palettes for other players if they too have the skin");

        }
        //This option is quarantined, dunno if i will try to fix now that it works only with SMH+
        //public void CreatecNetAllEeveeEntry(TextMenu menu, bool inGame) {
        //    allMode.Change(value => {
        //        cNetAllEevee = value;
        //    });
        //    menu.Add(allMode);
        //    allMode.AddDescription(menu, "Enable to Render all CelesteNet players as Eevee");
        //    bool loadedCelestenet = Everest.Loader.DependencyLoaded(EeveeModule.CelesteNetMeta);
        //   if (loadedCelestenet) { updatePaletteCelestenet(); }

        //}

        //from here on are only functions used inside each setting's entry, since the custom palette list changes in size, we cant use enums or the like
        //maybe there are better ways for this, but it works atleast.
        public string getList1(int i) {
            List<String> dict = new List<string>();

            foreach (string s in paletteList.Keys) {
                dict.Add(s);
            }

            return dict[i];
        }

        private string getList2(int i) {
            List<String> dict = new List<string>();

            foreach (string s in externalList.Keys) {
                dict.Add(s);
            }

            return dict[i];
        }

        private string getList3(int i) {
            return createList[i];
        }

        private string skinModes(int i) {
            string[] modes = { "default", "normal", "shiny" };
            return modes[i];
        }
        private string petModes(int i) {
            string[] modes = { "internal", "external"};
            return modes[i];
        }

        private string markerSlider(int i) {
            string[] modes = { "random", "running", "sleeping","turning" };
            return modes[i];
        }

        private string extraAnims(int i) {
            string[] extraAnims = { "Never", "Rare", "Always" };
            return extraAnims[i];
        }

        public void ReadContent(string path) {
            ModAsset assets = Everest.Content.Get(path, true);

            if (assets != null) foreach (ModAsset subAsset in assets.Children) {
                    paletteList.Add((subAsset.PathVirtual).Substring(subAsset.PathVirtual.LastIndexOf('/') + 1), subAsset.Deserialize<Palette>());
                }
        }

        public void updatePlayer() {
            if (Engine.Scene is Level level) {
                Player player = level.Tracker.GetEntity<Player>();
                if (player != null) {
                    PlayerSpriteMode mode = (SaveData.Instance.Assists.PlayAsBadeline ? PlayerSpriteMode.MadelineAsBadeline : player.DefaultSpriteMode);
                    if (player.Active) {
                        player.ResetSpriteNextFrame(mode);
                    } else {
                        player.ResetSprite(mode);
                    }
                }
            }
        }
        public void updatePaletteCelestenet() {
            //Taken from the Avali Skin 
            //this needs to be in a seperate function because the entire component
            // relies on an assembly reference
            if (CelesteNetEeveeComponent.Instance != null) {
                CelesteNetEeveeComponent.Instance.SendPaletteSkin();
            }
        }
        private string SendReceive(int i) {
            string holder = "";
            switch (i) {
                case 0:
                    holder = "SendReceive";
                    break;
                case 1:
                    holder = "Send";
                    break;
                case 2:
                    holder = "Receive";
                    break;
                case 3:
                    holder = "None";
                    break;
            }
            return holder;
        }
        //For testing porpuses, who am i kidding, you know what this is
        [YamlIgnore]
        public Palette test = new Palette() {
            ears1 = new List<string>() { "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB" },
            ears2 = new List<string>() { "#403B31", "#403B31", "#403B31", "#403B31", "#403B31" },
            ears3 = new List<string>() { "#806A59", "#806A59", "#806A59", "#806A59", "#806A59" },
            hair = new List<string>() { "#806A59", "#806A59", "#806A59", "#806A59", "#806A59" },
            fluff1 = new List<string>() { "#56B8D1", "#A7BED9", "#FFC98B", "#FF4A77", "#483D61" },
            fluff2 = new List<string>() { "#2C5BC9", "#64677A", "#B67732", "#B53339", "#2A223B" },
            tail1 = new List<string>() { "#56B8D1", "#A7BED9", "#FFC98B", "#FF4A77", "#483D61" },
            tail2 = new List<string>() { "#2C5BC9", "#64677A", "#B67732", "#B53339", "#2A223B" },
            tail3 = new List<string>() { "#1F4A94", "#434D5E", "#70491F", "#851924", "#280C45" },
            eyes = new List<string>() { "#080000", "#080000", "#080000", "#080000", "#080000" },
            face1 = new List<string>() { "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB" },
            face2 = new List<string>() { "#806A59", "#806A59", "#806A59", "#806A59", "#806A59" },
            frontPaw1 = new List<string>() { "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB" },
            FrontPaw2 = new List<string>() { "#4A443F", "#4A443F", "#4A443F", "#4A443F", "#4A443F" },
            backPaw1 = new List<string>() { "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB" },
            backPaw2 = new List<string>() { "#4A443F", "#4A443F", "#4A443F", "#4A443F", "#4A443F" },
            frontPaw3 = new List<string>() { "#66574A", "#66574A", "#66574A", "#66574A", "#66574A" },
            frontPaw4 = new List<string>() { "#1F1D1C", "#1F1D1C", "#1F1D1C", "#1F1D1C", "#1F1D1C" },
            backPaw3 = new List<string>() { "#66574A", "#66574A", "#66574A", "#66574A", "#66574A" },
            backPaw4 = new List<string>() { "#1F1D1C", "#1F1D1C", "#1F1D1C", "#1F1D1C", "#1F1D1C" },
            body1 = new List<string>() { "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB", "#D1C5AB" },
            body2 = new List<string>() { "#806A59", "#806A59", "#806A59", "#806A59", "#806A59" },
            tail4 = new List<string>() { "#806A59", "#806A59", "#806A59", "#806A59", "#806A59" },
            tail5 = new List<string>() { "#66574A", "#66574A", "#66574A", "#66574A", "#66574A" },
            body3 = new List<string>() { "#66574A", "#66574A", "#66574A", "#66574A", "#66574A" },
            body4 = new List<string>() { "#4D3F33", "#4D3F33", "#4D3F33", "#4D3F33", "#4D3F33" },
            dash = new List<string>() { "#1C69AD", "#DEDEDE", "#FFD391", "#FF0A3B", "#3D5061" },
            outline = new List<string>() { "#000000", "#000000", "#000000", "#000000", "#000000" },
        };
    }
}
