using UnityEngine;
using System.Collections;

public class CameraManager : MonoBehaviour
{
	// Orientation enum (Nord look at along world z axis)
	public enum Direction
	{
		FarNorth,
		FarWest,
		FarSouth,
		FarEast,
		NearNorth,
		NearWest,
		NearSouth,
		NearEast
	};
	
	// Instance
	private static CameraManager s_Instance;
	public static CameraManager Instance
	{
		get
		{
			if(s_Instance == null)
			{
				s_Instance = FindObjectOfType(typeof(CameraManager)) as CameraManager;
			}
			return s_Instance;
		}
	}
	// Transform
	private Transform m_Transform;
    public Transform MainCameraTransform
	{
        get { return m_CameraTransform; }
	}
	// Camera info
	private Camera m_Camera;
	private Transform m_CameraTransform;
	// Orientation is changing ?
	private bool m_OrientationChanges;
	private Direction m_ToOrientation;
	private Timer m_Timer;
	// Offset with the player
	private Transform m_Target;
	private Vector3 m_Offset;
    // Camera movements
    private CameraMovements m_CameraMovements;
    private Vector3 m_NewPosition;
    private bool m_CanMove;
    private bool m_IsFix;
    public float m_SwitchSpeed = 1f;
    // => Movable zones
    private float m_Distance;
    private bool m_LockX;
    private bool m_LockY;
    private float m_ValueX;
    private float m_ValueY;
    // => Fix zones
    private Vector3 m_FixPosition;
	
	void Start()
	{
        m_Offset = new Vector3(0f, 0f, -3f);
		m_Transform = transform;
		m_Camera = GetComponentInChildren<Camera>();
		//m_Camera.fieldOfView = 40f;
		m_CameraTransform = m_Camera.transform; // GameObject.Find("Camera").transform;
        m_CameraTransform.localPosition = Vector3.zero;
		m_Target = GameObject.Find("LookAtTarget").transform;
		m_Timer = new Timer(0.5f);
		m_OrientationChanges = false;
        m_CameraMovements = gameObject.GetComponent<CameraMovements>();
        m_CanMove = true;
        m_IsFix = false;
	}
	
	void LateUpdate()
	{
        if (GameManager.Instance.HeroTransform == null)
            return;

        // Replace the camera
        m_CameraMovements.HerosZone(GameManager.Instance.HeroTransform.position);

        if (m_CanMove)
        {
            // Follows the player
            m_NewPosition = m_Target.position + m_Offset;
            if (m_LockX)
            {
                m_NewPosition.Set(m_ValueX, m_NewPosition.y, m_NewPosition.z);
            }
            if (m_LockY)
            {
                m_NewPosition.Set(m_NewPosition.x, m_ValueY, m_NewPosition.z);
            }
            m_Transform.position = m_NewPosition;
            // Looks at the target
            //m_CameraTransform.LookAt(m_Target.position);
        }
	}
    
    // Zone changement
    // => Movable zone
    public void NewSettings(float distance, bool lockX, bool lockY, float axeX, float axeY)
    {
        bool lastLockX = m_LockX;
        bool lastLockY = m_LockY;

        m_Distance = -distance;
        m_LockX = lockX;
        m_LockY = lockY;
        m_ValueX = axeX;
        m_ValueY = axeY;

        if (m_IsFix)
        {
            m_IsFix = false;
            StartCoroutine("C_FixToMobile");
        }
        else
        {
            m_CanMove = false;
            StartCoroutine(C_MobToMob(lastLockX, lastLockY));
        }
    }

    private IEnumerator C_FixToMobile()
    {
        Vector3 beginPosition = m_Transform.position;
        Vector3 endPosition;
        Timer timer = new Timer(m_SwitchSpeed);
        timer.Start();

        do
        {
            endPosition = m_Target.position + m_Offset;
            if (m_LockX)
            {
                endPosition.x = m_ValueX;
            }
            if (m_LockY)
            {
                endPosition.y = m_ValueY;
            }
            m_Transform.position = Vector3.Lerp(beginPosition, endPosition, timer.GetHermiteProgress());
            yield return 0;
        } while (!timer.UpdateTime() && !m_IsFix);

        if(!m_IsFix) m_CanMove = true;
    }

    private IEnumerator C_MobToMob(bool lastLockX, bool lastLockY)
    {
        float beginDistance = m_Offset.z;

        // Used if the last zone was locked
        Vector3 beginPosition = m_Transform.position;
        Vector3 endPosition;

        // Used if the new zone is locked
        float xValBegin = beginPosition.x;
        float xValEnd = m_ValueX;
        float yValBegin = beginPosition.y;
        float yValEnd = m_ValueY;

        Timer timer = new Timer(m_SwitchSpeed);
        timer.Start();

        do
        {
            m_Offset.Set(m_Offset.x, m_Offset.y, CustomMath.Hermite(beginDistance, m_Distance, timer.Progress()));
            endPosition = m_Target.position + m_Offset;
            if (m_LockX)
            {
                endPosition.x = CustomMath.Hermite(xValBegin, xValEnd, timer.Progress());
            }
            else if (lastLockX)
            {
                endPosition.x = CustomMath.Hermite(xValBegin, endPosition.x, timer.GetHermiteProgress());
            }
            if (m_LockY)
            {
                endPosition.y = CustomMath.Hermite(yValBegin, yValEnd, timer.Progress());
            }
            else if (lastLockY)
            {
                endPosition.y = CustomMath.Hermite(yValBegin, endPosition.y, timer.GetHermiteProgress());
            }

            m_Transform.position = Vector3.Lerp(beginPosition, endPosition, timer.GetHermiteProgress());
            yield return 0;
        } while (!timer.UpdateTime() && !m_IsFix);

        if(!m_IsFix) m_CanMove = true;
    }

    // => Fixed zone
    public void NewSettings(Transform target)
    {
        m_FixPosition = target.position;
        m_CameraTransform.rotation = target.rotation;
        StartCoroutine("C_MobileToFix");

        m_IsFix = true;
        m_CanMove = false;
    }

    private IEnumerator C_MobileToFix() // Works for Fix to fix too
    {
        Vector3 beginPosition = m_Transform.position;
        Timer timer = new Timer(m_SwitchSpeed);
        timer.Start();

        do
        {
            m_Transform.position = Vector3.Lerp(beginPosition, m_FixPosition, timer.GetHermiteProgress());
            yield return 0;
        } while (!timer.UpdateTime() && m_IsFix);
    }
}