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

public class Animal : MonoBehaviour
{
    enum State
    {
        RunningFromPredator,
        SearchingForNeeds,
        ConsumingNeed,
        ChasingNeed,
        Wandering,
        Labor
    }

    [SerializeField] GameObject reproductionBarPrefab;
    [SerializeField] GameObject actionTextPrefab;
    [SerializeField] GameObject hungerBarPrefab;
    [SerializeField] GameObject thirstBarPrefab;
    [SerializeField] GameObject gravePrefab;
    [SerializeField] string[] tagsForFood;
    GameObject myReproductionBarFill;
    [SerializeField] AudioClip eatSound;
    [SerializeField] AudioClip dieSound;
    [SerializeField] AudioClip clickSound;
    [SerializeField] float hopForce;
    [SerializeField] Canvas canvas;
    GameObject myActionTextObject;
    [SerializeField] string myName;
    GameObject myReproductionBar;
    GameObject myHungerBarFill;
    GameObject myThirstBarFill;
    bool needIsReproduction;
    public GameObject predator;
    GameObject myHungerBar;
    GameObject myThirstBar;
    float reproductiveUrge;
    TMP_Text myActionText;
    AudioSource aSource;
    State currentState;
    GameObject target;
    bool needIsWater;
    bool needIsFood;
    float maxGrowth;
    Rigidbody2D rb;
    float hunger;
    float thirst;
    string sex;

    private void Awake()
    {
        aSource = GetComponent<AudioSource>();

        Game.I.AddOrSubPop(gameObject.tag, 1);

        transform.rotation = Quaternion.Euler(0, 0, RandomFloat(0, 360));

        canvas = FindObjectOfType<Canvas>();

        myActionTextObject = Instantiate(actionTextPrefab, transform.position + new Vector3(0, 1.25f, 0), Quaternion.identity, canvas.transform);
        myActionText = myActionTextObject.GetComponent<TMP_Text>();

        var ran = Random.Range(0, 2);

        if (ran == 0)
        {
            sex = "Male";
            GetComponent<SpriteRenderer>().color = Color.gray;
        }
        else
        {
            sex = "Female";
        }

        SetupBars();

        currentState = new State();
        rb = GetComponent<Rigidbody2D>();

        maxGrowth = 1 + Random.Range(-.2f, .2f);

        Logic(true);
    }

    void SetupBars()
    {
        myHungerBar = Instantiate(hungerBarPrefab, transform.position + new Vector3(0, .5f, 0), Quaternion.identity, canvas.transform);
        myHungerBarFill = myHungerBar.transform.GetChild(0).gameObject;

        myThirstBar = Instantiate(thirstBarPrefab, transform.position + new Vector3(0, .75f, 0), Quaternion.identity, canvas.transform);
        myThirstBarFill = myThirstBar.transform.GetChild(0).gameObject;

        myReproductionBar = Instantiate(reproductionBarPrefab, transform.position + new Vector3(0, 1f, 0), Quaternion.identity, canvas.transform);
        myReproductionBarFill = myReproductionBar.transform.GetChild(0).gameObject;
    }

    void DisplayHungerBar()
    {
        myHungerBarFill.GetComponent<Image>().fillAmount = hunger / 100;
        myHungerBar.transform.position = transform.position + new Vector3(0, .5f, 0);
    }

    void DisplayThirstBar()
    {
        myThirstBarFill.GetComponent<Image>().fillAmount = thirst / 100;
        myThirstBar.transform.position = transform.position + new Vector3(0, .75f, 0);
    }

    void DisplayReproductionBar()
    {
        myReproductionBarFill.GetComponent<Image>().fillAmount = reproductiveUrge / 100;
        myReproductionBar.transform.position = transform.position + new Vector3(0, 1f, 0);
    }

    void DoBars()
    {
        DisplayHungerBar();
        DisplayThirstBar();
        DisplayReproductionBar();
    }

    void ChangeState(State newState)
    {
        currentState = newState;

        string need = "";
        if (needIsFood) { need = "food"; }
        else if (needIsWater) { need = "water"; }
        else if (needIsReproduction) { need = "a mate"; }

        switch (newState)
        {

            case State.ChasingNeed:
                myActionText.text = $"Getting {need}";
                break;

            case State.ConsumingNeed:
                myActionText.text = $"Consuming {need}";
                break;

            case State.Labor:
                myActionText.text = "In labor";
                break;

            case State.Wandering:
                myActionText.text = "Wandering";
                break;

            case State.RunningFromPredator:
                myActionText.text = "Running from predator";
                break;

            case State.SearchingForNeeds:
                myActionText.text = $"Searching for {need}";
                break;
        }
    }

    void UpdateActionTextPosition()
    {
        myActionTextObject.transform.position = transform.position + new Vector3(0, 1.25f, 0);
    }

    void Logic(bool reset)
    {
        if (predator)
        {
            ChangeState(State.RunningFromPredator);
            return;
        }

        if (hunger > thirst)
        {
            if (reproductiveUrge >= 40 && reproductiveUrge > hunger)
            {
                needIsWater = false;
                needIsReproduction = true;
                needIsFood = false;
            }
            else
            {
                needIsWater = false;
                needIsReproduction = false;
                needIsFood = true;
            }
        }
        else if (thirst > hunger)
        {
            if (reproductiveUrge >= 40 && reproductiveUrge > thirst)
            {
                needIsWater = false;
                needIsReproduction = true;
                needIsFood = false;
            }
            else
            {
                needIsWater = true;
                needIsReproduction = false;
                needIsFood = false;
            }
        }

        if (currentState == State.Labor && reproductiveUrge > 2) { return; }

        if ((hunger > 10 || thirst > 10 || (reproductiveUrge > 40)))
        {
            if (currentState != State.ConsumingNeed && currentState != State.ChasingNeed || reset)
            {
                ChangeState(State.SearchingForNeeds);
            }
        }

        else if (hunger <= 10 && thirst <= 10 && reproductiveUrge <= 10)
        {
            ChangeState(State.Wandering);
        }
    }

    void DoStates()
    {
        if (currentState == State.Wandering)
        {
            Wander();
        }
        else if (currentState == State.SearchingForNeeds)
        {
            SearchForNeed();
        }
        else if (currentState == State.ChasingNeed)
        {
            ChaseNeed();
        }
        else if (currentState == State.ConsumingNeed)
        {
            ConsumeNeed();
        }
        else if (currentState == State.RunningFromPredator)
        {
            Run();
        }
        else if (currentState == State.Labor)
        {
            Labor();
        }
    }

    bool dying;

    public void Die(bool showGrave)
    {
        if (dying) { return; }

        dying = true;

        Game.I.AddOrSubPop(gameObject.tag, -1);

        aSource.PlayOneShot(dieSound);

        StartCoroutine(Disappear(showGrave));
    }

    IEnumerator Disappear(bool showGrave)
    {
        aSource.Play();
        yield return new WaitWhile(() => aSource.isPlaying);

        if (showGrave)
        {
            Instantiate(gravePrefab, transform.position, Quaternion.identity);
        }

        Destroy(myActionTextObject);

        if (reproductiveUrge >= 100)
        {
            Destroy(myReproductionBar, 5);
        }
        else
        {
            Destroy(myReproductionBar);

            if (needIsFood && hunger > 99)
            {
                Destroy(myHungerBar, 5);
            }
            else
            {
                Destroy(myHungerBar);

                if (needIsWater && thirst > 99)
                {
                    Destroy(myThirstBar, 5f);
                }
                else
                {
                    Destroy(myThirstBar);
                }
            }
        }
        Destroy(gameObject);
    }

    void DoNeeds()
    {
        if (hunger >= 100 || thirst >= 100)
        {
            Die(true);
        }

        thirst += Time.deltaTime * 1.5f;

        if (myName == "Bear")
        {
            hunger += Time.deltaTime / 2;
        }
        else
        {
            hunger += Time.deltaTime;
        }

        reproductiveUrge += Time.deltaTime / 1.5f;
    }


    float runTimer;

    void Run()
    {
        if (!predator)
        {
            Logic(true);
            return;
        }

        if (runTimer > .25f)
        {
            if (predator.transform.position.x > transform.position.x)
            {
                rb.AddForce(new Vector2(-hopForce, 0), ForceMode2D.Impulse);
            }
            else if (predator.transform.position.x < transform.position.x)
            {
                rb.AddForce(new Vector2(hopForce, 0), ForceMode2D.Impulse);
            }

            if (predator.transform.position.y > transform.position.y)
            {
                rb.AddForce(new Vector2(0, -hopForce), ForceMode2D.Impulse);
            }
            else if (predator.transform.position.y < transform.position.y)
            {
                rb.AddForce(new Vector2(0, hopForce), ForceMode2D.Impulse);
            }

            runTimer = 0;
        }
        else
        {
            runTimer += Time.deltaTime;
        }
    }

    float growTimer;

    void Grow()
    {
        if (transform.localScale.x < maxGrowth)
        {
            if (growTimer > 10)
            {
                transform.localScale += new Vector3(.025f, .025f);
            }
            else
            {
                growTimer += Time.deltaTime;
            }
        }
    }

    void Update()
    {
        Logic(false);
        DoNeeds();
        DoStates();
        DoBars();
        if (dying) { return; }
        RotateTowardDirection();
        UpdateActionTextPosition();
        Grow();
    }

    void SearchForNeed()
    {
        HopAround(1);
    }

    float hopTimer;

    void HopAround(float timeBeforeNewHop)
    {
        if (hopTimer > timeBeforeNewHop + RandomFloat(-.2f, .2f))
        {
            aSource.Play();
            HopInRandomDir();
            hopTimer = 0;
        }
        else
        {
            hopTimer += Time.deltaTime;
        }
    }

    void HopInRandomDir()
    {
        var xr = Random.Range(-hopForce, hopForce + 1);
        var yr = Random.Range(-hopForce, hopForce + 1);
        while (xr == 0 && yr == 0)
        {
            xr = Random.Range(-hopForce, hopForce + 1);
            yr = Random.Range(-hopForce, hopForce + 1);
        }
        rb.AddForce(new Vector2(xr, yr), ForceMode2D.Impulse);

    }

    float chaseTimer;
    float chaseSteps;

    void ChaseNeed()
    {
        if (target == null)
        {
            ChangeState(State.SearchingForNeeds);
            return;
        }

        if (chaseTimer > .25f)
        {
            chaseSteps++;

            if (target.transform.position.x > transform.position.x)
            {
                rb.AddForce(new Vector2(1, 0), ForceMode2D.Impulse);
            }
            else if (target.transform.position.x < transform.position.x)
            {
                rb.AddForce(new Vector2(-1, 0), ForceMode2D.Impulse);
            }

            if (target.transform.position.y > transform.position.y)
            {
                rb.AddForce(new Vector2(0, 1), ForceMode2D.Impulse);
            }
            else if (target.transform.position.y < transform.position.y)
            {
                rb.AddForce(new Vector2(0, -1), ForceMode2D.Impulse);
            }

            if (Vector2.Distance(transform.position, target.transform.position) <= .75f)
            {
                if (needIsReproduction)
                {

                    if (sex == "Male")
                    {
                        reproductiveUrge = 0;
                        target.GetComponent<Animal>().currentState = State.Labor;
                    }
                    //else
                    //{
                    //    ChangeState(State.Labor);
                    //}
                }
                else
                {
                    ChangeState(State.ConsumingNeed);
                }
            }

            if (chaseSteps > 5 && target.tag == "Bunny" && myName == "Bear")
            {
                GiveUp();

            }

            if (chaseSteps >= 10 && (target.tag == "Bunny" || target.tag == "Fox") && needIsFood)
            {
                GiveUp();
            }

            chaseTimer = 0;
        }
        else
        {
            chaseTimer += Time.deltaTime;
        }
    }

    void GiveUp()
    {
        target.GetComponent<Animal>().predator = null;
        target.GetComponent<Animal>().Logic(true);
        target = null;
        chaseSteps = 0;
        ChangeState(State.SearchingForNeeds);
    }

    void Labor()
    {
        for (int i = 0; i < Random.Range(1, 5); i++)
        {
            var newbaby = Instantiate(this.gameObject, transform.position, Quaternion.identity);
            newbaby.transform.localScale = new Vector3(.5f, .5f, 1);
        }

        aSource.PlayOneShot(clickSound);

        reproductiveUrge = 0;
        Logic(true);
    }

    [SerializeField] float timeToConsume;
    float consumeTimer;

    void ConsumeNeed()
    {
        if (target == null)
        {
            ChangeState(State.SearchingForNeeds);
            return;
        }

        if (consumeTimer > timeToConsume)
        {
            //eat it

            if (needIsFood && target.tag != "Water")
            {
                aSource.PlayOneShot(eatSound);

                if (target.tag == "Bunny")
                {
                    hunger -= 50;
                }
                else if (target.tag == "Fox")
                {
                    hunger -= 60;
                }
                else if (target.tag == "Plant")
                {
                    hunger -= 40;
                    Game.I.AddOrSubPop("Plant", -1);
                    Destroy(target.gameObject);
                }

                if (hunger < 0)
                {
                    hunger = 0;
                }

                if (target.tag == "Bunny" || target.tag == "Fox")
                {
                    target.GetComponent<Animal>().Die(true);
                }
            }
            else
            {
                thirst -= 20;

                if (thirst < 0)
                {
                    thirst = 0;
                }
            }

            target = null;

            consumeTimer = 0;
            Logic(true);
        }
        else
        {
            consumeTimer += Time.deltaTime;
        }
    }

    void Wander()
    {
        HopAround(2);
    }

    bool IsFood(string tag)
    {
        for (int i = 0; i < tagsForFood.Length; i++)
        {
            if (tagsForFood[i] == tag)
            {
                return true;
            }
        }
        return false;
    }

    void RotateTowardDirection()
    {
        Vector2 moveDirection = rb.velocity;
        if (moveDirection != Vector2.zero)
        {
            float angle = Mathf.Atan2(moveDirection.y, moveDirection.x) * Mathf.Rad2Deg;
            transform.rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
        }
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Water") && currentState == State.ChasingNeed && needIsWater)
        {
            ChangeState(State.ConsumingNeed);
        }
        else if (collision.gameObject.CompareTag("Border") && currentState == State.RunningFromPredator)
        {
            HopAround(.25f);
            predator = null;
        }
    }

    private void OnCollisionStay2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Water") && currentState == State.ChasingNeed && needIsWater)
        {
            ChangeState(State.ConsumingNeed);
        }
        else if (collision.gameObject.CompareTag("Border") && currentState == State.RunningFromPredator)
        {
            HopAround(.25f);
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (currentState != State.SearchingForNeeds) { return; }

        if (needIsWater && collision.gameObject.CompareTag("Water"))
        {
            target = collision.gameObject;
            ChangeState(State.ChasingNeed);
        }
        else if (needIsFood && IsFood(collision.gameObject.tag))
        {
            if (collision.gameObject.tag == "Bunny" && myName == "Bear")
            {
                if (hunger < 50)
                {
                    return;
                }
            }

            target = collision.gameObject;
            ChangeState(State.ChasingNeed);

            if (IsFood("Bunny") || IsFood("Fox"))
            {
                target.gameObject.GetComponent<Animal>().predator = this.gameObject;
            }
        }
        else if (needIsReproduction)
        {
            if ((collision.gameObject.CompareTag("Bunny") && myName == "Bunny") || (collision.gameObject.CompareTag("Fox") && myName == "Fox") || (collision.gameObject.CompareTag("Bear") && myName == "Bear"))
            {
                if ((sex == "Male" && collision.gameObject.GetComponent<Animal>().sex == "Female") || (sex == "Female" && collision.gameObject.GetComponent<Animal>().sex == "Male"))
                {
                    target = collision.gameObject;
                    ChangeState(State.ChasingNeed);
                }
            }
        }
    }

    private void OnTriggerStay2D(Collider2D collision)
    {
        if (currentState != State.SearchingForNeeds) { return; }

        if (needIsWater && collision.gameObject.CompareTag("Water"))
        {
            target = collision.gameObject;
            ChangeState(State.ChasingNeed);
        }
        else if (needIsFood && IsFood(collision.gameObject.tag))
        {
            if (collision.gameObject.tag == "Bunny" && myName == "Bear")
            {
                if (hunger < 50)
                {
                    return;
                }
            }

            target = collision.gameObject;
            ChangeState(State.ChasingNeed);

            if (IsFood("Bunny") || IsFood("Fox"))
            {
                target.gameObject.GetComponent<Animal>().predator = this.gameObject;
            }
        }
        else if (needIsReproduction)
        {
            if ((collision.gameObject.CompareTag("Bunny") && myName == "Bunny") || (collision.gameObject.CompareTag("Fox") && myName == "Fox") || (collision.gameObject.CompareTag("Bear") && myName == "Bear"))
            {
                if ((sex == "Male" && collision.gameObject.GetComponent<Animal>().sex == "Female") || (sex == "Female" && collision.gameObject.GetComponent<Animal>().sex == "Male"))
                {
                    target = collision.gameObject;
                    ChangeState(State.ChasingNeed);
                }
            }
        }
    }

    float RandomFloat(float min, float max)
    {
        return Random.Range(min, max);
    }
}
