using System;
using System.Collections.Generic;
using HarmonyLib;
using Snapshots;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace BlacksmithMaster {
    [HarmonyPatch]
    public class UISnapshotPanelBuyAll {
        public static UISnapshotPanel panel;
        public static Traverse panelTraverse;
        public static SnapshotLiveData selectedData;

        [HarmonyPostfix]
        [HarmonyPatch(typeof(UISnapshotPanel), "OnPool")]
        public static void PostfixCreate(UISnapshotPanel __instance) {
            if (Main.debugBuyAll.Value)
                Console.WriteLine("UISnapshotPanel.OnPool: {0}", __instance);
            panel = __instance;
            panelTraverse = Traverse.Create(__instance);
            var placeButton = panelTraverse.Field("m_PlaceButton").GetValue<Button>();
            placeButton.gameObject.AddComponent<RightClickHandler>();

            var swapButton = panelTraverse.Field("m_SwapButton").GetValue<Button>();
            swapButton.gameObject.AddComponent<RightClickHandlerSwap>();
        }

        [HarmonyPostfix]
        [HarmonyPatch(typeof(UISnapshotPanel), "OnSelectedChanged")]
        public static void PostfixCreate(UISnapshotPanel __instance, ref SnapshotLiveData selectedData) {
            if (Main.debugBuyAll.Value)
                Console.WriteLine("UISnapshotPanel.OnSelectedChanged: {0}", __instance);
            UISnapshotPanelBuyAll.selectedData = selectedData;
        }
    }

    public abstract class BaseRightClickHandler : MonoBehaviour, IPointerClickHandler {
        protected static Traverse m_TechAvailLookupTraverse;
        protected static Dictionary<Snapshot, TechDataAvailValidation> m_TechAvailLookup;

        protected virtual void Awake() {
            var trav = Traverse.Create(Singleton.Manager<ManSnapshots>.inst);
            m_TechAvailLookupTraverse = trav.Field("m_TechAvailLookup");
            m_TechAvailLookup = m_TechAvailLookupTraverse.GetValue<Dictionary<Snapshot, TechDataAvailValidation>>();
        }

        protected Dictionary<BlockTypes, int> CalculateMissingBlocks(
            Dictionary<BlockTypes, TechDataAvailValidation.BlockTypeAvailability> blockAvailability, bool isSpawning) {
            Dictionary<BlockTypes, int> missing = new Dictionary<BlockTypes, int>();

            foreach (var kvp in blockAvailability) {
                int numMissing;
                if (isSpawning)
                    numMissing = kvp.Value.numRequired - kvp.Value.numInInventory;
                else
                    numMissing = kvp.Value.numRequired - kvp.Value.numInInventory - kvp.Value.numOnPlayerTech;
            }

            if (missing.Count == 0)
                foreach (var kvp in blockAvailability) missing.Add(kvp.Key, kvp.Value.numRequired);
            return missing;
        }

        protected int CalculateTotalCost(Dictionary<BlockTypes, int> missingBlocks) {
            int totalCost = 0;
            RecipeManager recipeManager = Singleton.Manager<RecipeManager>.inst;

            foreach (var kvp in missingBlocks) {
                int cost = recipeManager.GetBlockBuyPrice(kvp.Key) * kvp.Value;
                totalCost += cost;

                if (Main.debugBuyAll.Value)
                    Console.WriteLine("{0} of {1} would cost {2}, total now {3}", kvp.Value, kvp.Key, cost, totalCost);
            }
            return totalCost;
        }

        protected bool TryPurchaseBlocks(Dictionary<BlockTypes, int> missingBlocks, int totalCost) {
            ManPlayer player = Singleton.Manager<ManPlayer>.inst;
            if (player.GetCurrentMoney() < totalCost) {
                if (Main.debugBuyAll.Value)
                    Console.WriteLine("Not enough money, have {0} but need {1}, nothing to do",
                                      player.GetCurrentMoney(), totalCost);
                return false;
            }

            player.PayMoney(totalCost);
            foreach (var kvp in missingBlocks) {
                if (Main.debugBuyAll.Value)
                    Console.WriteLine("Buying {0} of {1}", kvp.Value, kvp.Key);
                player.PlayerInventory.HostAddItem(kvp.Key, kvp.Value);
            }
            return true;
        }

        protected bool ProcessPurchase(
            Dictionary<BlockTypes, TechDataAvailValidation.BlockTypeAvailability> blockAvailability, bool isSpawning) {
            try {
                if (blockAvailability == null) {
                    if (Main.debugBuyAll.Value)
                        Console.WriteLine("Block availability is null (wtf?), nothing to do");
                    return false;
                }

                var missingBlocks = CalculateMissingBlocks(blockAvailability, isSpawning);
                int totalCost = CalculateTotalCost(missingBlocks);

                if (totalCost > 0) {
                    if (Main.debugBuyAll.Value)
                        Console.WriteLine("Total cost: {0}", totalCost);
                    return TryPurchaseBlocks(missingBlocks, totalCost);
                }

                if (Main.debugBuyAll.Value)
                    Console.WriteLine("No blocks missing or no cost calculated");
                return false;
            } catch (Exception e) {
                if (Main.debugBuyAll.Value)
                    Console.WriteLine("Error during purchase processing: {0}", e);
                return false;
            }
        }

        protected Dictionary<BlockTypes, TechDataAvailValidation.BlockTypeAvailability> GetCurrentBlockAvailability() {
            SnapshotLiveData selectedSnapshotData = UISnapshotPanelBuyAll.selectedData;
            Snapshot selectedSnapshot = selectedSnapshotData.m_Snapshot;
            if (selectedSnapshot == null) {
                if (Main.debugBuyAll.Value)
                    Console.WriteLine("Selected snapshot is null wtf??: {0}", gameObject.name);
                return null;
            }

            TechDataAvailValidation techDataAvail;
            if (!m_TechAvailLookup.TryGetValue(selectedSnapshot, out techDataAvail)) {
                if (Main.debugBuyAll.Value)
                    Console.WriteLine("Failed to find TechDataAvailValidation for snapshot: {0}", selectedSnapshot);
                return null;
            }

            var trav = Traverse.Create(techDataAvail);
            var mBlockAvailabilityField = trav.Field("m_BlockAvailability");
            var mBlockAvailability =
                mBlockAvailabilityField
                    .GetValue<Dictionary<BlockTypes, TechDataAvailValidation.BlockTypeAvailability>>();

            return mBlockAvailability;
        }

        public abstract void OnPointerClick(PointerEventData eventData);
    }

    public class RightClickHandler : BaseRightClickHandler {
        public override void OnPointerClick(PointerEventData eventData) {
            if (Main.debugBuyAll.Value)
                Console.WriteLine("UISnapshotPanel.OnPointerClick: {0} {1}", gameObject.name, eventData.button);
            try {
                if (eventData.button == PointerEventData.InputButton.Right)
                    ProcessPurchase(GetCurrentBlockAvailability(), true);
            } catch (Exception e) {
                if (Main.debugBuyAll.Value)
                    Console.WriteLine("Shit exploded fml: {0}", e);
            }
        }
    }

    public class RightClickHandlerSwap : BaseRightClickHandler {
        public override void OnPointerClick(PointerEventData eventData) {
            if (Main.debugBuyAll.Value)
                Console.WriteLine("UISnapshotPanel.OnPointerClick: {0} {1}", gameObject.name, eventData.button);
            try {
                if (eventData.button == PointerEventData.InputButton.Right)
                    ProcessPurchase(GetCurrentBlockAvailability(), false);
            } catch (Exception e) {
                if (Main.debugBuyAll.Value)
                    Console.WriteLine("Shit exploded fml: {0}", e);
            }
        }
    }
}
