using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using TMPro;
using UnityEngine;

public class Level : MonoBehaviour
{

    public GameObject Player;
    public GameObject ResourceSpawner;
    public GameObject TextMoney;
    public GameObject Inventory;
    public GameObject ItemContainer;
    
    public AudioSource AudioPickUp;
    public AudioSource AudioBuild;
    public AudioSource AudioFail;

    public List<GameObject> TileCandidates;
    public List<GameObject> BuildingTemplates;
    public List<Sprite> ItemTextures;
    public GameObject ItemTemplate;
    public GameObject MonolithTemplate;
    public int Money;
    public int BuyNewLandCost;
    public float BuyNewLandCostFactor;
    public float BuyNewLandCostFactorIncrease;

    [HideInInspector]
    public TileMatrix Matrix = new();

    [HideInInspector]
    public List<GameObject> Items = new();

    void Start()
    {
        Matrix.Initialize(this, transform.Find("Start Tile"));
    }

    void Update()
    {
        Matrix.Update();
        var textMoneyGui = TextMoney.GetComponent<TextMeshProUGUI>();
        textMoneyGui.text = Money.ToString(CultureInfo.InvariantCulture);
        for (var i = Items.Count - 1; i >= 0; i--)
            if (Items[i].transform.position.y <= -100f)
            {
                DestroyItem(i);
            }
            else if (!Items[i].GetComponent<ItemBehavior>().IsPickUpBlocked
                && (Items[i].transform.position - Player.transform.position).magnitude <= Global.PICK_UP_RANGE)
            {
                AudioPickUp.Play();
                PickUp(Items[i]);
                DestroyItem(i);
            }
    }

    private void DestroyItem(int index)
    {
        Destroy(Items[index]);
        Items.RemoveAt(index);
    }

    public GameObject InstantiateTile(Index index, int candidateID)
    {
        return Instantiate(TileCandidates[candidateID], new Vector3(index.X * 20, -10, index.Y * 20), Quaternion.identity, transform);
    }

    public void NotifyDestruction(GameObject node, int resourceID, int upgradeLevel)
    {
        var count = 1;
        for (var i = 0; i < upgradeLevel; i++)
            if (Global.Rng.NextDouble() <= 0.1d)
                count++;
        for (var i = 0; i < count; i++)
            DropResources(resourceID, node);
        Matrix.Remove(node);
    }

    private void DropResources(int resourceID, GameObject obj)
    {
        if (resourceID < 0)
            return;

        if (resourceID == Global.RESOURCE_STONE)
        {
            PerformResourceDrop(Global.ITEM_STONE, obj);
        }
        else if (resourceID == Global.RESOURCE_ORE_COPPER)
        {
            PerformResourceDrop(Global.ITEM_STONE, obj);
            PerformResourceDrop(Global.ITEM_COPPER_ORE, obj);

        }
        else if (resourceID == Global.RESOURCE_ORE_IRON)
        {
            PerformResourceDrop(Global.ITEM_STONE, obj);
            PerformResourceDrop(Global.ITEM_IRON_ORE, obj);

        }
        else if (resourceID == Global.RESOURCE_ORE_GOLD)
        {
            PerformResourceDrop(Global.ITEM_STONE, obj);
            PerformResourceDrop(Global.ITEM_GOLD_ORE, obj);

        }
        else if (resourceID == Global.RESOURCE_ORE_TITANIUM)
        {
            PerformResourceDrop(Global.ITEM_STONE, obj);
            PerformResourceDrop(Global.ITEM_TITANIUM_ORE, obj);

        }
        else if (resourceID == Global.RESOURCE_TREE_BIRCH)
        {
            PerformResourceDrop(Global.ITEM_WOOD, obj);
        }
        else if (resourceID == Global.RESOURCE_BEET)
        {
            PerformResourceDrop(Global.ITEM_BEET, obj);
        }
    }

    private void PerformResourceDrop(int itemID, GameObject obj)
    {
        var direction = Camera.main.transform.forward;
        direction.y = 2f;
        direction = direction.normalized;
        direction = Quaternion.Euler(0, Random.Range(-45, 45), 0) * direction;
        SpawnItem(itemID, obj.transform.position, direction * 6f);
    }

    public bool CanUpgradeTile(Index tileIndex)
    {
        var tileObject = Matrix.Get(tileIndex.Identifier);
        if (tileObject == null)
            return false;
        return (tileObject.Element.GetComponent<Tile>().Upgrade + 1 < Global.UPGRADE_COST.Length);
    }

    public bool TryUpgradeTile(Index tileIndex)
    {
        var tile = Matrix.Get(tileIndex.Identifier);
        if (tile == null)
            return false;
        var targetUpgrade = tile.Upgrade + 1;
        if (targetUpgrade >= Global.UPGRADE_COST.Length)
            return false;
        var cost = Global.GetUpgradeCost(currentUpgrade: tile.Upgrade);
        if (cost > Money)
            return false;
        Money -= cost;
        tile.Upgrade++;
        return true;
    }

    public bool TryBuyNewTile()
    {
        var cost = BuyNewLandCost;
        if (Money >= cost)
        {
            Money -= cost;
            BuyNewLandCost = BuyNewLandCost + (int)Mathf.Floor(BuyNewLandCost * BuyNewLandCostFactor);
            BuyNewLandCostFactor *= BuyNewLandCostFactorIncrease;
            Matrix.AddRandomTile();
            return true;
        }
        return false;
    }

    public void Build(int buildingID)
    {
        var inventory = Inventory.GetComponent<Inventory>();
        var costs = GetCost(buildingID);
        var canPay = true;
        if (!Global.DEBUG_IGNORE_BUILDING_COST)
            for (var itemID = 0; itemID < Global.ITEM_COUNT; itemID++)
                if (costs[itemID] > inventory.ItemCounts[itemID])
                    canPay = false;
        if (canPay)
        {
            if (!Global.DEBUG_IGNORE_BUILDING_COST)
                for (var itemID = 0; itemID < Global.ITEM_COUNT; itemID++)
                    inventory.ItemCounts[itemID] -= costs[itemID];
            var pos = Player.transform.position;
            var tileIndex = Global.PositionToIndex(pos);
            var tile = Matrix.Get(tileIndex.Identifier);
            var direction = Camera.main.transform.forward;
            direction.y = 0;
            var building = Instantiate(BuildingTemplates[buildingID], pos, Quaternion.LookRotation(direction), tile.Element);
            building.GetComponent<Production>().Level = gameObject;
            building.GetComponent<Production>().Inventory = Inventory;
            tile.Build(buildingID, building);
            AudioBuild.Play();
        }
        else
        {
            AudioFail.Play();
        }
    }

    public int[] GetCost(int buildingID)
    {
        var costs = new int[Global.ITEM_COUNT];
        if (buildingID == Global.BUILDING_CHARCOAL_KILN)
            costs[Global.ITEM_WOOD] = 10;
        else if (buildingID == Global.BUILDING_FURNACE)
        {
            costs[Global.ITEM_STONE] = 20;
            costs[Global.ITEM_COAL] = 5;
        }
        return costs;
    }

    public void SpawnItem(int itemID, float pickUpBlockOverwrite = -1)
    {
        SpawnItem(itemID, Player.transform.position, Vector3.zero, pickUpBlockOverwrite);
    }

    public void SpawnItem(int itemID, Vector3 position, Vector3 velocity, float pickUpBlockOverwrite = -1)
    {
        var pos = position + new Vector3(0, 0.6f, 0);
        var item = Instantiate(ItemTemplate, pos, Quaternion.Euler(0, Random.Range(0, 360), 0), ItemContainer.transform);
        item.GetComponent<SpriteRenderer>().sprite = ItemTextures[itemID];
        var itemBehavior = item.GetComponent<ItemBehavior>();
        itemBehavior.ItemID = itemID;
        if (pickUpBlockOverwrite >= 0)
            itemBehavior.PickUpBlockCounter = pickUpBlockOverwrite;
        item.GetComponent<Rigidbody>().velocity = velocity;
        Items.Add(item);
    }

    public void PickUp(GameObject item)
    {
        var behavior = item.GetComponent<ItemBehavior>();
        Inventory.GetComponent<Inventory>().Add(behavior.ItemID, 1);
    }

}
