﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine;
using TMPro;

public class Game : MonoBehaviour
{
    [SerializeField] GameObject borderTilePrefab;
    [SerializeField] GameObject grassTilePrefab;
    [SerializeField] GameObject waterTilePrefab;
    [SerializeField] GameObject plantPrefab;
    [SerializeField] GameObject bunnyPrefab;
    [SerializeField] GameObject bearPrefab;
    [SerializeField] GameObject foxPrefab;
    [SerializeField] GameObject LoseUI;
    [SerializeField] TMP_Text populationText;
    [SerializeField] TMP_Text haveToGiveText;
    [SerializeField] AudioClip plopClip;
    [SerializeField] Image plantFill;
    [SerializeField] Image bunnyFill;
    [SerializeField] Image foxFill;
    [SerializeField] Image bearFill;
    [SerializeField] int waterSizeControl;
    [SerializeField] int waterPatchAmount;
    [SerializeField] int worldHeight;
    [SerializeField] int worldWidth;
    [SerializeField] int initalPlants;
    [SerializeField] int initalBunnys;
    [SerializeField] int initalBears;
    [SerializeField] int initalFox;
    [SerializeField] int plantPopulation;
    [SerializeField] int bunnyPopulation;
    [SerializeField] int foxPopulation;
    [SerializeField] int bearPopulation;
    [SerializeField] int plantCount;
    [SerializeField] int bunnyCount;
    [SerializeField] int foxCount;
    [SerializeField] int bearCount;
    [SerializeField] bool inMenu;
    List<GameObject> grassTiles;
    List<GameObject> waterTiles;
    GameObject currentThing;
    AudioSource audioSource;

    #region Statics
    static Game instance;
    public static Game I
    {
        get
        {
            if (instance == null)
            {
                var gob = new GameObject("World");
                gob.AddComponent<Game>();
            }
            return instance;
        }
    }
    #endregion

    void Awake()
    {
        if (instance != null)
        {
            Debug.LogWarning($"There is another instance of {GetType().Name} already in the scene. Deleting this one!");
            DestroyImmediate(gameObject);
        }
        else
        {
            instance = this;
            //DontDestroyOnLoad(gameObject);
        }

        if (!inMenu)
        {
            audioSource = GetComponent<AudioSource>();
        }

        grassTiles = new List<GameObject>();
        waterTiles = new List<GameObject>();
        currentThing = plantPrefab;
        GenerateTiles();
        AddInitialLife();

        UpdateToGiveText();
    }

    void GenerateTiles()
    {
        for (int x = -worldWidth / 2; x < worldWidth / 2; x++)
        {
            for (int y = -worldHeight / 2; y < worldHeight / 2; y++)
            {
                var newtile = Instantiate(grassTilePrefab, new Vector3(x, y, 0), Quaternion.identity, this.transform);
                grassTiles.Add(newtile);
            }
        }

        for (int k = -worldWidth / 2 - 1; k < worldWidth / 2 + 1; k++)
        {
            Instantiate(borderTilePrefab, new Vector2(k, -worldHeight / 2 - 1), Quaternion.identity, this.transform);
            Instantiate(borderTilePrefab, new Vector2(k, worldHeight / 2), Quaternion.identity, this.transform);
        }

        for (int j = -worldHeight / 2; j < worldHeight / 2; j++)
        {
            Instantiate(borderTilePrefab, new Vector2(-worldWidth / 2 - 1, j), Quaternion.identity, this.transform);
            Instantiate(borderTilePrefab, new Vector2(worldWidth / 2, j), Quaternion.identity, this.transform);
        }

        for (int i = 0; i < waterPatchAmount; i++)
        {
            GenerateWaterPatches();
        }
    }

    void GenerateWaterPatches()
    {
        //rx1 is farther to the left
        var rX1 = Random.Range(-worldWidth / 2, worldWidth / 2 + 1);
        //rx2 is farther to the right
        var rX2 = Random.Range(rX1, waterSizeControl);

        //ry1 is farther down
        var rY1 = Random.Range(-worldHeight / 2, worldHeight / 2 + 1);
        //ry1 is farther up
        var rY2 = Random.Range(rY1, waterSizeControl);

        for (int i = grassTiles.Count - 1; i >= 0; i--)
        {
            if (grassTiles[i].transform.position.x >= rX1 && grassTiles[i].transform.position.x <= rX2 && grassTiles[i].transform.position.y >= rY1 && grassTiles[i].transform.position.y <= rY2)
            {
                if ((grassTiles[i].transform.position.x == rX1 && (grassTiles[i].transform.position.y == rY1 || grassTiles[i].transform.position.y == rY2)) || (grassTiles[i].transform.position.x == rX2 && (grassTiles[i].transform.position.y == rY1 || grassTiles[i].transform.position.y == rY2)))
                {
                    //if the width and the height are greater than 4
                    continue;
                }
                if ((grassTiles[i].transform.position.x == rX1 + 1 && (grassTiles[i].transform.position.y == rY2 || grassTiles[i].transform.position.y == rY1)) || (grassTiles[i].transform.position.x == rX2 - 1 && (grassTiles[i].transform.position.y == rY2 || grassTiles[i].transform.position.y == rY1)) || (grassTiles[i].transform.position.y == rY2 - 1 && (grassTiles[i].transform.position.x == rX1 || grassTiles[i].transform.position.x == rX2)) || (grassTiles[i].transform.position.y == rY1 + 1 && (grassTiles[i].transform.position.x == rX1 || grassTiles[i].transform.position.x == rX2)))
                {
                    continue;
                }
                else
                {
                    ConvertTileToWater(grassTiles[i], rX1, rX2, rY1, rY2);
                }
            }
        }
    }

    void ConvertTileToWater(GameObject tile, int x1, int x2, int y1, int y2)
    {
        var newWaterTile = Instantiate(waterTilePrefab, tile.transform.position, Quaternion.identity, this.transform);

        //if (tile.transform.position.x != x1 && tile.transform.position.y != y1 && tile.transform.position.x != x2 && tile.transform.position.y != y2)
        //{
        //    newWaterTile.GetComponent<BoxCollider2D>().enabled = false;
        //}

        waterTiles.Add(newWaterTile);
        grassTiles.Remove(tile);
        Destroy(tile);
    }

    void AddInitialLife()
    {
        for (int i = 0; i < initalPlants; i++)
        {
            PlaceObject(plantPrefab, false);
        }
        for (int i = 0; i < initalBunnys; i++)
        {
            PlaceObject(bunnyPrefab, false);
        }
        for (int i = 0; i < initalFox; i++)
        {
            PlaceObject(foxPrefab, false);
        }
        for (int i = 0; i < initalBears; i++)
        {
            PlaceObject(bearPrefab, false);
        }
    }

    private void Update()
    {
        if (inMenu) { return; }
        CheckForLoss();
        if (lost) { return; }
        Inputs();
        GenerateNewThings();
    }

    bool lost;

    void CheckForLoss()
    {
        if (plantPopulation == 0 || bunnyPopulation == 0 || foxPopulation == 0 || bearPopulation == 0)
        {
            lost = true;
        }

        if (lost)
        {
            LoseUI.SetActive(true);
        }
    }

    float plantTimer;
    float bunnyTimer;
    float foxTimer;
    float bearTimer;

    void GenerateNewThings()
    {
        plantFill.fillAmount = plantTimer / 5;
        bunnyFill.fillAmount = bunnyTimer / 10;
        foxFill.fillAmount = foxTimer / 20;
        bearFill.fillAmount = bearTimer / 30;

        if (plantTimer > 5)
        {
            EditCount(1, true, "Plant");
            plantTimer = 0;
        }
        if (bunnyTimer > 10)
        {
            EditCount(1, true, "Bunny");
            bunnyTimer = 0;
        }
        if (foxTimer > 20)
        {
            EditCount(1, true, "Fox");
            foxTimer = 0;
        }
        if (bearTimer > 30)
        {
            EditCount(1, true, "Bear");
            bearTimer = 0;
        }

        plantTimer += Time.deltaTime;
        bunnyTimer += Time.deltaTime;
        foxTimer += Time.deltaTime;
        bearTimer += Time.deltaTime;
    }

    void Inputs()
    {
        if (Input.GetKey(KeyCode.Alpha1))
        {
            currentThing = plantPrefab;
        }
        else if (Input.GetKey(KeyCode.Alpha2))
        {
            currentThing = bunnyPrefab;
        }
        else if (Input.GetKey(KeyCode.Alpha3))
        {
            currentThing = foxPrefab;
        }
        else if (Input.GetKey(KeyCode.Alpha4))
        {
            currentThing = bearPrefab;
        }
        else if (Input.GetKeyDown(KeyCode.R))
        {
            SceneManager.LoadScene(2);
        }

        if (Input.GetMouseButtonDown(0))
        {
            if (CanPutDown(currentThing))
            {
                PlaceObject(currentThing, true);
            }
        }
    }

    void PlaceObject(GameObject obj, bool click)
    {
        var rand = Random.Range(0, grassTiles.Count);

        if (click && inMenu) { return; }

        if (click)
        {
            if (!CanPlaceThere()) { return; }
            obj = Instantiate(obj, Camera.main.ScreenToWorldPoint(Input.mousePosition) + new Vector3(0, 0, 10), Quaternion.identity, this.transform);
            CheckForOverlaps(obj);
        }
        else
        {
            Instantiate(obj, grassTiles[rand].transform.position, Quaternion.identity, this.transform);

            if (obj.tag == "Plant")
            {
                AddOrSubPop(obj.tag, 1);
            }
        }

    }

    bool CanPlaceThere()
    {
        RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), transform.forward, Mathf.Infinity);

        if (hit)
        {
            return true;
        }

        return false;
    }

    void CheckForOverlaps(GameObject objec)
    {
        ContactFilter2D contactFilter = new ContactFilter2D();
        Collider2D[] colliders = new Collider2D[10];

        contactFilter.useLayerMask = true;
        contactFilter.layerMask = LayerMask.GetMask("Water", "Border");

        int colliderCount = objec.GetComponent<CapsuleCollider2D>().OverlapCollider(contactFilter, colliders);

        if (colliderCount > 0)
        {
            if (objec.tag == "Bunny" || objec.tag == "Fox" || objec.tag == "Bear")
            {
                objec.GetComponent<Animal>().Die(false);
            }
            else
            {
                Destroy(objec);
            }
        }
        else
        {
            if (objec.tag == "Plant")
            {
                AddOrSubPop(objec.tag, 1);
            }

            EditCount(-1, false);

            UpdatePopulationText();

            audioSource.PlayOneShot(plopClip);
        }
    }

    void EditCount(int amount, bool naturalGeneration, string tag = null)
    {
        if (naturalGeneration)
        {
            if (tag == "Plant")
            {
                plantCount += amount;
            }
            if (tag == "Bunny")
            {
                bunnyCount += amount;
            }
            if (tag == "Fox")
            {
                foxCount += amount;
            }
            if (tag == "Bear")
            {
                bearCount += amount;
            }
        }
        else
        {
            if (currentThing == plantPrefab)
            {
                plantCount += amount;
            }
            else if (currentThing == foxPrefab)
            {
                foxCount += amount;
            }
            else if (currentThing == bearPrefab)
            {
                bearCount += amount;
            }
            else if (currentThing == bunnyPrefab)
            {
                bunnyCount += amount;
            }
        }

        UpdateToGiveText();
    }

    bool CanPutDown(GameObject prefab)
    {
        if (prefab == plantPrefab)
        {
            if (plantCount > 0) { return true; }
            else { return false; }
        }
        else if (prefab == bunnyPrefab)
        {
            if (bunnyCount > 0) { return true; }
            else { return false; }
        }
        else if (prefab == foxPrefab)
        {
            if (foxCount > 0) { return true; }
            else { return false; }
        }
        else if (prefab == bearPrefab)
        {
            if (bearCount > 0) { return true; }
            else { return false; }
        }
        return false;
    }

    void UpdateToGiveText()
    {
        if (inMenu) { return; }

        haveToGiveText.text = $"<color=red>You Have:</color>\n<color=blue>Plant:</color> <color=purple>{plantCount}</color>\n<color=blue>Bunny:</color> <color=purple>{bunnyCount}</color>\n<color=blue>Fox:</color> <color=purple>{foxCount}</color>\n<color=blue>Bear:</color> <color=purple>{bearCount}</color>\n";
    }

    void UpdatePopulationText()
    {
        if (inMenu) { return; }

        populationText.text = $"<color=red>Population:</color>\n<color=blue>Plant:</color> <color=purple>{plantPopulation}</color>\n<color=blue>Bunny:</color> <color=purple>{bunnyPopulation}</color>\n<color=blue>Fox:</color> <color=purple>{foxPopulation}</color>\n<color=blue>Bear:</color> <color=purple>{bearPopulation}</color>\n";
    }

    public void AddOrSubPop(string yourTag, int amount)
    {
        if (inMenu) { return; }

        if (yourTag == "Bunny")
        {
            bunnyPopulation += amount;
        }
        else if (yourTag == "Fox")
        {
            foxPopulation += amount;
        }
        else if (yourTag == "Plant")
        {
            plantPopulation += amount;
        }
        else if (yourTag == "Bear")
        {
            bearPopulation += amount;
        }

        UpdatePopulationText();
    }
}
