using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Birb : MonoBehaviour
{
    public enum State
    {
        Ground,
        Air,
        Branch,
        Nest
    }
    State state = State.Ground;

    public bool inNest = false;

    public float groundMove = .05f;
    public float airMove = 1.0f; // airspeed velocity of an unladen birb
    public float airDrag = .25f; // drag coefficient

    public float groundedThreshold = 1.0f;

    public AudioClip hopSound;
    public AudioClip flapSound;
    public AudioClip peckSound;

    Energy birbEnergy;
    int flapEnergy = 2; // how much flapping costs
    int wormEnergy = 10;

    void OnFeetCollided(Collision2D theCollision)
    {

        if (!Input.GetButton("Jump")) // jumping will set you free
        {
            foreach (ContactPoint2D contact in theCollision.contacts)
            {
                if (contact.normal.y > groundedThreshold)
                {
                    string layerName = LayerMask.LayerToName(contact.collider.gameObject.layer);
                    if (layerName == "ground")
                        state = State.Ground;
                    else if (layerName == "branch")
                        state = State.Branch;
                    else if (layerName == "nest")
                        state = State.Nest;

                    groundedOn = theCollision.gameObject;
                    break;
                }
            }
        }
    }


    void OnFeetNoLongerColliding(Collision2D theCollision)
    {
        if (theCollision.gameObject == groundedOn)
        {
            groundedOn = null;
            state = State.Air;
        }
    }

    void OnNestEnter(Collider2D theCollider)
    {
        inNest = true;
        hud.setCommentary("home is where the nest is");
        nestGlow.begin();

    }
    void OnNestExit(Collider2D theCollider)
    {
        inNest = false;
        nestGlow.end();
    }


    // Start is called before the first frame update
    void Start()
    {
        rigidbody = GetComponent<Rigidbody2D>();
        groundLayerMask = LayerMask.GetMask("ground");
        collectiblesLayerMask = LayerMask.GetMask("collectibles");
        //groundOrBranchLayerMask = LayerMask.GetMask("ground", "branch");
        state = State.Air;
        collider = GetComponent<Collider2D>();
        feetColliders = feet.GetComponents<Collider2D>();
        feet.OnFeetCollisionEnter.AddListener(OnFeetCollided);
        feet.OnFeetCollisionExit.AddListener(OnFeetNoLongerColliding);

        nestDetector.OnNestTriggerEnter.AddListener(OnNestEnter);
        nestDetector.OnNestTriggerExit.AddListener(OnNestExit);

        birbEnergy = (Energy)gameObject.GetComponent("Energy");
    }
    new Rigidbody2D rigidbody;
    new Collider2D collider;
    Collider2D[] feetColliders;

    public float hopJumpVelocity = 2.5f;
    public float flapJumpVelocity = 1.5f;
    public BirbFeet feet;
    public BirbNestDetector nestDetector;

    public float peckRadius = 1.1f;

    public float rightyUpDamp = .5f;

    public float glideDampen = .5f;

    public Animator birbAnimator;


    Collectible inMouth;

    Vector2 velocity;

    LayerMask groundLayerMask;
    //LayerMask groundOrBranchLayerMask;
    //LayerMask branchLayerMask;
    //LayerMask nestLayerMask;
    LayerMask collectiblesLayerMask;

    public Hud hud;

    public Glowy nestGlow;

    State DetectState()
    {
        Debug.DrawRay(transform.position, Vector3.down);
        RaycastHit2D groundHit = Physics2D.Raycast(transform.position, Vector3.down, Mathf.Infinity, groundLayerMask);
        if (groundHit.collider != null)
            return State.Air;

        return State.Ground;
    }

    static GameObject treeRoot
    {
        get
        {
            if (!_treeRoot)
                _treeRoot = GameObject.FindGameObjectWithTag("tree");
            return _treeRoot;
        }
    }
    static GameObject _treeRoot = null;

    GameObject groundedOn = null;
    static Collider2D[] treeCollders
    {
        get
        {
            if (null == _treeColliders)
                _treeColliders = treeRoot.GetComponentsInChildren<Collider2D>();
            return _treeColliders;
        }
    }
    static Collider2D[] _treeColliders = null;
    

    bool ignoringTreeColliders = false;

    void IgnoreTreeColliders(bool ignore = true)
    {
        foreach(Collider2D treeCollider in treeCollders)
        {
            foreach(Collider2D feetCollider in feetColliders)
                Physics2D.IgnoreCollision(feetCollider, treeCollider, ignore);
        }
        ignoringTreeColliders = ignore;
    }
    Collider2D[] updateColliderResults = new Collider2D[1];
    ContactFilter2D contactFilter = new ContactFilter2D();

    // Update is called once per frame
    void Update()
    {
        updateUI();

        if (Input.GetButtonDown("Jump"))
        {
            birbAnimator.SetTrigger("Flap"); // flap flap
        }

        velocity = rigidbody.velocity;
        if (state == State.Ground)
        {
            velocity.x = 0;

            // on ground:
            // no buttons pressed => ground-idle
            // left or right arrow down-press => move left or right
            float xMove = Input.GetAxis("Horizontal");
            if (Mathf.Abs(xMove) > 0)
            {
                //Debug.LogFormat("Move {0}", xMove);
                velocity += xMove * groundMove * Vector2.right * Time.deltaTime;
            }

            // jump button down-press => hop
            if (Input.GetButtonDown("Jump"))
            {
                state = State.Air;
                //Debug.Log("Jump!");
                velocity = Vector2.up * hopJumpVelocity;
                if (hopSound)
                    AudioSource.PlayClipAtPoint(hopSound, transform.position);
            }

            // peck button down-press => peck
            if (Input.GetButtonDown("Peck"))
            {
                Debug.Log("Peck");
                birbAnimator.SetTrigger("Peck");
                Collectible pickup = FindNearestPickup();
                if (pickup != null)
                {
                    inMouth = pickup;

                    string pickupMessage = "";
                    switch (pickup.type)
                    {
                        case "twigs":
                            pickupMessage = "A spindly twig";
                            break;
                        case "grass":
                            pickupMessage = "Some mulchy grass";
                            break;
                        case "mud":
                            pickupMessage = "A beakful of mud";
                            break;
                        case "worm":
                            pickupMessage = "A yummy worm!";
                            break;
                        default:
                            pickupMessage = "";
                            break;
                    }
                    hud.setCommentary(pickupMessage);

                    // eat?
                    if (pickup.gameObject.GetComponent("Eatable"))
                    {
                        Eat(pickup.gameObject);
                        inMouth = null;
                    }
                }
                if (peckSound)
                    AudioSource.PlayClipAtPoint(peckSound, transform.position);
            }
        }
        else
        if (state == State.Air)
        {
            velocity.x *= airDrag;

            if (velocity.y > 0)
            {
                if (!ignoringTreeColliders)
                    IgnoreTreeColliders(true);
                    
            } else
            {
                if (ignoringTreeColliders)
                    IgnoreTreeColliders(false);
            }

            foreach(CircleCollider2D feetCollider in feetColliders)
            {
                if (feetCollider.OverlapCollider(contactFilter, updateColliderResults) > 0)
                {
                    string layer = LayerMask.LayerToName(updateColliderResults[0].gameObject.layer);
                    if (layer == "branch")
                        state = State.Branch;
                    if (layer == "nest")
                        state = State.Nest;
                }

            }

            // in air:
            // jump button down-press => flap
            if (Input.GetButtonDown("Jump"))
            {
                FlyMore();
            } else // jump button hold when velocity.y < 0 && |velocity.x| > 0 => glide (x velocity counteracts gravity)
            if (Input.GetButton("Jump"))
            {
                if (velocity.y < 0 && Mathf.Abs(velocity.x) > 0)
                {
                    Debug.Log("Glide");
                  // this didn't really add anything when i tried it but YMMV  velocity.y *= glideDampen;
                }
            }

            // left or right arrow => move left or right
            float xMove = Input.GetAxis("Horizontal");
            if (Mathf.Abs(xMove) > 0)
            {
                Debug.LogFormat("Move {0}", xMove);
                velocity += xMove * airMove * Vector2.right * Time.deltaTime;
            }

            Vector2 up = transform.up;
            rigidbody.rotation *= rightyUpDamp;
            
        } else
        if (state == State.Branch)
        {
            if (Input.GetButtonDown("Jump"))
            {
                if (Input.GetAxis("Vertical") < 0)
                { // help the birb get unstuck from pesky branches
                    rigidbody.velocity = hopJumpVelocity * Vector2.down;
                    rigidbody.MovePosition(rigidbody.position + rigidbody.velocity);
                }
                else
                    rigidbody.velocity = hopJumpVelocity * Vector3.up;

            }
        } else
        if (state == State.Nest)
        {
            if (Input.GetButtonDown("Jump"))
                rigidbody.velocity = hopJumpVelocity * Vector3.up;
        }
        rigidbody.velocity = velocity;

        // peck button is not held in so drop whatever you're holding
        if (!Input.GetButton("Peck") && inMouth != null)
        {
            inMouth = null;
        }

        if (inMouth != null)
        {
            inMouth.MoveTowards(transform.position);
        }
    }

    Collectible FindNearestPickup()
    {
        Collectible result = null;

        SortedList<float, Collider2D> collidersSortedByDistance = new SortedList<float, Collider2D>();
        foreach(Collider2D collider in Physics2D.OverlapCircleAll(transform.position, peckRadius, collectiblesLayerMask))
        {
            float distance = Vector2.Distance(rigidbody.position, collider.transform.position);
            while (collidersSortedByDistance.ContainsKey(distance))
                distance += 0.001f;
            collidersSortedByDistance.Add(distance, collider);
        }

        if (collidersSortedByDistance.Count > 0)
        {
            IEnumerator<KeyValuePair<float,Collider2D>> enumerator = collidersSortedByDistance.GetEnumerator();
            enumerator.MoveNext();
            result = enumerator.Current.Value.GetComponent<Collectible>(); ;
        }

        return result;
    }

    public bool canAddToNest()
    {
        return inNest;
    }

    void Eat(GameObject foodObject)
    {
        Eatable food = (Eatable)foodObject.GetComponent("Eatable");
        if (food)
        {
            birbEnergy.Replenish(food.amount);
        }
        Destroy(foodObject);
    }

    void FlyMore()
    {
        //Debug.Log("Flap");
        velocity = Vector2.up * flapJumpVelocity;
        AudioSource.PlayClipAtPoint(flapSound, transform.position);

        birbEnergy.Drain(flapEnergy);
    }

    void updateUI()
    {
        hud.setEnergy(birbEnergy.currentLevel);
    }
}
