using Dalamud.Game.ClientState.Objects.Types;
using System.Collections.Generic;

namespace HSUI.Helpers
{
    public enum JobRoles
    {
        Tank = 0,
        Healer = 1,
        DPSMelee = 2,
        DPSRanged = 3,
        DPSCaster = 4,
        Crafter = 5,
        Gatherer = 6,
        Unknown
    }

    public enum PrimaryResourceTypes
    {
        MP = 0,
        CP = 1,
        GP = 2,
        None = 3
    }

    public static class JobsHelper
    {
        public static JobRoles RoleForJob(uint jobId)
        {
            if (JobRolesMap.TryGetValue(jobId, out var role))
            {
                return role;
            }

            return JobRoles.Unknown;
        }

        public static bool IsJobARole(uint jobId, JobRoles role)
        {
            if (JobRolesMap.TryGetValue(jobId, out var r))
            {
                return r == role;
            }

            return false;
        }

        public static bool IsJobTank(uint jobId)
        {
            return IsJobARole(jobId, JobRoles.Tank);
        }

        public static bool IsJobWithCleanse(uint jobId, int level)
        {
            var isOnCleanseJob = _cleanseJobs.Contains(jobId);

            if (jobId == JobIDs.BRD && level < 35)
            {
                isOnCleanseJob = false;
            }

            return isOnCleanseJob;
        }

        private static readonly List<uint> _cleanseJobs = new List<uint>()
        {
            JobIDs.CNJ,
            JobIDs.WHM,
            JobIDs.SCH,
            JobIDs.AST,
            JobIDs.SGE,
            JobIDs.BRD,
            JobIDs.BLU
        };

        public static bool IsJobHealer(uint jobId)
        {
            return IsJobARole(jobId, JobRoles.Healer);
        }

        public static bool IsJobDPS(uint jobId)
        {
            if (JobRolesMap.TryGetValue(jobId, out var r))
            {
                return r == JobRoles.DPSMelee || r == JobRoles.DPSRanged || r == JobRoles.DPSCaster;
            }

            return false;
        }

        public static bool IsJobDPSMelee(uint jobId)
        {
            return IsJobARole(jobId, JobRoles.DPSMelee);
        }

        public static bool IsJobDPSRanged(uint jobId)
        {
            return IsJobARole(jobId, JobRoles.DPSRanged);
        }

        public static bool IsJobDPSCaster(uint jobId)
        {
            return IsJobARole(jobId, JobRoles.DPSCaster);
        }

        public static bool IsJobCrafter(uint jobId)
        {
            return IsJobARole(jobId, JobRoles.Crafter);
        }

        public static bool IsJobGatherer(uint jobId)
        {
            return IsJobARole(jobId, JobRoles.Gatherer);
        }

        public static bool IsJobWithRaise(uint jobId, uint level)
        {
            var isOnRaiseJob = _raiseJobs.Contains(jobId);

            if ((jobId == JobIDs.RDM && level < 64) || level < 12)
            {
                isOnRaiseJob = false;
            }

            return isOnRaiseJob;
        }

        private static readonly List<uint> _raiseJobs = new List<uint>()
        {
            JobIDs.CNJ,
            JobIDs.WHM,
            JobIDs.SCH,
            JobIDs.AST,
            JobIDs.RDM,
            JobIDs.SMN,
            JobIDs.SGE
        };

        public static uint CurrentPrimaryResource(ICharacter? character)
        {
            if (character == null)
            {
                return 0;
            }

            uint jobId = character.ClassJob.RowId;

            if (IsJobGatherer(jobId))
            {
                return character.CurrentGp;
            }

            if (IsJobCrafter(jobId))
            {
                return character.CurrentCp;
            }

            return character.CurrentMp;
        }

        public static uint MaxPrimaryResource(ICharacter? character)
        {
            if (character == null)
            {
                return 0;
            }

            uint jobId = character.ClassJob.RowId;

            if (IsJobGatherer(jobId))
            {
                return character.MaxGp;
            }

            if (IsJobCrafter(jobId))
            {
                return character.MaxCp;
            }

            return character.MaxMp;
        }

        public static uint GPResourceRate(ICharacter? character)
        {
            if (character == null)
            {
                return 0;
            }

            // Preferably I'd want to check the active traits because these traits are locked behind job quests, but no idea how to check traits.

            // Level 83 Trait 239 (MIN), 240 (BTN), 241 (FSH)
            if (character.Level >= 83)
            {
                return 8;
            }

            // Level 80 Trait 236 (MIN), 237 (BTN), 238 (FSH)
            if (character.Level >= 80)
            {
                return 7;
            }

            // Level 70 Trait 192 (MIN), 193 (BTN), 194 (FSH)
            if (character.Level >= 70)
            {
                return 6;
            }

            return 5;
        }

        public static string TimeTillMaxGP(ICharacter? character)
        {
            if (character == null)
            {
                return "";
            }

            uint jobId = character.ClassJob.RowId;

            if (!IsJobGatherer(jobId))
            {
                return "";
            }

            uint gpRate = GPResourceRate(character);

            if (character.CurrentGp == character.MaxGp)
            {
                return "";
            }

            // Since I'm not using a stopwatch or anything like MPTickHelper here the time will only update every 3 seconds, would be nice if the time ticks down every second.
            float gpPerSecond = gpRate / 3f;
            float secondsTillMax = (character.MaxGp - character.CurrentGp) / gpPerSecond;

            return $"{Utils.DurationToString(secondsTillMax)}";
        }

        public static uint IconIDForJob(uint jobId)
        {
            return jobId + 62000;
        }

        public static uint IconIDForJob(uint jobId, uint style)
        {
            if (style < 2)
            {
                return IconIDForJob(jobId) + style * 100;
            }

            ColorizedIconIDs.TryGetValue(jobId, out var iconID);
            return iconID;
        }

        public static uint RoleIconIDForJob(uint jobId, bool specificDPSIcons = false)
        {
            var role = RoleForJob(jobId);

            switch (role)
            {
                case JobRoles.Tank: return 62581;
                case JobRoles.Healer: return 62582;

                case JobRoles.DPSMelee:
                case JobRoles.DPSRanged:
                case JobRoles.DPSCaster:
                    if (specificDPSIcons && SpecificDPSIcons.TryGetValue(jobId, out var iconId))
                    {
                        return iconId;
                    }
                    else
                    {
                        return 62583;
                    }

                case JobRoles.Gatherer:
                case JobRoles.Crafter:
                    return IconIDForJob(jobId);
            }

            return 0;
        }

        public static uint RoleIconIDForBattleCompanion => 62043;

        public static Dictionary<uint, JobRoles> JobRolesMap = new Dictionary<uint, JobRoles>()
        {
            // tanks
            [JobIDs.GLA] = JobRoles.Tank,
            [JobIDs.MRD] = JobRoles.Tank,
            [JobIDs.PLD] = JobRoles.Tank,
            [JobIDs.WAR] = JobRoles.Tank,
            [JobIDs.DRK] = JobRoles.Tank,
            [JobIDs.GNB] = JobRoles.Tank,

            // healers
            [JobIDs.CNJ] = JobRoles.Healer,
            [JobIDs.WHM] = JobRoles.Healer,
            [JobIDs.SCH] = JobRoles.Healer,
            [JobIDs.AST] = JobRoles.Healer,
            [JobIDs.SGE] = JobRoles.Healer,

            // melee dps
            [JobIDs.PGL] = JobRoles.DPSMelee,
            [JobIDs.LNC] = JobRoles.DPSMelee,
            [JobIDs.ROG] = JobRoles.DPSMelee,
            [JobIDs.MNK] = JobRoles.DPSMelee,
            [JobIDs.DRG] = JobRoles.DPSMelee,
            [JobIDs.NIN] = JobRoles.DPSMelee,
            [JobIDs.SAM] = JobRoles.DPSMelee,
            [JobIDs.RPR] = JobRoles.DPSMelee,
            [JobIDs.VPR] = JobRoles.DPSMelee,

            // ranged phys dps
            [JobIDs.ARC] = JobRoles.DPSRanged,
            [JobIDs.BRD] = JobRoles.DPSRanged,
            [JobIDs.MCH] = JobRoles.DPSRanged,
            [JobIDs.DNC] = JobRoles.DPSRanged,

            // ranged magic dps
            [JobIDs.THM] = JobRoles.DPSCaster,
            [JobIDs.ACN] = JobRoles.DPSCaster,
            [JobIDs.BLM] = JobRoles.DPSCaster,
            [JobIDs.SMN] = JobRoles.DPSCaster,
            [JobIDs.RDM] = JobRoles.DPSCaster,
            [JobIDs.BLU] = JobRoles.DPSCaster,
            [JobIDs.PCT] = JobRoles.DPSCaster,

            // crafters
            [JobIDs.CRP] = JobRoles.Crafter,
            [JobIDs.BSM] = JobRoles.Crafter,
            [JobIDs.ARM] = JobRoles.Crafter,
            [JobIDs.GSM] = JobRoles.Crafter,
            [JobIDs.LTW] = JobRoles.Crafter,
            [JobIDs.WVR] = JobRoles.Crafter,
            [JobIDs.ALC] = JobRoles.Crafter,
            [JobIDs.CUL] = JobRoles.Crafter,

            // gatherers
            [JobIDs.MIN] = JobRoles.Gatherer,
            [JobIDs.BOT] = JobRoles.Gatherer,
            [JobIDs.FSH] = JobRoles.Gatherer,
        };

        public static Dictionary<JobRoles, List<uint>> JobsByRole = new Dictionary<JobRoles, List<uint>>()
        {
            // tanks
            [JobRoles.Tank] = new List<uint>() {
                JobIDs.GLA,
                JobIDs.MRD,
                JobIDs.PLD,
                JobIDs.WAR,
                JobIDs.DRK,
                JobIDs.GNB,
            },

            // healers
            [JobRoles.Healer] = new List<uint>()
            {
                JobIDs.CNJ,
                JobIDs.WHM,
                JobIDs.SCH,
                JobIDs.AST,
                JobIDs.SGE
            },

            // melee dps
            [JobRoles.DPSMelee] = new List<uint>() {
                JobIDs.PGL,
                JobIDs.LNC,
                JobIDs.ROG,
                JobIDs.MNK,
                JobIDs.DRG,
                JobIDs.NIN,
                JobIDs.SAM,
                JobIDs.RPR,
                JobIDs.VPR
            },

            // ranged phys dps
            [JobRoles.DPSRanged] = new List<uint>()
            {
                JobIDs.ARC,
                JobIDs.BRD,
                JobIDs.MCH,
                JobIDs.DNC,
            },

            // ranged magic dps
            [JobRoles.DPSCaster] = new List<uint>()
            {
                JobIDs.THM,
                JobIDs.ACN,
                JobIDs.BLM,
                JobIDs.SMN,
                JobIDs.RDM,
                JobIDs.BLU,
                JobIDs.PCT
            },

            // crafters
            [JobRoles.Crafter] = new List<uint>()
            {
                JobIDs.CRP,
                JobIDs.BSM,
                JobIDs.ARM,
                JobIDs.GSM,
                JobIDs.LTW,
                JobIDs.WVR,
                JobIDs.ALC,
                JobIDs.CUL,
            },

            // gatherers
            [JobRoles.Gatherer] = new List<uint>()
            {
                JobIDs.MIN,
                JobIDs.BOT,
                JobIDs.FSH,
            },

            // unknown
            [JobRoles.Unknown] = new List<uint>()
        };

        public static Dictionary<uint, string> JobNames = new Dictionary<uint, string>()
        {
            // tanks
            [JobIDs.GLA] = "GLA",
            [JobIDs.MRD] = "MRD",
            [JobIDs.PLD] = "PLD",
            [JobIDs.WAR] = "WAR",
            [JobIDs.DRK] = "DRK",
            [JobIDs.GNB] = "GNB",

            // melee dps
            [JobIDs.PGL] = "PGL",
            [JobIDs.LNC] = "LNC",
            [JobIDs.ROG] = "ROG",
            [JobIDs.MNK] = "MNK",
            [JobIDs.DRG] = "DRG",
            [JobIDs.NIN] = "NIN",
            [JobIDs.SAM] = "SAM",
            [JobIDs.RPR] = "RPR",
            [JobIDs.VPR] = "VPR",

            // ranged phys dps
            [JobIDs.ARC] = "ARC",
            [JobIDs.BRD] = "BRD",
            [JobIDs.MCH] = "MCH",
            [JobIDs.DNC] = "DNC",

            // ranged magic dps
            [JobIDs.THM] = "THM",
            [JobIDs.ACN] = "ACN",
            [JobIDs.BLM] = "BLM",
            [JobIDs.SMN] = "SMN",
            [JobIDs.RDM] = "RDM",
            [JobIDs.BLU] = "BLU",
            [JobIDs.PCT] = "PCT",

            // healers
            [JobIDs.CNJ] = "CNJ",
            [JobIDs.WHM] = "WHM",
            [JobIDs.SCH] = "SCH",
            [JobIDs.SGE] = "SGE",
            [JobIDs.AST] = "AST",

            // crafters
            [JobIDs.CRP] = "CRP",
            [JobIDs.BSM] = "BSM",
            [JobIDs.ARM] = "ARM",
            [JobIDs.GSM] = "GSM",
            [JobIDs.LTW] = "LTW",
            [JobIDs.WVR] = "WVR",
            [JobIDs.ALC] = "ALC",
            [JobIDs.CUL] = "CUL",

            // gatherers
            [JobIDs.MIN] = "MIN",
            [JobIDs.BOT] = "BOT",
            [JobIDs.FSH] = "FSH",
        };
        
        public static Dictionary<uint, string> JobFullNames = new Dictionary<uint, string>()
        {
            // tanks
            [JobIDs.GLA] = "Gladiator",
            [JobIDs.MRD] = "Marauder",
            [JobIDs.PLD] = "Paladin",
            [JobIDs.WAR] = "Warrior",
            [JobIDs.DRK] = "Dark Knight",
            [JobIDs.GNB] = "Gunbreaker",

            // melee dps
            [JobIDs.PGL] = "Pugilist",
            [JobIDs.LNC] = "Lancer",
            [JobIDs.ROG] = "Rogue",
            [JobIDs.MNK] = "Monk",
            [JobIDs.DRG] = "Dragoon",
            [JobIDs.NIN] = "Ninja",
            [JobIDs.SAM] = "Samurai",
            [JobIDs.RPR] = "Reaper",
            [JobIDs.VPR] = "Viper",

            // ranged phys dps
            [JobIDs.ARC] = "Archer",
            [JobIDs.BRD] = "Bard",
            [JobIDs.MCH] = "Machinist",
            [JobIDs.DNC] = "Dancer",

            // ranged magic dps
            [JobIDs.THM] = "Thaumaturge",
            [JobIDs.ACN] = "Arcanist",
            [JobIDs.BLM] = "Black Mage",
            [JobIDs.SMN] = "Summoner",
            [JobIDs.RDM] = "Red Mage",
            [JobIDs.BLU] = "Blue Mage",
            [JobIDs.PCT] = "Pictomancer",

            // healers
            [JobIDs.CNJ] = "Conjurer",
            [JobIDs.WHM] = "White Mage",
            [JobIDs.SCH] = "Scholar",
            [JobIDs.SGE] = "Sage",
            [JobIDs.AST] = "Astrologian",

            // crafters
            [JobIDs.CRP] = "Carpenter",
            [JobIDs.BSM] = "Blacksmith",
            [JobIDs.ARM] = "Armorer",
            [JobIDs.GSM] = "Goldsmith",
            [JobIDs.LTW] = "Leatherworker",
            [JobIDs.WVR] = "Weaver",
            [JobIDs.ALC] = "Alchemist",
            [JobIDs.CUL] = "Culinarian",

            // gatherers
            [JobIDs.MIN] = "Miner",
            [JobIDs.BOT] = "Botanist",
            [JobIDs.FSH] = "Fisher",
        };

        public static Dictionary<JobRoles, string> RoleNames = new Dictionary<JobRoles, string>()
        {
            [JobRoles.Tank] = "Tank",
            [JobRoles.Healer] = "Healer",
            [JobRoles.DPSMelee] = "Melee",
            [JobRoles.DPSRanged] = "Ranged",
            [JobRoles.DPSCaster] = "Caster",
            [JobRoles.Crafter] = "Crafter",
            [JobRoles.Gatherer] = "Gatherer",
            [JobRoles.Unknown] = "Unknown"
        };

        public static Dictionary<uint, uint> SpecificDPSIcons = new Dictionary<uint, uint>()
        {
            // melee dps
            [JobIDs.PGL] = 62584,
            [JobIDs.LNC] = 62584,
            [JobIDs.ROG] = 62584,
            [JobIDs.MNK] = 62584,
            [JobIDs.DRG] = 62584,
            [JobIDs.NIN] = 62584,
            [JobIDs.SAM] = 62584,
            [JobIDs.RPR] = 62584,
            [JobIDs.VPR] = 62584,

            // ranged phys dps
            [JobIDs.ARC] = 62586,
            [JobIDs.BRD] = 62586,
            [JobIDs.MCH] = 62586,
            [JobIDs.DNC] = 62586,

            // ranged magic dps
            [JobIDs.THM] = 62587,
            [JobIDs.ACN] = 62587,
            [JobIDs.BLM] = 62587,
            [JobIDs.SMN] = 62587,
            [JobIDs.RDM] = 62587,
            [JobIDs.BLU] = 62587,
            [JobIDs.PCT] = 62587
        };

        public static Dictionary<uint, uint> ColorizedIconIDs = new Dictionary<uint, uint>()
        {
            // tanks
            [JobIDs.GLA] = 94022,
            [JobIDs.MRD] = 94024,
            [JobIDs.PLD] = 94079,
            [JobIDs.WAR] = 94081,
            [JobIDs.DRK] = 94123,
            [JobIDs.GNB] = 94130,

            // melee dps
            [JobIDs.PGL] = 92523,
            [JobIDs.LNC] = 92525,
            [JobIDs.ROG] = 92621,
            [JobIDs.MNK] = 92580,
            [JobIDs.DRG] = 92582,
            [JobIDs.NIN] = 92622,
            [JobIDs.SAM] = 92627,
            [JobIDs.RPR] = 92632,
            [JobIDs.VPR] = 92685,

            // ranged phys dps
            [JobIDs.ARC] = 92526,
            [JobIDs.BRD] = 92583,
            [JobIDs.MCH] = 92625,
            [JobIDs.DNC] = 92631,

            // ranged magic dps
            [JobIDs.THM] = 92529,
            [JobIDs.ACN] = 92530,
            [JobIDs.BLM] = 92585,
            [JobIDs.SMN] = 92586,
            [JobIDs.RDM] = 92628,
            [JobIDs.BLU] = 92629,
            [JobIDs.PCT] = 92686,

            // healers
            [JobIDs.CNJ] = 94528,
            [JobIDs.WHM] = 94584,
            [JobIDs.SCH] = 94587,
            [JobIDs.SGE] = 94633,
            [JobIDs.AST] = 94624,

            // crafters
            [JobIDs.CRP] = 91031,
            [JobIDs.BSM] = 91032,
            [JobIDs.ARM] = 91033,
            [JobIDs.GSM] = 91034,
            [JobIDs.LTW] = 91034,
            [JobIDs.WVR] = 91036,
            [JobIDs.ALC] = 91037,
            [JobIDs.CUL] = 91038,

            // gatherers
            [JobIDs.MIN] = 91039,
            [JobIDs.BOT] = 91039,
            [JobIDs.FSH] = 91041,
        };

        public static Dictionary<JobRoles, PrimaryResourceTypes> PrimaryResourceTypesByRole = new Dictionary<JobRoles, PrimaryResourceTypes>()
        {
            [JobRoles.Tank] = PrimaryResourceTypes.MP,
            [JobRoles.Healer] = PrimaryResourceTypes.MP,
            [JobRoles.DPSMelee] = PrimaryResourceTypes.MP,
            [JobRoles.DPSRanged] = PrimaryResourceTypes.MP,
            [JobRoles.DPSCaster] = PrimaryResourceTypes.MP,
            [JobRoles.Crafter] = PrimaryResourceTypes.CP,
            [JobRoles.Gatherer] = PrimaryResourceTypes.GP,
            [JobRoles.Unknown] = PrimaryResourceTypes.MP
        };
    }

    public static class JobIDs
    {
        public const uint GLA = 1;
        public const uint MRD = 3;
        public const uint PLD = 19;
        public const uint WAR = 21;
        public const uint DRK = 32;
        public const uint GNB = 37;

        public const uint CNJ = 6;
        public const uint WHM = 24;
        public const uint SCH = 28;
        public const uint AST = 33;
        public const uint SGE = 40;

        public const uint PGL = 2;
        public const uint LNC = 4;
        public const uint ROG = 29;
        public const uint MNK = 20;
        public const uint DRG = 22;
        public const uint NIN = 30;
        public const uint SAM = 34;
        public const uint RPR = 39;
        public const uint VPR = 41;

        public const uint ARC = 5;
        public const uint BRD = 23;
        public const uint MCH = 31;
        public const uint DNC = 38;

        public const uint THM = 7;
        public const uint ACN = 26;
        public const uint BLM = 25;
        public const uint SMN = 27;
        public const uint RDM = 35;
        public const uint BLU = 36;
        public const uint PCT = 42;

        public const uint CRP = 8;
        public const uint BSM = 9;
        public const uint ARM = 10;
        public const uint GSM = 11;
        public const uint LTW = 12;
        public const uint WVR = 13;
        public const uint ALC = 14;
        public const uint CUL = 15;

        public const uint MIN = 16;
        public const uint BOT = 17;
        public const uint FSH = 18;
    }
}
