/***************************
        ** Mod Injection Methods **
        ***************************/

        /// <summary>The mod entry point, called after the mod is first loaded.</summary>
        /// <param name="helper">Provides simplified APIs for writing mods.</param>
        public override void Entry(IModHelper helper)
        {
            try
            {
                myMonitor = this.Monitor;
                myInput   = this.Helper.Input;
                str       = this.Helper.Translation;
                Config    = this.Helper.ReadConfig <ModConfig>();

                helper.Events.GameLoop.GameLaunched += this.OnGameLaunched;
                helper.Events.Input.ButtonPressed   += this.OnButtonPressed;
                helper.Events.Input.CursorMoved     += this.OnCursorMoved;

                Harmony harmonyInstance = new Harmony(this.ModManifest.UniqueID);

                patchPrefix(harmonyInstance, typeof(Farmer), nameof(Farmer.useTool),
                            typeof(ModEntry), nameof(ModEntry.Prefix_useTool));

                patchPostfix(harmonyInstance, typeof(Farmer), nameof(Farmer.useTool),
                             typeof(ModEntry), nameof(ModEntry.Postfix_useTool));

                patchPrefix(harmonyInstance, typeof(Character), nameof(Character.GetToolLocation),
                            typeof(ModEntry), nameof(ModEntry.Prefix_GetToolLocation),
                            new Type[] { typeof(Vector2), typeof(bool) });

                patchPrefix(harmonyInstance, typeof(Utility), nameof(Utility.isWithinTileWithLeeway),
                            typeof(ModEntry), nameof(ModEntry.Prefix_isWithinTileWithLeeway));

                patchPrefix(harmonyInstance, typeof(Game1), nameof(Game1.pressUseToolButton),
                            typeof(ModEntry), nameof(ModEntry.Prefix_pressUseToolButton));

                patchPrefix(harmonyInstance, typeof(Farmer), nameof(Farmer.draw),
                            typeof(ModEntry), nameof(ModEntry.Prefix_Farmer_draw),
                            new Type[] { typeof(SpriteBatch) });

                patchPostfix(harmonyInstance, typeof(Farmer), nameof(Farmer.draw),
                             typeof(ModEntry), nameof(ModEntry.Postfix_Farmer_draw),
                             new Type[] { typeof(SpriteBatch) });

                patchPrefix(harmonyInstance, typeof(SpriteBatch), nameof(SpriteBatch.Draw),
                            typeof(ModEntry), nameof(ModEntry.Prefix_SpriteBatch_Draw),
                            new Type[] { typeof(Texture2D), typeof(Vector2), typeof(Rectangle?), typeof(Color),
                                         typeof(float), typeof(Vector2), typeof(Vector2), typeof(SpriteEffects), typeof(float) });

                patchPrefix(harmonyInstance, typeof(Utility), nameof(Utility.playerCanPlaceItemHere),
                            typeof(ModEntry), nameof(ModEntry.Prefix_playerCanPlaceItemHere));

                patchPostfix(harmonyInstance, typeof(Utility), nameof(Utility.playerCanPlaceItemHere),
                             typeof(ModEntry), nameof(ModEntry.Postfix_playerCanPlaceItemHere));

                patchPrefix(harmonyInstance, typeof(Utility), nameof(Utility.withinRadiusOfPlayer),
                            typeof(ModEntry), nameof(ModEntry.Prefix_withinRadiusOfPlayer));

                if (helper.ModRegistry.IsLoaded("Thor.HoeWaterDirection"))
                {
                    patchPostfix(harmonyInstance, null, "",
                                 typeof(ModEntry), nameof(ModEntry.Postfix_HandleChangeDirectoryImpl),
                                 null, "Thor.Stardew.Mods.HoeWaterDirection.ModEntry:HandleChangeDirectoryImpl");
                }
            }
            catch (Exception e)
            {
                Log("Error in mod setup: " + e.Message + Environment.NewLine + e.StackTrace);
            }
        }
        /********************************
        ** Config Menu Initialization **
        ********************************/

        /// <summary>Initializes menu for Generic Mod Config Menu on game launch.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event data.</param>
        private void OnGameLaunched(object sender, GameLaunchedEventArgs e)
        {
            try
            {
                // Get Generic Mod Config Menu's API (if it's installed).
                var configMenu = Helper.ModRegistry.GetApi <IGenericModConfigMenuApi>("spacechase0.GenericModConfigMenu");
                if (configMenu is null)
                {
                    return;
                }

                // Register mod.
                configMenu.Register(mod: ModManifest, reset: () => Config = new ModConfig(), save: () => Helper.WriteConfig(Config));

                // Add options.
                configMenu.AddSectionTitle(mod: ModManifest, text: () => str.Get("headerRanges"));

                List <string> rangeList = new List <string>();
                rangeList.Add("1");
                rangeList.Add("-1");
                for (int i = 2; i <= 20; i++)
                {
                    rangeList.Add(i.ToString());
                }

                foreach (string subject in new string[] { "axe", "pickaxe", "hoe", "wateringCan", "seeds", "objects" })
                {
                    configMenu.AddTextOption(
                        mod: ModManifest,
                        name: () => str.Get("optionRangeName", new { subject = str.Get(subject + "ForRangeName") }),
                        tooltip: () => str.Get("optionRangeTooltip", new { subject = str.Get(subject + "ForRangeTooltip") }),
                        getValue: () =>
                    {
                        int value = 1;
                        switch (subject)
                        {
                        case "axe": value = Config.AxeRange; break;

                        case "pickaxe": value = Config.PickaxeRange; break;

                        case "hoe": value = Config.HoeRange; break;

                        case "wateringCan": value = Config.WateringCanRange; break;

                        case "seeds": value = Config.SeedRange; break;

                        case "objects": value = Config.ObjectPlaceRange; break;
                        }
                        return(rangeList[value == 1? 0    // Default
                                           : value < 0? 1 // Unlimited
                                           : value]);     // Extended
                    },
                        setValue: strValue =>
                    {
                        int value = 1;
                        if (strValue.Equals(rangeList[0]))
                        {
                            value = 1;
                        }
                        else if (strValue.Equals(rangeList[1]))
                        {
                            value = -1;
                        }
                        else
                        {
                            for (int i = 2; i <= 20; i++)
                            {
                                if (strValue.Equals(rangeList[i]))
                                {
                                    value = i;
                                    break;
                                }
                            }
                        }

                        switch (subject)
                        {
                        case "axe": Config.AxeRange = value; break;

                        case "pickaxe": Config.PickaxeRange = value; break;

                        case "hoe": Config.HoeRange = value; break;

                        case "wateringCan": Config.WateringCanRange = value; break;

                        case "seeds": Config.SeedRange = value; break;

                        case "objects": Config.ObjectPlaceRange = value; break;
                        }
                    },
                        allowedValues: rangeList.ToArray(),
                        formatAllowedValue: value =>
                    {
                        if (value.Equals("1"))
                        {
                            return(str.Get("rangeDefault"));
                        }
                        else if (value.Equals("-1"))
                        {
                            return(str.Get("rangeUnlimited"));
                        }
                        else
                        {
                            return(str.Get("rangeExtended", new { tiles = value }));
                        }
                    }
                        );
                }

                configMenu.AddSectionTitle(mod: ModManifest, text: () => str.Get("headerUseOnTile"));

                foreach (string tool in new string[] { "axe", "pickaxe", "hoe" })
                {
                    configMenu.AddBoolOption(
                        mod: ModManifest,
                        name: () => str.Get("optionSelfUsabilityName", new { tool = str.Get(tool + "ForUsabilityName") }),
                        tooltip: () => str.Get("optionSelfUsabilityTooltip", new { tool = str.Get(tool + "ForUsabilityTooltip") }),
                        getValue: () =>
                    {
                        switch (tool)
                        {
                        case "axe": return(Config.AxeUsableOnPlayerTile);

                        case "pickaxe": return(Config.PickaxeUsableOnPlayerTile);

                        case "hoe": return(Config.HoeUsableOnPlayerTile);
                        }
                        return(true);
                    },
                        setValue: value =>
                    {
                        switch (tool)
                        {
                        case "axe": Config.AxeUsableOnPlayerTile = value; break;

                        case "pickaxe": Config.PickaxeUsableOnPlayerTile = value; break;

                        case "hoe": Config.HoeUsableOnPlayerTile = value; break;
                        }
                    }
                        );
                }

                configMenu.AddSectionTitle(mod: ModManifest, text: () => str.Get("headerFaceClick"));

                configMenu.AddBoolOption(
                    mod: ModManifest,
                    name: () => str.Get("optionToolFaceClickName"),
                    tooltip: () => str.Get("optionToolFaceClickTooltip"),
                    getValue: () => Config.ToolAlwaysFaceClick,
                    setValue: value => Config.ToolAlwaysFaceClick = value
                    );

                configMenu.AddBoolOption(
                    mod: ModManifest,
                    name: () => str.Get("optionWeaponFaceClickName"),
                    tooltip: () => str.Get("optionWeaponFaceClickTooltip"),
                    getValue: () => Config.WeaponAlwaysFaceClick,
                    setValue: value => Config.WeaponAlwaysFaceClick = value
                    );

                configMenu.AddSectionTitle(mod: ModManifest, text: () => str.Get("headerMisc"));

                configMenu.AddTextOption(
                    mod: ModManifest,
                    name: () => str.Get("optionToolHitLocationName"),
                    tooltip: () => str.Get("optionToolHitLocationTooltip"),
                    getValue: () => Config.ToolHitLocationDisplay.ToString(),
                    setValue: value => Config.ToolHitLocationDisplay = int.Parse(value),
                    allowedValues: new string[] { "0", "1", "2" },
                    formatAllowedValue: value =>
                {
                    switch (value)
                    {
                    case "0": return(str.Get("locationLogicOriginal"));

                    case "1":
                    default: return(str.Get("locationLogicNew"));

                    case "2": return(str.Get("locationLogicCombined"));
                    }
                }
                    );

                configMenu.AddBoolOption(
                    mod: ModManifest,
                    name: () => str.Get("optionAllowRangedChargeName"),
                    tooltip: () => str.Get("optionAllowRangedChargeTooltip"),
                    getValue: () => Config.AllowRangedChargeEffects,
                    setValue: value => Config.AllowRangedChargeEffects = value
                    );

                configMenu.AddBoolOption(
                    mod: ModManifest,
                    name: () => str.Get("optionOnClickOnlyName"),
                    tooltip: () => str.Get("optionOnClickOnlyTooltip"),
                    getValue: () => Config.CustomRangeOnClickOnly,
                    setValue: value => Config.CustomRangeOnClickOnly = value
                    );
            }
            catch (Exception exception)
            {
                Log("Error setting up mod config menu (menu may not appear): " + exception.InnerException
                    + Environment.NewLine + exception.StackTrace);
            }
        }