using UnityEngine;
using System.Collections;

public struct CameraPose
{
	public Vector3 m_position;
	public Quaternion m_rotation;
	public float m_fov;

	public CameraPose(Vector3 pos, Quaternion rot, float fov)
	{
		m_position = pos;
		m_rotation = rot;
		m_fov = fov;
	}

    public static CameraPose FromTransform(Transform t, float fov)
    {
        CameraPose pose = new CameraPose();
        pose.m_position = t.position;
        pose.m_rotation = t.rotation;
        pose.m_fov = fov;
        return pose;
    }

    public static CameraPose FromCamera(Camera camera)
	{
		CameraPose pose = new CameraPose();
		pose.m_position = camera.transform.position;
		pose.m_rotation = camera.transform.rotation;
		pose.m_fov = camera.fieldOfView;
		return pose;
	}

	public void ToCamera(Camera camera)
	{
		camera.transform.position = m_position;
		camera.transform.rotation = m_rotation;
		camera.fieldOfView = m_fov;
	}

	public static CameraPose Lerp(CameraPose a, CameraPose b, float t)
	{
		CameraPose pose = new CameraPose();
		pose.m_position = Vector3.Lerp(a.m_position, b.m_position, t);
		pose.m_rotation = Quaternion.Slerp(a.m_rotation, b.m_rotation, t);
		pose.m_fov = Mathf.Lerp (a.m_fov, b.m_fov, t);
		return pose;
	}
}

public class CameraManager : MonoBehaviourSingleton<CameraManager>
{
	public bool m_active;

	public delegate void Callback();

	[System.NonSerialized]
	public bool m_isBlending = false;
	[System.NonSerialized]
	public float m_blendFactor;
	[System.NonSerialized]
	public float m_blendDuration;

	private GFCurve m_blendCurve;
    private AnimationCurve m_blendCurveAnimation;
    private Callback m_blendCallback;

	private CameraPose m_oldPose;
	private CameraPose m_crtPose;

	public CameraManager()
	{
		m_crtPose = new CameraPose(Vector3.zero, Quaternion.identity, 60.0f);
	}

	public void Blend(float duration, GFCurve curve = GFCurve.Linear, Callback callback = null)
	{
		m_isBlending = true;
		m_blendFactor = 0.0f;
		m_blendDuration = duration;
		m_blendCurve = curve;
		m_blendCallback = callback;

		m_oldPose = CameraPose.FromCamera(Camera.main);
	}

    public void SetBlendAnimation(AnimationCurve anim)
    {
        m_blendCurve = GFCurve.None;
        m_blendCurveAnimation = anim;
    }

    public CameraPose GetPose()
    {
        return m_crtPose;
    }

    public void SetPose(CameraPose pose)
	{
		m_active = true;
		m_crtPose = pose;
	}

    public void SetPosition(Vector3 position)
    {
        m_active = true;
        m_crtPose.m_position = position;
    }

    public void SetRotation(Quaternion rotation)
    {
        m_active = true;
        m_crtPose.m_rotation = rotation;
    }

    public void OnUpdate()
	{
		if(!m_active)
			return;

		if(m_isBlending)
		{
			if(m_blendDuration > 0.0f)
			{
				m_blendFactor += Time.deltaTime / m_blendDuration;
				if(m_blendFactor > 1.0f)
				{
					m_blendFactor = 1.0f;
					m_isBlending = false;
				}
			}
			else
			{
				m_blendFactor = 1.0f;
				m_isBlending = false;
			}

            float t;
            if (m_blendCurve != GFCurve.None)
                t = GFMath.CurvedDistribution(m_blendFactor, m_blendCurve);
            else if (m_blendCurveAnimation != null)
                t = m_blendCurveAnimation.Evaluate(m_blendFactor);
            else
                t = m_blendFactor;

            CameraPose pose = CameraPose.Lerp(m_oldPose, m_crtPose, t);
			pose.ToCamera(Camera.main);

			// blend over event
			if(!m_isBlending && m_blendCallback != null)
			{
				m_blendCallback();
			}
		}
		else
		{
			m_crtPose.ToCamera(Camera.main);
		}
	}

    public void ApplyCamera()
    {
        m_crtPose.ToCamera(Camera.main);
    }
}
