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

public class Ellipse : MonoBehaviour {

    private float angle;

    public int direction = -1;

    //ellipse radius
    public float radius = 2.5f;

    [Range(1.0f, 2.0f)]
    public float skewX;

    [Range(1.0f, 2.0f)]
    public float skewY;

    public float minSkew = 1.0f;
    public float maxSkew = 1.5f;

    [Range(0.0f,0.05f)]
    public float speed;

    bool skewNoise = true;


    //chkpt prefab
    public GameObject chkpntPrefab;

    //list of ellipse checkpoint
    public List<CheckPoint> adeptPoints;

    //current id
    int currentId = 0;

    public GameObject fire;


    // Static Stuff

    // Auto-Instantiatiation

    static Ellipse _instance;

    static public Ellipse instance
    {
        // using lazy instantiation here... 
        get
        {
            return _instance;
        }
    }

    void Awake()
    {
        _instance = this;
    }


    // Use this for initialization
    void Start () {

    }

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

        angle += speed;

        //this method is going to updates all the chkpnt transforms
        UpdateEllipseCkptPos();

        if (skewNoise) { 
            float noise = Mathf.PerlinNoise(Time.time, 0.0f) * Random.Range(-1f, 1f);
            float newSkewY = skewY;
            if (noise > 0 && skewY < maxSkew)
                newSkewY = skewY + noise;
            if (noise < 0 && skewY > minSkew)
                newSkewY = skewY + noise;

            skewY = Mathf.Lerp(skewY, newSkewY, Time.deltaTime * 3);
        }
        
    }

    public void setMinSpeed() {
        speed = 0.002f;
    }

    public void setMidSpeed()
    {
        speed = 0.005f;
    }

    public void setMaxSpeed()
    {
        speed = 0.01f;
    }


    //evaluate the entrypoint on the ellipse, the angle teta is evaluated through dot product etc...
    public Vector3 GetEntryPoint(Vector3 adeptPos) {

        //vector home - fire
        Vector3 hf = adeptPos - fire.transform.position;
        //evaluate the teta angle using the dot between the vector (fire-home) and the x axis
        int sign = 1;
        Vector3 xAxis = new Vector3(1.0f, 0.0f, 0.0f);
        float projHfOverX = Vector3.Dot(hf, xAxis);
        
        float teta = Mathf.Acos(projHfOverX / (hf.magnitude * xAxis.magnitude));

        if (adeptPos.y < 0)
            sign = -1;

        return new Vector3(Mathf.Cos(teta * sign) * radius * skewX, Mathf.Sin(teta * sign) * radius * skewY, 0.0f);  
    }

    public CheckPoint AddEllipseCkpt() {

        GameObject newChkpnt = Instantiate(chkpntPrefab) as GameObject;
        CheckPoint tmp = newChkpnt.GetComponent<CheckPoint>();
        tmp.id = currentId++;
        adeptPoints.Add(tmp);

        //return the newly creates chkpnt to adeptfactory
        return tmp;

    }


    public void UpdateEllipseCkptPos() {

        foreach (CheckPoint ckpt in adeptPoints)
        {
            //get the chkpnt updated position
            ckpt.transform.position = GetPos(ckpt.id);
            //Debug.DrawLine(Vector3.zero , ckpt.transform.position, Color.red);
        }

    }


    public void RemoveEllipseCkpt(CheckPoint chkptToDestroy) {

        int toDEstroyId = chkptToDestroy.id;

        foreach (CheckPoint ckpt in adeptPoints)
        {
            //we are going to decrease all the id greater then the IDtoDelete
            if (ckpt.id > toDEstroyId) {
                ckpt.id--;
            }
        }

        //we remove the chekcpoint
        adeptPoints.Remove(chkptToDestroy);

        //and then we are going to decrease also the number of instantiated chkpnt
        currentId--;

        //we should destroy also the chkpnt game object instance
        GameObject.Destroy(chkptToDestroy.gameObject);

    }

    public CheckPoint GetFreeEllipseCkpt(Vector3 adeptPos) {

        //cercare il ckpnt più vicino
        float ckptDistance = float.MaxValue;

        foreach (CheckPoint ckpt in adeptPoints) {
            //distance betwen each ckpnt and the current adept transform
            float tmp = (ckpt.transform.position - adeptPos).magnitude;

            if (tmp < ckptDistance) {
                ckptDistance = tmp;
            }
        }

        //fare spostare gli altri


        //assegnare id

        return new CheckPoint();
    }


    //evaluate the current ellipse point for the adept that is calling it
    Vector3 GetPos(int chkptId)
    {
        float newShiftDeg;

        if (chkptId == 0) {
            newShiftDeg = angle;
        }
        else { 
            //prendo il currentId e divido 360 per quel numero
            float phaseShiftDeg = 360 / currentId+1;

            //we should also consider the 0 element and sum to the current angle
            newShiftDeg = angle + phaseShiftDeg * (chkptId);
        }

        float x = Mathf.Sin((angle * direction) - newShiftDeg * Mathf.Deg2Rad) * skewX * radius;
        float y = Mathf.Cos((angle * direction) - newShiftDeg * Mathf.Deg2Rad) * skewY * radius;

        //for the z we use y to obtain near adept with higer z and far adept with a lower z
        return new Vector3(x, y, y);
    }


    public void StartSkewNoise() {

        skewNoise = true;
    }

    public void StopSkewNoise() {

        skewNoise = false;
    }
}
