using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Linq;

public class CameraController : Utils.Singleton<CameraController>
{
    public GameObject explosionPrefab;
    public enum Phase : int { CONFIRM, SNEEZE, LEAP, EXPLODE };
    public Reticle reticle;
    public Camera woeldCaemela;
    public AnimationCurve zoomCurve;
    public Transform spawnLocation;

    float animationDuration = 0.8f;
    float zoomOutSize; // set in Start()
    Vector3 zoomOutPosition; // set in Start()

    // Use this for initialization
    void Start()
    {
        zoomOutSize = woeldCaemela.orthographicSize;
        zoomOutPosition = woeldCaemela.transform.position;
        targetables = new List<Targetable>(
            GameObject.FindGameObjectsWithTag("Targetable").Select(i => i.GetComponent<Targetable>())
            );

        CleanDefeatedCreatures();

        if (FixTarget() && !AllDefeated())
        {
            SelectTargetable(target);
        }
        else
        {
            Destroy(reticle.gameObject);
            Explode();
        }
        

        // if we have defeated an enemy, we need to zoom out of that particular enemy
        if (GetPreviouslyVisitedTargetable() != null)
            ZoomOut();
    }

    public void ReportPhaseCompleted(Phase which)
    {
        bool hadPrevious = GetPreviouslyVisitedTargetable() != null;
        switch (which)
        {
            case Phase.CONFIRM:
                if (hadPrevious) Sneeze();
                else Leap();
                break;
            case Phase.SNEEZE:
                Leap();
                break;
            case Phase.LEAP:
                if (hadPrevious)
                    Explode();
                else
                    ZoomIn();
                break;
            case Phase.EXPLODE:

                Targetable prev = GetPreviouslyVisitedTargetable();
                if (prev != null)
                {
                    ReplaceWithDefeated(prev);
                }
                if (AllDefeated())
                {
                    SceneManager.LoadScene("end");
                }
                else
                {
                    ZoomIn();
                }
                break;
        }
    }

    private void CleanDefeatedCreatures()
    {
        Targetable prev = GetPreviouslyVisitedTargetable();

        foreach (Targetable t in targetables)
        {
            // remove creature, if it has been defeated and it isn't the thing we _just_ defeated
            if (Progress.IsDefeated(t.levelName) && t.levelName != prev.levelName)
            {
                ReplaceWithDefeated(t);
            }
        }
    }

    private void ConfirmTarget()
    {
        ReportPhaseCompleted(Phase.CONFIRM);
    }

    private void ReplaceWithDefeated(Targetable t)
    {
        Destroy(t.gameObject);
    }

    void Sneeze()
    {
        Targetable previous = GetPreviouslyVisitedTargetable();

        if (previous != null)
        {
            // this will callback us, and perform leap + zoom
            previous.DoSneeze();
        }
        else
        {
            // didnt have previous, so we just leap to the selected target
            ReportPhaseCompleted(Phase.SNEEZE);
        }
    }

    void Leap()
    {
        Targetable previousFucker = GetPreviouslyVisitedTargetable();

        if (previousFucker == null)
        {
            UIController.Instance.PlayLeap(true);
        }
        else
        {
            bool dir = previousFucker.transform.position.x < target.transform.position.x;
            UIController.Instance.PlayLeap(dir);
        }
        
    }

    void Explode()
    {
        Targetable prev = GetPreviouslyVisitedTargetable();
        if (prev == null)
        {
            print("THIS SHOULD NOT HAPPEN");
            ReportPhaseCompleted(Phase.EXPLODE);
            return;
        }
        Instantiate<GameObject>(explosionPrefab, prev.targetTransform.position, Quaternion.identity);
    }

    // real world target
    Targetable target;
    CreatureInfo targetInfo;
    float targetZoomLevel;

    // state bools
    bool animating;
    bool lockedInClose;

    List<Targetable> targetables;

	
	// Update is called once per frame
	void Update () {
        if (FakeInput.GetKeyUp(KeyCode.Space))
        {
            ConfirmTarget();
        }
        if (FakeInput.GetKeyUp(KeyCode.RightArrow))
        {
            NextTarget();
        }
        if (FakeInput.GetKeyUp(KeyCode.LeftArrow))
        {
            PreviousTarget();
        }

        if (lockedInClose)
        {
            SetWorldCameraPosition(GetTargetPosition());
        }

        //if (animating) print(String.Format("Animation state:\n position: {0}\n zoom: {1}", woeldCaemela.transform.position, woeldCaemela.orthographicSize));
        
	}

    private bool AllDefeated()
    {
        foreach (Targetable t in targetables)
        {
            if (!Progress.IsDefeated(t.levelName))
            {
                return false;
            }
        }
        return true;
    }

    public void SelectTarget(GameObject target)
    {
        Targetable targetable = target.GetComponent<Targetable>();

        if (!IsValidTarget(targetable)) return;

        SelectTargetable(targetable);
        
        // do something
    }

    private void SelectTargetable(Targetable targetable)
    {
        print("Selected target " + targetable.levelName);
        target = targetable;
        reticle.TravelToTarget(targetable);
    }

    public void NextTarget()
    {
        if (!FixTarget()) return;
        int index = targetables.IndexOf(target);
        
        for (int i = 0; i < targetables.Count; i++)
        {
            index++;
            if (index >= targetables.Count) index = 0;
            Targetable _target = targetables[index];

            if (IsValidTarget(_target))
            {
                SelectTargetable(_target);
                return;
            }
        }
    }

    private Targetable GetPreviouslyVisitedTargetable()
    { 
        Progress.Defeat defeat = Progress.LatestDefeat();

        if (defeat == null) return null;

        string prevname = defeat.Name;
        foreach (Targetable t in targetables)
        {
            if (prevname == t.levelName)
            {
                return t;
            }
        }

        return null;
    }

    private Targetable TargetableFromName(string name)
    {
        foreach (Targetable t in targetables)
        {
            if (t.levelName == name) return t;
        }

        return null;
    }

    public void PreviousTarget()
    {
        if (!FixTarget()) return;
        int index = targetables.IndexOf(target);

        for (int i = 0; i < targetables.Count; i++)
        {
            index--;
            if (index < 0) index = targetables.Count - 1;
            Targetable _target = targetables[index];

            if (IsValidTarget(_target))
            {
                SelectTargetable(_target);
                return;
            }
        }
    }

    private bool FixTarget()
    {
        if (target == null && !AllDefeated())
        {
            foreach (Targetable t in targetables)
            {
                if (IsValidTarget(t))
                {
                    target = t;
                    return true;
                }
            }
            return false;
        }
        return true;
    }

    private bool IsValidTarget(Targetable targetable)
    {
        return !Progress.IsDefeated(targetable.levelName);
    }

    public void ZoomIn()
    {
        if (animating) return;
        if (target == null)
        {
            Debug.LogError("No target, nothin to zoom to!");
            return;
        }
        UIController.Instance.FadeOut(animationDuration);
        StartCoroutine(AnimateZoomIn());
    }

    public void ZoomOut()
    {
        if (animating) return;

        UIController.Instance.FadeIn(animationDuration);
        StartCoroutine(AnimateZoomOut());
    }

    IEnumerator AnimateZoomIn()
    {

        animating = true;
        float animatedFor = 0;
        float targetSize = GetCloseUpSize();

        while (animatedFor < animationDuration)
        {
            // adjust animation time to 0...1 range
            float percentage = animatedFor / animationDuration;
            // calculate position and zoom level based on time and zoomCurve
            Vector2 position = Vector2.Lerp(zoomOutPosition, GetTargetPosition(), zoomCurve.Evaluate(percentage));
            float size = Mathf.Lerp(zoomOutSize, targetSize, zoomCurve.Evaluate(percentage));
            SetWorldCameraPosition(position);
            SetWorldCameraSize(size);

            animatedFor += Time.deltaTime;

            yield return new WaitForEndOfFrame();
        }
        animating = false;
        LockWorldCameraToTarget();
        StartLevel();
    }

    IEnumerator AnimateZoomOut()
    {
        UnlockWorldCameraFromTarget();
        animating = true;
        float animatedFor = 0;
        float startSize = GetCloseUpSize();

        while (animatedFor < animationDuration)
        {
            // adjust animation time to 0...1 range
            float percentage = animatedFor / animationDuration;
            // calculate position and zoom level based on time and zoomCurve
            Vector2 position = Vector2.Lerp(GetTargetPosition(), zoomOutPosition, zoomCurve.Evaluate(percentage));
            float size = Mathf.Lerp(startSize, zoomOutSize, zoomCurve.Evaluate(percentage));
            SetWorldCameraPosition(position);
            SetWorldCameraSize(size);


            animatedFor += Time.deltaTime;
            yield return new WaitForEndOfFrame();
        }

        // CleanTarget();

        animating = false;
    }

    private void SetWorldCameraPosition(Vector2 position)
    {
        woeldCaemela.transform.position = new Vector3(position.x, position.y, -10);
    }

    private void SetWorldCameraSize(float size)
    {
        woeldCaemela.orthographicSize = size;
    }

    private void StartLevel()
    {
        SceneManager.LoadScene(target.levelName);
    }

    private void LockWorldCameraToTarget()
    {
        lockedInClose = true;
    }

    private void UnlockWorldCameraFromTarget()
    {
        lockedInClose = false;
    }


    Vector2 GetTargetPosition()
    {
        if (target == null) return Vector2.zero;

        Vector2 ret = target.targetTransform.position;
        //ret += creatureController.GetPlayersCenterRelativePosition() * GetTargetZoomLevel();
        
        return ret;
    }


    float GetCloseUpSize()
    {
        return 0.2f;
    }

    void CleanTarget()
    {
        target = null;
        targetInfo = null;
    }
}
