using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class CastleAnimator : MonoBehaviour {

    public float WaitAtStart = 2;
    public float MoveSpeed = 30;
    public float MaxWaitTime = 5;
    [SerializeField]
    public AnimationCurve SpeedCurve;
    public float BridgeSpeed = 30;
    public float maxBridgeWaitTime = 0.5f;
    [SerializeField]
    public AnimationCurve ScaleCurve;





    [System.Serializable]
    public struct Base {
        public GameObject gameObject;
        public Bounds BaseBound;
        public Tower tower;
    }

    [System.Serializable]
    public struct Tower {
        public Vector3 originalPos;
        public GameObject gameObject;
        public List<GameObject> TowerPieces;
        public GameObject[] ScalePoints;
    }

    [HideInInspector]
    public List<Base> Bases = new List<Base>();

    public enum ScaleDirection {PositiveX, PositiveZ, NegativeX, NegativeZ }


    public void Start()
    {
        if (MoveSpeed == 0) {
            Debug.LogWarning("Tower Movement set to 0");
            return;
        }

        foreach(Base Base in Bases) {
            StartCoroutine(MoveTower(Base.tower, MoveSpeed, MaxWaitTime));
        }



 
    }

    private IEnumerator MoveTower(Tower tower, float moveSpeed, float startRandomizer)
    {

        foreach (GameObject scalePoint in tower.ScalePoints) {
            scalePoint.SetActive(false);
            scalePoint.transform.localScale = Vector3.zero;
        }

        Vector3 endPos = tower.gameObject.transform.position;
        tower.gameObject.transform.localPosition = Vector3.zero;

        Vector3 startPos = tower.gameObject.transform.position;
        float elapsedTime = 0;
        float randomWait = Random.Range(0, startRandomizer);
        float animationTime = (Vector3.Distance(endPos, startPos) / moveSpeed);

        yield return new WaitForSeconds(randomWait + WaitAtStart);

        while (elapsedTime < animationTime) {


            float curvePercent = SpeedCurve.Evaluate((elapsedTime / animationTime));

            tower.gameObject.transform.position = UnclampedLerp(startPos, endPos, curvePercent);
            elapsedTime += Time.deltaTime;
            yield return new WaitForEndOfFrame();
        }

        tower.gameObject.transform.position = endPos;

        StartCoroutine(scaleBridge(tower.ScalePoints[0], ScaleDirection.PositiveX, BridgeSpeed,maxBridgeWaitTime));
        StartCoroutine(scaleBridge(tower.ScalePoints[1], ScaleDirection.PositiveZ, BridgeSpeed,maxBridgeWaitTime));
        StartCoroutine(scaleBridge(tower.ScalePoints[2], ScaleDirection.NegativeX, BridgeSpeed,maxBridgeWaitTime));
        StartCoroutine(scaleBridge(tower.ScalePoints[3], ScaleDirection.NegativeZ, BridgeSpeed,maxBridgeWaitTime));

    }

    private IEnumerator scaleBridge(GameObject scalePoint, ScaleDirection direction, float scaleSpeed, float startRandomizer)
    {

        float randomWait = Random.Range(0, startRandomizer);
        yield return new WaitForSeconds(randomWait);

        scalePoint.SetActive(true);

        Vector3 scaleVector = Vector3.zero;
        if (direction == ScaleDirection.NegativeX || direction == ScaleDirection.PositiveX) {
            scaleVector = Vector3.right;
            scalePoint.transform.localScale = new Vector3(0, 1, 1);
        } else {
            scaleVector = Vector3.forward;
            scalePoint.transform.localScale = new Vector3(1, 1, 0);
        }

        Vector3 startScale = scalePoint.transform.localScale;

        float elapsedTime = 0;
        float animationTime = 10 / scaleSpeed;

        while (elapsedTime < animationTime) {

            float curvePercent = ScaleCurve.Evaluate((elapsedTime / animationTime));

            scalePoint.gameObject.transform.localScale = UnclampedLerp(startScale, Vector3.one, curvePercent);
            elapsedTime += Time.deltaTime;
            yield return new WaitForEndOfFrame();
        }

        scalePoint.transform.localScale = Vector3.one;
    }

    public static Vector3 UnclampedLerp(Vector3 a, Vector3 b, float t)
    {
        return t * b + (1 - t) * a;
    }







    private Bounds getWorldBounds(GameObject input)
    {
        Bounds retVal = new Bounds(input.transform.position, Vector3.zero);

        foreach (Renderer renderer in input.GetComponentsInChildren<Renderer>()) {
            retVal.Encapsulate(renderer.bounds);
        }

        return retVal;
    }

    private void MoveParent(GameObject input, Vector3 moveTo)
    {
        Vector3 movement = moveTo - input.transform.position;

        input.transform.position = moveTo;

        foreach (Transform child in input.transform) {
            child.position -= movement;
        }



    }

#if UNITY_EDITOR
    public void AddBase(Transform input)
    {
        Base newBase = new Base();
        GameObject Base = new GameObject();
        Undo.RegisterCreatedObjectUndo(Base, "Created Base");

        Base.transform.position = getWorldBounds(input.gameObject).center;
        Base.transform.parent = transform;
        Base.name = "Base" + Bases.Count;

        Undo.SetTransformParent(input.transform, Base.transform, "Created Base");
        //newBaseObject.parent = Base.transform;
        newBase.gameObject = Base;
        Undo.RecordObject(this, "Created Base");

        GameObject towerHolder = new GameObject();
        towerHolder.transform.parent = Base.transform;
        towerHolder.transform.localPosition = Vector3.zero;
        towerHolder.name = "Tower Holder" + Bases.Count;

        newBase.tower = new Tower();
        newBase.tower.gameObject = towerHolder;
        newBase.tower.TowerPieces = new List<GameObject>();

        newBase.tower.ScalePoints = new GameObject[4];
        for (int i = 0; i < 4; i++) {
            newBase.tower.ScalePoints[i] = new GameObject();
            newBase.tower.ScalePoints[i].transform.parent = Base.transform;
            newBase.tower.ScalePoints[i].transform.localPosition = Vector3.zero;
            newBase.tower.ScalePoints[i].name = "ScalePoint" + Bases.Count + ", " + i;
        }

        Bases.Add(newBase);
    }

    public void AddTower(Base selectedBase, Transform[] input)
    {
        Undo.RecordObject(this, "Added Tower");
        foreach (Transform towerObject in input) {
            if (towerObject == selectedBase.gameObject.transform)
                continue;

            if (selectedBase.tower.TowerPieces.Contains(towerObject.gameObject))
                continue;

            selectedBase.tower.TowerPieces.Add(towerObject.gameObject);
            Undo.SetTransformParent(towerObject.transform, selectedBase.tower.gameObject.transform, "Added Tower");


        }

        Undo.RecordObject(selectedBase.tower.gameObject, "Added Tower");

        MoveParent(selectedBase.tower.gameObject, selectedBase.gameObject.transform.position + ((getWorldBounds(selectedBase.tower.gameObject).size.y) * Vector3.up));
        selectedBase.tower.originalPos = selectedBase.tower.gameObject.transform.position;
    }

    public void AddBridge(Base selectedBase,ScaleDirection direction, Transform[] input)
    {

        foreach (Transform bridgeObject in input) {
            if (bridgeObject == selectedBase.gameObject)
                continue;

            if (selectedBase.tower.TowerPieces.Contains(bridgeObject.gameObject))
                continue;

            Undo.SetTransformParent(bridgeObject.transform, selectedBase.tower.ScalePoints[(int)direction].transform, "Added Bridge");
        }
    }

    public string[] GetBaseNames()
    {
        string[] retVal = new string[Bases.Count];

        for (int i = 0; i < retVal.Length; i++) {
            retVal[i] = "Tower" + i;
        }
        return retVal;
    }

#endif

}


