using Microsoft.Xna.Framework.Graphics;
using oldschoolcrafting.Content.Items;
using ReLogic.Content;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using Terraria;
using Terraria.GameContent;
using Terraria.GameContent.ItemDropRules;
using Terraria.ID;
using Terraria.Localization;
using Terraria.ModLoader;

namespace oldschoolcrafting
{
    public class oldschoolcrafting : Mod
    {
    }

    public class RecipieChanges : ModSystem
    {
        private record IngredientSpec(bool IsGroup, int Id, int Stack);

        private static IngredientSpec Item(int itemId, int stack = 1) => new(false, itemId, stack);
        private static IngredientSpec Group(int groupId, int stack = 1) => new(true, groupId, stack);

        private record RecipeReplacement
        {
            public IngredientSpec[][] Variants { get; }
            public int[] TileOverride { get; }

            public RecipeReplacement(IngredientSpec[] ingredients, int[] tileOverride = null)
            {
                Variants = new[] { ingredients };
                TileOverride = tileOverride;
            }

            public RecipeReplacement(IngredientSpec[][] variants, int[] tileOverride = null)
            {
                Variants = variants;
                TileOverride = tileOverride;
            }
        }

        private static int AdamantiteOrTitaniumHelmetMelee;
        private static int AdamantiteOrTitaniumHelmetRanged;
        private static int AdamantiteOrTitaniumHelmetMagic;
        private static int AdamantiteOrTitaniumChest;
        private static int AdamantiteOrTitaniumLegs;
        private static int CobaltOrPalladiumAnyHelmet;
        private static int MythrilOrOrichalcumAnyHelmet;
        private static int AdamantiteOrTitaniumAnyHelmet;
        private static int CobaltOrPalladiumHelmetMelee;
        private static int CobaltOrPalladiumHelmetRanged;
        private static int CobaltOrPalladiumHelmetMagic;
        private static int CobaltOrPalladiumChest;
        private static int CobaltOrPalladiumLegs;
        private static int MythrilOrOrichalcumHelmetMelee;
        private static int MythrilOrOrichalcumHelmetRanged;
        private static int MythrilOrOrichalcumHelmetMagic;
        private static int MythrilOrOrichalcumChest;
        private static int MythrilOrOrichalcumLegs;
        private static int CopperOrTinBar;
        private static int SilverOrTungstenBar;
        private static int GoldOrPlatinumBar;
        private static int CobaltOrPalladiumBar;
        private static int MythrilOrOrichalcumBar;
        private static int AdamantiteOrTitaniumBar;
        private static int CobaltOrPalladiumPickaxe;
        private static int MythrilOrOrichalcumPickaxe;
        private static int AdamantiteOrTitaniumPickaxe;
        private static int CobaltOrPalladiumDrill;
        private static int MythrilOrOrichalcumDrill;
        private static int AdamantiteOrTitaniumDrill;
        private static int CobaltOrPalladiumWaraxe;
        private static int MythrilOrOrichalcumWaraxe;
        private static int AdamantiteOrTitaniumWaraxe;
        private static int CobaltOrPalladiumChainsaw;
        private static int MythrilOrOrichalcumChainsaw;
        private static int AdamantiteOrTitaniumChainsaw;
        private static int CobaltOrPalladiumSword;
        private static int MythrilOrOrichalcumSword;
        private static int AdamantiteOrTitaniumSword;
        private static int CobaltOrPalladiumRepeater;
        private static int MythrilOrOrichalcumRepeater;
        private static int AdamantiteOrTitaniumRepeater;
        private static int CobaltOrPalladiumSpear;
        private static int MythrilOrOrichalcumSpear;
        private static int AdamantiteOrTitaniumSpear;

        public override void AddRecipeGroups()
        {
            CopperOrTinBar = RegisterGroup(
               "CopperOrTinBar",
               new int[] {
        ItemID.CopperBar, ItemID.TinBar
               },
               displayNameOverride: "Any Copper Bar");

            /* RecipieGroupID already has IronBar for us, thanks 1.2! */

            SilverOrTungstenBar = RegisterGroup(
              "CopperOrTinBar",
              new int[] {
        ItemID.SilverBar, ItemID.TungstenBar
              },
              displayNameOverride: "Any Silver Bar");

            GoldOrPlatinumBar = RegisterGroup(
              "CopperOrTinBar",
              new int[] {
        ItemID.GoldBar, ItemID.PlatinumBar
              },
              displayNameOverride: "Any Gold Bar");

            /* T1 to T3 HM Ore Bars */
            CobaltOrPalladiumBar = RegisterGroup(
       "CobaltOrPalladiumBar", ItemID.CobaltBar, ItemID.PalladiumBar);

            MythrilOrOrichalcumBar = RegisterGroup(
                "MythrilOrOrichalcumBar", ItemID.MythrilBar, ItemID.OrichalcumBar);

            AdamantiteOrTitaniumBar = RegisterGroup(
                "AdamantiteOrTitaniumBar", ItemID.AdamantiteBar, ItemID.TitaniumBar);

            /* Cobalt */
            CobaltOrPalladiumHelmetMelee = RegisterGroup(
                "CobaltOrPalladiumHelmetMelee",
                ItemID.CobaltHelmet, ItemID.PalladiumMask);

            CobaltOrPalladiumHelmetRanged = RegisterGroup(
                "CobaltOrPalladiumHelmetRanged",
                ItemID.CobaltMask, ItemID.PalladiumHelmet);

            CobaltOrPalladiumHelmetMagic = RegisterGroup(
                "CobaltOrPalladiumHelmetMagic",
                ItemID.CobaltHat, ItemID.PalladiumHeadgear);

            CobaltOrPalladiumChest = RegisterGroup(
                "CobaltOrPalladiumChest",
                ItemID.CobaltBreastplate, ItemID.PalladiumBreastplate);

            CobaltOrPalladiumLegs = RegisterGroup(
                "CobaltOrPalladiumLegs",
                ItemID.CobaltLeggings, ItemID.PalladiumLeggings);


            /* Mythril */
            MythrilOrOrichalcumHelmetMelee = RegisterGroup(
                "MythrilOrOrichalcumHelmetMelee",
                ItemID.MythrilHelmet, ItemID.OrichalcumMask);

            MythrilOrOrichalcumHelmetRanged = RegisterGroup(
                "MythrilOrOrichalcumHelmetRanged",
                ItemID.MythrilHat, ItemID.OrichalcumHelmet);

            MythrilOrOrichalcumHelmetMagic = RegisterGroup(
                "MythrilOrOrichalcumHelmetMagic",
                ItemID.MythrilHood, ItemID.OrichalcumHeadgear);

            MythrilOrOrichalcumChest = RegisterGroup(
                "MythrilOrOrichalcumChest",
                ItemID.MythrilChainmail, ItemID.OrichalcumBreastplate);

            MythrilOrOrichalcumLegs = RegisterGroup(
                "MythrilOrOrichalcumLegs",
                ItemID.MythrilGreaves, ItemID.OrichalcumLeggings);

            /* Adamantite */
            AdamantiteOrTitaniumHelmetMelee = RegisterGroup(
               "AdamantiteOrTitaniumHelmetMelee", ItemID.AdamantiteHelmet, ItemID.TitaniumHelmet);

            AdamantiteOrTitaniumHelmetRanged = RegisterGroup(
                "AdamantiteOrTitaniumHelmetRanged", ItemID.AdamantiteMask, ItemID.TitaniumMask);

            AdamantiteOrTitaniumHelmetMagic = RegisterGroup(
                "AdamantiteOrTitaniumHelmetMagic", ItemID.AdamantiteHeadgear, ItemID.TitaniumHeadgear);

            AdamantiteOrTitaniumChest = RegisterGroup(
                "AdamantiteOrTitaniumChest", ItemID.AdamantiteBreastplate, ItemID.TitaniumBreastplate);

            AdamantiteOrTitaniumLegs = RegisterGroup(
                "AdamantiteOrTitaniumLegs", ItemID.AdamantiteLeggings, ItemID.TitaniumLeggings);


            /* Any Helmet */
            CobaltOrPalladiumAnyHelmet = RegisterGroup(
     "CobaltOrPalladiumAnyHelmet",
     new int[] {
        ItemID.CobaltHelmet, ItemID.CobaltMask, ItemID.CobaltHat,
        ItemID.PalladiumMask, ItemID.PalladiumHelmet, ItemID.PalladiumHeadgear,
     },
     displayNameOverride: "Any Cobalt or Palladium Helmets");

            MythrilOrOrichalcumAnyHelmet = RegisterGroup(
                "MythrilOrOrichalcumAnyHelmet",
                new int[] {
        ItemID.MythrilHelmet, ItemID.MythrilHat, ItemID.MythrilHood,
        ItemID.OrichalcumMask, ItemID.OrichalcumHelmet, ItemID.OrichalcumHeadgear,
                },
                displayNameOverride: "Any Mythril or Orichalcum Helmets");

            AdamantiteOrTitaniumAnyHelmet = RegisterGroup(
                "AdamantiteOrTitaniumAnyHelmet",
                new int[] {
        ItemID.AdamantiteHelmet, ItemID.AdamantiteMask, ItemID.AdamantiteHeadgear,
        ItemID.TitaniumHelmet, ItemID.TitaniumMask, ItemID.TitaniumHeadgear,
                },
                displayNameOverride: "Any Adamantite or Titanium Helmets");


            CobaltOrPalladiumPickaxe = RegisterGroup(
                "CobaltOrPalladiumPickaxe", ItemID.CobaltPickaxe, ItemID.PalladiumPickaxe);
            MythrilOrOrichalcumPickaxe = RegisterGroup(
                "MythrilOrOrichalcumPickaxe", ItemID.MythrilPickaxe, ItemID.OrichalcumPickaxe);
            AdamantiteOrTitaniumPickaxe = RegisterGroup(
                "AdamantiteOrTitaniumPickaxe", ItemID.AdamantitePickaxe, ItemID.TitaniumPickaxe);

            CobaltOrPalladiumDrill = RegisterGroup(
                "CobaltOrPalladiumDrill", ItemID.CobaltDrill, ItemID.PalladiumDrill);
            MythrilOrOrichalcumDrill = RegisterGroup(
                "MythrilOrOrichalcumDrill", ItemID.MythrilDrill, ItemID.OrichalcumDrill);
            AdamantiteOrTitaniumDrill = RegisterGroup(
                "AdamantiteOrTitaniumDrill", ItemID.AdamantiteDrill, ItemID.TitaniumDrill);

            CobaltOrPalladiumWaraxe = RegisterGroup(
                "CobaltOrPalladiumWaraxe", ItemID.CobaltWaraxe, ItemID.PalladiumWaraxe);
            MythrilOrOrichalcumWaraxe = RegisterGroup(
                "MythrilOrOrichalcumWaraxe", ItemID.MythrilWaraxe, ItemID.OrichalcumWaraxe);
            AdamantiteOrTitaniumWaraxe = RegisterGroup(
                "AdamantiteOrTitaniumWaraxe", ItemID.AdamantiteWaraxe, ItemID.TitaniumWaraxe);

            CobaltOrPalladiumChainsaw = RegisterGroup(
                "CobaltOrPalladiumChainsaw", ItemID.CobaltChainsaw, ItemID.PalladiumChainsaw);
            MythrilOrOrichalcumChainsaw = RegisterGroup(
                "MythrilOrOrichalcumChainsaw", ItemID.MythrilChainsaw, ItemID.OrichalcumChainsaw);
            AdamantiteOrTitaniumChainsaw = RegisterGroup(
                "AdamantiteOrTitaniumChainsaw", ItemID.AdamantiteChainsaw, ItemID.TitaniumChainsaw);

            CobaltOrPalladiumSword = RegisterGroup(
                "CobaltOrPalladiumSword", ItemID.CobaltSword, ItemID.PalladiumSword);
            MythrilOrOrichalcumSword = RegisterGroup(
                "MythrilOrOrichalcumSword", ItemID.MythrilSword, ItemID.OrichalcumSword);
            AdamantiteOrTitaniumSword = RegisterGroup(
                "AdamantiteOrTitaniumSword", ItemID.AdamantiteSword, ItemID.TitaniumSword);

            CobaltOrPalladiumRepeater = RegisterGroup(
                "CobaltOrPalladiumRepeater", ItemID.CobaltRepeater, ItemID.PalladiumRepeater);
            MythrilOrOrichalcumRepeater = RegisterGroup(
                "MythrilOrOrichalcumRepeater", ItemID.MythrilRepeater, ItemID.OrichalcumRepeater);
            AdamantiteOrTitaniumRepeater = RegisterGroup(
                "AdamantiteOrTitaniumRepeater", ItemID.AdamantiteRepeater, ItemID.TitaniumRepeater);

            CobaltOrPalladiumSpear = RegisterGroup(
                "CobaltOrPalladiumSpear", ItemID.CobaltNaginata, ItemID.PalladiumPike);
            MythrilOrOrichalcumSpear = RegisterGroup(
                "MythrilOrOrichalcumSpear", ItemID.MythrilHalberd, ItemID.OrichalcumHalberd);
            AdamantiteOrTitaniumSpear = RegisterGroup(
                "AdamantiteOrTitaniumSpear", ItemID.AdamantiteGlaive, ItemID.TitaniumTrident);


        }


        private static string FormatGroupName(int[] items, string displayNameOverride)
        {
            if (displayNameOverride != null)
                return displayNameOverride;

            var names = items.Select(Lang.GetItemNameValue).ToArray();

            return names.Length switch
            {
                1 => names[0],
                2 => $"{names[0]} or {names[1]}",
                _ => string.Join(", ", names[..^1]) + $", or {names[^1]}"
            };
        }

        private static int RegisterGroup(string name, int[] items, string displayNameOverride = null)
        {
            var group = new RecipeGroup(() => FormatGroupName(items, displayNameOverride), items);
            return RecipeGroup.RegisterGroup($"oldschoolcrafting:{name}", group);
        }

        private static int RegisterGroup(string name, params int[] items) => RegisterGroup(name, items, displayNameOverride: null);


        private static Dictionary<int, RecipeReplacement> BuildReplacements() => new()
        {
            /* Jungle Stuff */
            #region Jungle Stuff
            [ItemID.JungleHat] = new RecipeReplacement(new IngredientSpec[][]
            {
        new[] { Item(ItemID.SilverHelmet, 1), Item(ItemID.Sapphire, 1), Item(ItemID.Emerald, 1), Item(ItemID.JungleSpores, 15) },
        new[] { Item(ItemID.TungstenHelmet, 1), Item(ItemID.Sapphire, 1), Item(ItemID.Emerald, 1), Item(ItemID.JungleSpores, 15) },
            }),

            [ItemID.JungleShirt] = new RecipeReplacement(new IngredientSpec[][]
            {
        new[] { Item(ItemID.SilverChainmail, 1), Item(ItemID.Ruby, 1), Item(ItemID.Diamond, 1), Item(ItemID.JungleSpores, 20), Item(ItemID.Stinger, 20) },
        new[] { Item(ItemID.TungstenChainmail, 1), Item(ItemID.Ruby, 1), Item(ItemID.Diamond, 1), Item(ItemID.JungleSpores, 20), Item(ItemID.Stinger, 20) },
            }),

            [ItemID.JunglePants] = new RecipeReplacement(new IngredientSpec[][]
            {
        new[] { Item(ItemID.SilverGreaves, 1), Item(ItemID.Amethyst, 1), Item(ItemID.Topaz, 1), Item(ItemID.JungleSpores, 20), Item(ItemID.Vine, 2) },
        new[] { Item(ItemID.TungstenGreaves, 1), Item(ItemID.Amethyst, 1), Item(ItemID.Topaz, 1), Item(ItemID.JungleSpores, 20), Item(ItemID.Vine, 2) },
            }),

            [ItemID.IvyWhip] = new RecipeReplacement(new[]
            {
        Item(ItemID.GrapplingHook, 1),
        Item(ItemID.JungleSpores, 15),
        Item(ItemID.Vine, 3),
    }),

            [ItemID.BladeofGrass] = new RecipeReplacement(new IngredientSpec[][]
            {
        new[] { Item(ItemID.SilverBroadsword, 1), Item(ItemID.JungleSpores, 20), Item(ItemID.Stinger, 20) },
        new[] { Item(ItemID.TungstenBroadsword, 1), Item(ItemID.JungleSpores, 20), Item(ItemID.Stinger, 20) },
            }),

            [ItemID.ThornChakram] = new RecipeReplacement(new[]
            {
        Item(ItemID.JungleSpores, 15),
        Item(ItemID.Stinger, 20)
    }),
            /* End Jungle Stuff */
            #endregion

            /* Hell Stuff */
            #region Hell Stuff
            [ItemID.Flamarang] = new RecipeReplacement(new[]
            {
        Item(ItemID.HellstoneBar, 15),
        Item(ItemID.EnchantedBoomerang, 1)
    }),

            [ItemID.MoltenFury] = new RecipeReplacement(new[]
            {
        Item(ItemID.HellstoneBar, 25),
    }),

            [ItemID.FieryGreatsword] = new RecipeReplacement(new[]
            {
        Item(ItemID.HellstoneBar, 35),
    }),

            [ItemID.MoltenPickaxe] = new RecipeReplacement(new[]
            {
        Item(ItemID.HellstoneBar, 35),
    }),

            [ItemID.MoltenHamaxe] = new RecipeReplacement(new[]
            {
        Item(ItemID.HellstoneBar, 35),
    }),

            [ItemID.PhoenixBlaster] = new RecipeReplacement(new[]
            {
        Item(ItemID.HellstoneBar, 20),
        Item(ItemID.Handgun, 1),
    }),

            [ItemID.MoltenHelmet] = new RecipeReplacement(new IngredientSpec[][]
            {
        new[] { Item(ItemID.GoldHelmet, 1), Item(ItemID.HellstoneBar, 30) },
        new[] { Item(ItemID.PlatinumHelmet, 1), Item(ItemID.HellstoneBar, 30) },
            }),

            [ItemID.MoltenBreastplate] = new RecipeReplacement(new IngredientSpec[][]
            {
        new[] { Item(ItemID.GoldChainmail, 1), Item(ItemID.HellstoneBar, 40) },
        new[] { Item(ItemID.PlatinumChainmail, 1), Item(ItemID.HellstoneBar, 40) },
            }),

            [ItemID.MoltenGreaves] = new RecipeReplacement(new IngredientSpec[][]
            {
        new[] { Item(ItemID.GoldGreaves, 1), Item(ItemID.HellstoneBar, 35) },
        new[] { Item(ItemID.PlatinumGreaves, 1), Item(ItemID.HellstoneBar, 35) },
            }),

            /** Custom: 75% more expensive, using 35 -> 20 bars from the Fiery Greatsword (Volcano) as a baseline. **/
            [ItemID.ImpStaff] = new RecipeReplacement(new[]
            {
                Item(ItemID.HellstoneBar, 30),
            }),
            /* End Hell */
            #endregion

            #region Ores
            /* Ores */
            [ItemID.AdamantiteBar] = new RecipeReplacement(new[]
            {
                Item(ItemID.AdamantiteOre, 5),
            }),

            [ItemID.TitaniumBar] = new RecipeReplacement(new[]
            {
                Item(ItemID.TitaniumOre, 5),
            }),

            [ItemID.ChlorophyteBar] = new RecipeReplacement(new[]
            {
                Item(ItemID.ChlorophyteOre, 6),
            }),

            [ItemID.DemoniteBar] = new RecipeReplacement(new[]
            {
                Item(ItemID.DemoniteOre, 4),
            }),

            [ItemID.CrimtaneBar] = new RecipeReplacement(new[]
            {
                Item(ItemID.CrimtaneOre, 4),
            }),

            /* Meteorite */
            /** 1.1 Recipie **/
            [ItemID.MeteoriteBar] = new RecipeReplacement(new[]
            {
                Item(ItemID.Meteorite, 4),
            }),
            #endregion
            /* End Ores */

            #region Meteorite
            [ItemID.SpaceGun] = new RecipeReplacement(new[]
            {
                Item(ItemID.FlintlockPistol, 1),
                Item(ItemID.MeteoriteBar, 30),
                Item(ItemID.FallenStar, 10),
            }),

            [ItemID.MeteorHamaxe] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 35),
            }),

            [ItemID.MeteorHamaxe] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 35),
            }),

            [ItemID.MeteorHelmet] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 20),
            }),
            [ItemID.MeteorSuit] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 30),
            }),
            [ItemID.MeteorHelmet] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 25),
            }),
            [ItemID.BluePhaseblade] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 20),
                Item(ItemID.Sapphire, 10),
            }),
            [ItemID.RedPhaseblade] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 20),
                Item(ItemID.Ruby, 10),
            }),
            [ItemID.GreenPhaseblade] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 20),
                Item(ItemID.Emerald, 10),
            }),
            [ItemID.PurplePhaseblade] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 20),
                Item(ItemID.Amethyst, 10),
            }),
            [ItemID.WhitePhaseblade] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 20),
                Item(ItemID.Diamond, 10),
            }),
            [ItemID.YellowPhaseblade] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 20),
                Item(ItemID.Topaz, 10),
            }),
            [ItemID.OrangePhaseblade] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 20),
                Item(ItemID.Amber, 10),
            }),
            /* Not in 1.4.4.9...
            [ItemID.RainbowPhaseblade] = new RecipeReplacement(new[]
            {

            }),
            */
            /* End Meteroite */
            #endregion

            /* Hallowed / 1.1 Era */
            #region Hallowed / 1.1 Era
            [ItemID.HallowedMask] = new RecipeReplacement(new[]
            {
                   Group(CobaltOrPalladiumHelmetMelee, 1),
                              Group(MythrilOrOrichalcumHelmetMelee, 1),
            Group(AdamantiteOrTitaniumHelmetMelee, 1),


            Item(ItemID.HallowedBar, 12),
            Item(ItemID.SoulofSight, 20)
        }),
            [ItemID.HallowedHelmet] = new RecipeReplacement(new[]
            {
                        Group(CobaltOrPalladiumHelmetRanged, 1),
            Group(MythrilOrOrichalcumHelmetRanged, 1),
                Group(AdamantiteOrTitaniumHelmetRanged, 1),

            Item(ItemID.HallowedBar, 12),
            Item(ItemID.SoulofSight, 20)
        }),
            [ItemID.HallowedHeadgear] = new RecipeReplacement(new[]
            {
                            Group(CobaltOrPalladiumHelmetMagic, 1),

            Group(MythrilOrOrichalcumHelmetMagic, 1),
                        Group(AdamantiteOrTitaniumHelmetMagic, 1),

            Item(ItemID.HallowedBar, 12),
            Item(ItemID.SoulofSight, 20)
        }),


            [ItemID.HallowedPlateMail] = new RecipeReplacement(new[]
            {
                            Group(CobaltOrPalladiumChest, 1),

            Group(MythrilOrOrichalcumChest, 1),
                        Group(AdamantiteOrTitaniumChest, 1),

            Item(ItemID.HallowedBar, 18),
            Item(ItemID.SoulofMight, 20)
        }),

            [ItemID.HallowedGreaves] = new RecipeReplacement(new[]
            {
                Group(CobaltOrPalladiumLegs, 1),
                Group(MythrilOrOrichalcumLegs, 1),
                Group(AdamantiteOrTitaniumLegs, 1),

            Item(ItemID.HallowedBar, 15),
            Item(ItemID.SoulofFright, 20)
        }),
            [ItemID.HallowedHood] = new RecipeReplacement(new IngredientSpec[][]
            {
                new []  {
                Group(CobaltOrPalladiumAnyHelmet, 1),
                 Group(MythrilOrOrichalcumAnyHelmet, 1),
                  Group(AdamantiteOrTitaniumAnyHelmet, 1),
                Item(ItemID.HallowedBar, 12),
                Item(ItemID.SoulofSight, 20)
                    },
                //new [] {
                //    Group(CobaltOrPalladiumBar, 12), 
                //    Group(MythrilOrOrichalcumBar, 12),
                //    Group(AdamantiteOrTitaniumBar, 12),
                //Item(ItemID.HallowedBar, 12),
                //Item(ItemID.SoulofSight, 20) }
        }),


            /* Ancient Hallow */
            [ItemID.AncientHallowedMask] = new RecipeReplacement(new[]
            {
                   Group(CobaltOrPalladiumHelmetMelee, 1),
                              Group(MythrilOrOrichalcumHelmetMelee, 1),
            Group(AdamantiteOrTitaniumHelmetMelee, 1),


            Item(ItemID.HallowedBar, 12),
            Item(ItemID.SoulofSight, 20)
        }),
            [ItemID.AncientHallowedHelmet] = new RecipeReplacement(new[]
            {
                        Group(CobaltOrPalladiumHelmetRanged, 1),
            Group(MythrilOrOrichalcumHelmetRanged, 1),
                Group(AdamantiteOrTitaniumHelmetRanged, 1),

            Item(ItemID.HallowedBar, 12),
            Item(ItemID.SoulofSight, 20)
        }),
            [ItemID.AncientHallowedHeadgear] = new RecipeReplacement(new[]
            {
                            Group(CobaltOrPalladiumHelmetMagic, 1),

            Group(MythrilOrOrichalcumHelmetMagic, 1),
                        Group(AdamantiteOrTitaniumHelmetMagic, 1),

            Item(ItemID.HallowedBar, 12),
            Item(ItemID.SoulofSight, 20)
        }),


            [ItemID.AncientHallowedPlateMail] = new RecipeReplacement(new[]
            {
                            Group(CobaltOrPalladiumChest, 1),

            Group(MythrilOrOrichalcumChest, 1),
                        Group(AdamantiteOrTitaniumChest, 1),

            Item(ItemID.HallowedBar, 18),
            Item(ItemID.SoulofMight, 20)
        }),

            [ItemID.AncientHallowedGreaves] = new RecipeReplacement(new[]
            {
                Group(CobaltOrPalladiumLegs, 1),
                Group(MythrilOrOrichalcumLegs, 1),
                Group(AdamantiteOrTitaniumLegs, 1),

            Item(ItemID.HallowedBar, 15),
            Item(ItemID.SoulofFright, 20)
        }),
            [ItemID.AncientHallowedHood] = new RecipeReplacement(new IngredientSpec[][]
            {
                new []  {
                Group(CobaltOrPalladiumAnyHelmet, 1),
                 Group(MythrilOrOrichalcumAnyHelmet, 1),
                  Group(AdamantiteOrTitaniumAnyHelmet, 1),
                Item(ItemID.HallowedBar, 12),
                Item(ItemID.SoulofSight, 20)
                    },
                //new [] {
                //    Group(CobaltOrPalladiumBar, 12), 
                //    Group(MythrilOrOrichalcumBar, 12),
                //    Group(AdamantiteOrTitaniumBar, 12),
                //Item(ItemID.HallowedBar, 12),
                //Item(ItemID.SoulofSight, 20) }
        }),

            [ItemID.Flamethrower] = new RecipeReplacement(new IngredientSpec[][]
            {
                new [] {
            Group(CopperOrTinBar, 20),
            Group(RecipeGroupID.IronBar, 20),
            Item(ItemID.IllegalGunParts),
            Item(ItemID.CursedFlame, 35),
            Item(ItemID.SoulofFright, 20)
            },
                new []
                {
            Group(CopperOrTinBar, 20),
            Group(RecipeGroupID.IronBar, 20),
            Item(ItemID.IllegalGunParts),
            Item(ItemID.Ichor, 35),
            Item(ItemID.SoulofFright, 20)
                }

            }),

            [ItemID.MagicalHarp] = new RecipeReplacement(new[]
            {
                Item(ItemID.Harp),
                Item(ItemID.CrystalShard, 25),
                Item(ItemID.SoulofNight, 15),
                Group(GoldOrPlatinumBar, 15),
                Item(ItemID.SoulofSight, 20)
            }),

            [ItemID.FairyBell] = new RecipeReplacement(new[]
            {
                Item(ItemID.Bell),
                Item(ItemID.PixieDust, 80),
                Item(ItemID.SoulofLight, 15),
                Group(GoldOrPlatinumBar, 15),
                Item(ItemID.SoulofSight, 20)
            }),

            [ItemID.RainbowRod] = new RecipeReplacement(new[]
            {
                Item(ItemID.CrystalShard, 30),
                Item(ItemID.UnicornHorn, 4),
                Item(ItemID.PixieDust, 60),
                Item(ItemID.SoulofLight, 10),
                Item(ItemID.SoulofSight, 20)
            }),

            [ItemID.LightDisc] = new RecipeReplacement(new[]
            {
                Group(CobaltOrPalladiumBar, 20),
                Group(MythrilOrOrichalcumBar, 20),
                Item(ItemID.SoulofLight, 15),
                Item(ItemID.SoulofMight, 25)
            }),

            [ItemID.PickaxeAxe] = new RecipeReplacement(new[]
            {
                Group(CobaltOrPalladiumPickaxe),
                Group(MythrilOrOrichalcumPickaxe),
                Group(AdamantiteOrTitaniumPickaxe),
                Group(CobaltOrPalladiumWaraxe),
                Group(MythrilOrOrichalcumWaraxe),
                Group(AdamantiteOrTitaniumWaraxe),
                Item(ItemID.HallowedBar, 18),
                Item(ItemID.SoulofFright, 5),
                Item(ItemID.SoulofMight, 5),
                Item(ItemID.SoulofSight, 5)
            }),

            [ItemID.Drax] = new RecipeReplacement(new[]
            {
                Group(CobaltOrPalladiumDrill),
                Group(MythrilOrOrichalcumDrill),
                Group(AdamantiteOrTitaniumDrill),
                Group(CobaltOrPalladiumChainsaw),
                Group(MythrilOrOrichalcumChainsaw),
                Group(AdamantiteOrTitaniumChainsaw),
                Item(ItemID.HallowedBar, 18),
                Item(ItemID.SoulofFright, 5),
                Item(ItemID.SoulofMight, 5),
                Item(ItemID.SoulofSight, 5)
            }),

            [ItemID.HallowedRepeater] = new RecipeReplacement(new[]
            {
                Group(CobaltOrPalladiumRepeater),
                Group(MythrilOrOrichalcumRepeater),
                Group(AdamantiteOrTitaniumRepeater),
                Item(ItemID.HallowedBar, 12),
                Item(ItemID.SoulofSight, 20)
            }),

            [ItemID.Excalibur] = new RecipeReplacement(new[]
            {
                Group(CobaltOrPalladiumSword),
                Group(MythrilOrOrichalcumSword),
                Group(AdamantiteOrTitaniumSword),
                Item(ItemID.HallowedBar, 12),
                Item(ItemID.SoulofMight, 20)
            }),

            [ItemID.Gungnir] = new RecipeReplacement(new[]
            {
                Group(CobaltOrPalladiumSpear),
                Group(MythrilOrOrichalcumSpear),
                Group(AdamantiteOrTitaniumSpear),
                Item(ItemID.HallowedBar, 12),
                Item(ItemID.SoulofFright, 20)
            }),

            /** Custom **/
            [ItemID.HallowJoustingLance] = new RecipeReplacement(new[]
            {
                Item(ItemID.JoustingLance),
                Item(ItemID.HallowedBar, 12),
                Item(ItemID.SoulofMight, 20)
            }),

            /** Custom **/
            [ItemID.SwordWhip] = new RecipeReplacement(new[]
            {
                Item(ItemID.HallowedBar, 12),
                Item(ItemID.SoulofFright, 20)
            }),

            [ItemID.CrystalStorm] = new RecipeReplacement(new[]
            {
                Item(ItemID.SpellTome, 1),
                Item(ItemID.CrystalShard, 30),
                Item(ItemID.SoulofLight, 20)
            }),

            [ItemID.CursedFlames] = new RecipeReplacement(new[]
            {
                Item(ItemID.SpellTome, 1),
                Item(ItemID.CursedFlame, 30),
                Item(ItemID.SoulofNight, 20)
            }),

            [ItemID.GoldenShower] = new RecipeReplacement(new[]
            {
                Item(ItemID.SpellTome, 1),
                Item(ItemID.Ichor, 30),
                Item(ItemID.SoulofNight, 20)
            }),

            /** Custom **/
            [ItemID.MeteorStaff] = new RecipeReplacement(new[]
            {
                Item(ItemID.MeteoriteBar, 20),
                Item(ItemID.PixieDust, 20),
                Item(ItemID.SoulofLight, 15)
            }),

            /** Custom **/
            [ItemID.Chik] = new RecipeReplacement(new[]
            {
                Item(ItemID.WoodYoyo, 1),
                Item(ItemID.CrystalShard, 25),
                Item(ItemID.SoulofLight, 15)
            }),

            /** Custom **/
            [ItemID.OnyxBlaster] = new RecipeReplacement(new[]
            {
                Item(ItemID.Shotgun, 1),
                Item(ItemID.DarkShard, 2),
                Item(ItemID.SoulofNight, 25) /* lol */
            }),

            [ItemID.CoolWhip] = new RecipeReplacement(new[]
            {
                Item(ItemID.SoulofLight, 12),
                Item(ItemID.SoulofNight, 12),
                Item(ItemID.FrostCore, 1)
            }),

            [ItemID.NeptunesShell] = new RecipeReplacement(new[]
            {
                Item(ItemID.Coral, 15),
                Item(ItemID.SharkFin, 5),
                Item(ItemID.Goldfish, 15),
                Item(ItemID.SoulofLight, 5),
                Item(ItemID.SoulofNight, 5),
                Item(ItemID.SoulofFright, 20),

            },
                tileOverride: new int[] { TileID.MythrilAnvil, }
            ),



            /* End Hallow */
            #endregion

            /** Custom **/
            [ItemID.TerraBlade] = new RecipeReplacement(new[]
            {
                Item(ItemID.TrueNightsEdge, 1),
                Item(ItemID.Excalibur, 1),
                Item(ItemID.BrokenHeroSword, 3) /* instead of 1 */
            }),

            [ItemID.DaoofPow] = new RecipeReplacement(new[]
            {
                Item(ItemID.DarkShard, 1),
                Item(ItemID.LightShard, 1),
                Item(ItemID.SoulofNight, 10),
                Item(ItemID.SoulofLight, 10),
            }),

            [ItemID.MechanicalWorm] = new RecipeReplacement(new IngredientSpec[][]
            {
                new [] {
                Item(ItemID.RottenChunk, 6),
                Group(CopperOrTinBar, 5),
                Group(RecipeGroupID.IronBar, 5),
                Item(ItemID.SoulofNight, 7),
                },
                 new [] {
                Item(ItemID.Vertebrae, 6),
                Group(CopperOrTinBar, 5),
                Group(RecipeGroupID.IronBar, 5),
                Item(ItemID.SoulofNight, 7),
                },

            }),

            [ItemID.MechanicalEye] = new RecipeReplacement(new[]
            {
                Item(ItemID.Lens, 3),
                Group(CopperOrTinBar, 5),
                Group(RecipeGroupID.IronBar, 5),
                Item(ItemID.SoulofLight, 7),
            }),

            [ItemID.MechanicalSkull] = new RecipeReplacement(new[]
            {
                Item(ItemID.Bone, 30),
                Group(CopperOrTinBar, 5),
                Group(RecipeGroupID.IronBar, 5),
                Item(ItemID.SoulofLight, 5),
                Item(ItemID.SoulofNight, 5),
            }),

            /** Custom **/
            [ItemID.AvengerEmblem] = new RecipeReplacement(new[]
            {
              Item(ItemID.SorcererEmblem, 1),
              Item(ItemID.WarriorEmblem, 1),
              Item(ItemID.RangerEmblem, 1),
              Item(ItemID.SummonerEmblem, 1),
              Item(ItemID.SoulofMight, 5),
              Item(ItemID.SoulofSight, 5),
              Item(ItemID.SoulofFright, 5)
            }),

            /** Keymolds **/
            #region Keymolds
            [ItemID.JungleKey] = new RecipeReplacement(new[]
            {
                Item(ItemID.TempleKey, 1),
                Item(ModContent.ItemType<JungleKeyMold>(), 1),
                Item(ItemID.SoulofFright, 5),
                Item(ItemID.SoulofMight, 5),
                Item(ItemID.SoulofSight, 5),

            }),

            [ItemID.CorruptionKey] = new RecipeReplacement(new[]
            {
                Item(ItemID.TempleKey, 1),
                Item(ModContent.ItemType<CorruptionKeyMold>(), 1),
                Item(ItemID.SoulofFright, 5),
                Item(ItemID.SoulofMight, 5),
                Item(ItemID.SoulofSight, 5),

            }),

            [ItemID.CrimsonKey] = new RecipeReplacement(new[]
            {
                Item(ItemID.TempleKey, 1),
                Item(ModContent.ItemType<CrimsonKeyMold>(), 1),
                Item(ItemID.SoulofFright, 5),
                Item(ItemID.SoulofMight, 5),
                Item(ItemID.SoulofSight, 5),

            }),

            [ItemID.HallowedKey] = new RecipeReplacement(new[]
            {
                Item(ItemID.TempleKey, 1),
                Item(ModContent.ItemType<HallowedKeyMold>(), 1),
                Item(ItemID.SoulofFright, 5),
                Item(ItemID.SoulofMight, 5),
                Item(ItemID.SoulofSight, 5),

            }),

            [ItemID.FrozenKey] = new RecipeReplacement(new[]
            {
                Item(ItemID.TempleKey, 1),
                Item(ModContent.ItemType<FrozenKeyMold>(), 1),
                Item(ItemID.SoulofFright, 5),
                Item(ItemID.SoulofMight, 5),
                Item(ItemID.SoulofSight, 5),

            }),

            [ItemID.DungeonDesertKey] = new RecipeReplacement(new[]
            {
                Item(ItemID.TempleKey, 1),
                Item(ModContent.ItemType<DesertKeyMold>(), 1),
                Item(ItemID.SoulofFright, 5),
                Item(ItemID.SoulofMight, 5),
                Item(ItemID.SoulofSight, 5),

            }),
            /* End Keymolds */
            #endregion

            /* Watches & Goggles */
            #region Watches & Goggles
            /* As of 1.4.4.9 this is already how the recipies are, but 1.4.5 
             * will change this -- so in preparation for that the adjustments
             * are made now.
             */
            //[ItemID.Goggles] = new RecipeReplacement(new[]
            //{
            //    Item(ItemID.Lens, 2),
            //},
            //    tileOverride: new int[] { TileID.Tables, TileID.Chairs }
            //    ),

            //[ItemID.CopperWatch] = new RecipeReplacement(new[]
            //{
            //    Item(ItemID.CopperBar, 10),
            //    Item(ItemID.Chain, 1)
            //},
            //    tileOverride: new int[] { TileID.Tables, TileID.Chairs }
            //    ),

            //[ItemID.TinWatch] = new RecipeReplacement(new[]
            //{
            //    Item(ItemID.TinBar, 10),
            //    Item(ItemID.Chain, 1)
            //},
            //    tileOverride: new int[] { TileID.Tables, TileID.Chairs }
            //),

            //            [ItemID.SilverWatch] = new RecipeReplacement(new[]
            //{
            //    Item(ItemID.SilverBar, 10),
            //    Item(ItemID.Chain, 1)
            //},
            //    tileOverride: new int[] { TileID.Tables, TileID.Chairs }
            //),

            //            [ItemID.TungstenWatch] = new RecipeReplacement(new[]
            //{
            //    Item(ItemID.TungstenBar, 10),
            //    Item(ItemID.Chain, 1)
            //},
            //    tileOverride: new int[] { TileID.Tables, TileID.Chairs }
            //),

            //            [ItemID.GoldWatch] = new RecipeReplacement(new[]
            //{
            //    Item(ItemID.GoldBar, 10),
            //    Item(ItemID.Chain, 1)
            //},
            //    tileOverride: new int[] { TileID.Tables, TileID.Chairs }
            //),

            //            [ItemID.PlatinumWatch] = new RecipeReplacement(new[]
            //{
            //    Item(ItemID.PlatinumBar, 10),
            //    Item(ItemID.Chain, 1)
            //},
            //    tileOverride: new int[] { TileID.Tables, TileID.Chairs }
            //),

            #endregion

        };

        public override void PostAddRecipes()
        {
            var Replacements = BuildReplacements();
            var byResult = new Dictionary<int, List<Recipe>>();

#if DEBUG
            DateTime start = DateTime.UtcNow;
#endif 
            for (int i = 0; i < Recipe.numRecipes; i++)
            {
                Recipe recipe = Main.recipe[i];
                int type = recipe.createItem.type;

                if (!Replacements.ContainsKey(type))
                {
                    continue;
                }

                if (!byResult.TryGetValue(type, out var list))
                {
                    byResult[type] = list = new List<Recipe>();
                }

                list.Add(recipe);
            }

            foreach (var (itemType, replacement) in Replacements)
            {
                byResult.TryGetValue(itemType, out var matches);

                if (matches != null && matches.Count > 1)
                {
                    int replacementCount = replacement.Variants.Length;

                    if (replacementCount < matches.Count)
                    {
#if DEBUG
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.Error.WriteLine(
                            $"[oldschoolcrafting] Warning: {ItemID.Search.GetName(itemType)} " +
                            $"({itemType}): found {matches.Count} existing recipes but only {replacementCount} replacement(s) defined.");
                        Console.ResetColor();
#endif
                    }
                }

                Recipe anchor = ((matches != null && matches.Count > 0) ? matches[0] : null);

#if DEBUG
                if (anchor == null && replacement.TileOverride == null)
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Error.WriteLine(
                        $"[oldschoolcrafting] No original recipe found for {ItemID.Search.GetName(itemType)} " +
                        $"({itemType}); new recipe(s) will require no crafting station unless overridden.");
                    Console.ResetColor();
                }
#endif

                foreach (var ingredients in replacement.Variants)
                {
                    var newRecipe = Recipe.Create(itemType);

                    foreach (var spec in ingredients)
                    {
                        if (spec.IsGroup)
                        {
                            newRecipe.AddRecipeGroup(spec.Id, spec.Stack);
                        }
                        else
                        {
                            newRecipe.AddIngredient(spec.Id, spec.Stack);
                        }
                    }

                    if (replacement.TileOverride != null)
                    {
                        foreach (int tile in replacement.TileOverride)
                        {
                            newRecipe.AddTile(tile);
                        }
                    }
                    else if (anchor != null)
                    {
                        foreach (int tile in anchor.requiredTile)
                        {
                            newRecipe.AddTile(tile);
                        }
                    }

                    if (anchor != null)
                    {
                        newRecipe.SortBefore(anchor);
                    }

                    newRecipe.Register();
                }

                if (matches != null)
                {
                    foreach (Recipe old in matches)
                    {
                        old.DisableRecipe();
                    }
                }
            }

#if DEBUG
            DateTime end = DateTime.UtcNow;
            TimeSpan total = end - start;
            Console.Write($"Total MS: " + total.TotalMilliseconds);
            Console.Write("\n");
            /* all this logic takes about 1 to 10 ms per iteration; not worth parallelizing */
#endif
        }
    }


    #region Keymold Logic
    public class SpriteOverrides : ModSystem
    {
        public class KeyMoldDrops : GlobalNPC
        {
            public override void ModifyGlobalLoot(GlobalLoot globalLoot)
            {
                Replace(globalLoot, ItemID.JungleKey, ModContent.ItemType<JungleKeyMold>(), new Conditions.JungleKeyCondition());
                Replace(globalLoot, ItemID.CorruptionKey, ModContent.ItemType<CorruptionKeyMold>(), new Conditions.CorruptKeyCondition());
                Replace(globalLoot, ItemID.CrimsonKey, ModContent.ItemType<CrimsonKeyMold>(), new Conditions.CrimsonKeyCondition());
                Replace(globalLoot, ItemID.HallowedKey, ModContent.ItemType<HallowedKeyMold>(), new Conditions.HallowKeyCondition());
                Replace(globalLoot, ItemID.FrozenKey, ModContent.ItemType<FrozenKeyMold>(), new Conditions.FrozenKeyCondition());
                Replace(globalLoot, ItemID.DungeonDesertKey, ModContent.ItemType<DesertKeyMold>(), new Conditions.DesertKeyCondition());
            }

            private static void Replace(GlobalLoot globalLoot, int oldItemId, int newItemId, IItemDropRuleCondition condition)
            {
                globalLoot.RemoveWhere(rule => rule is ItemDropWithConditionRule dropRule && dropRule.itemId == oldItemId);
                
                /* rates were 1 in 4000 prior to 1.2.3, which made them 1 in 2500! */ 
                globalLoot.Add(new ItemDropWithConditionRule(newItemId, 4000, 1, 1, condition, 1));
            }
        }
    }
    #endregion

    #region Individual Enemy Drop Adjustments

    public class NPCLootChanges : GlobalNPC
    {
        /* runs once per npc load */
        public override void ModifyNPCLoot(NPC npc, NPCLoot npcLoot)
        {
            if (npc.type != NPCID.CreatureFromTheDeep)
            {
                return;
            }

            switch (npc.type)
            {
                case (NPCID.CreatureFromTheDeep):
                    {
                        /* Unfortunately since this item was craftable prior to 1.2, we can't just have it drop from enemies like fodder, so we must remove it */
                        npcLoot.RemoveWhere(rule => rule is DropBasedOnExpertMode wrapper && DropsItem(wrapper.ruleForNormalMode, ItemID.NeptunesShell));

                        /* potentially replacing it with something else so the enemy drops *something* at least */
 //                       npcLoot.Add(new DropBasedOnExpertMode(
 //                            ItemDropRule.Common(ItemID.Coral, 5, 1, 2),
 //                            ItemDropRule.Common(ItemID.Coral, 5, 2, 4)
 //));
                        break;
                    }

                default:
                    return;
            }


        }

        private static bool DropsItem(IItemDropRule rule, int itemId)
        {
            return rule is CommonDropWithRerolls drop && drop.itemId == itemId;
        }
    }

    #endregion
}