using UnityEngine;
using System.Collections;

public class Character_Motor : MonoBehaviour
{
	// Character
	protected Transform m_CharacterModel;
	protected CharacterController m_CharController;
	
	// Animation manager
	protected Character_Animator m_CharacterAnimator;
	
	// Physic manager
	protected Character_Physic m_CharacterPhysic;
	
	// Movement
	protected Vector3 m_MoveVectorInCam;
	protected Vector3 m_MoveVector;
	public Vector3 MoveVector
	{
		get { return m_MoveVector; }
		set { m_MoveVector = value; }
	}
	// Walk/Run
	protected float m_WalkSpeed;
	public float Speed
	{
		get { return m_WalkSpeed; }
	}
	protected float m_RunSpeed;
    protected bool m_Run;
    // Jump
    protected float m_JumpHeight;
	protected float m_JumpSpeed;
	protected float m_FallSpeed;
    protected bool m_Jump;
	// Vertical velocity
	public float m_MaxVelocity = 0.5f;
	protected float m_VerticalVelocity;
	public float VerticalVelocity
	{
		get { return m_VerticalVelocity; }
		set { m_VerticalVelocity = value; }
	}
    // On movable platforms
    private bool m_IsOnPlatform;
    private bool m_CanMoveOnPlatform;
    private Platform m_Platform;
    // Must move somewhere
    private bool m_MoveSomewhere;
    private Vector3 m_MoveInitialPos;
    private Vector3 m_MoveFinalPos;
    private Timer m_MoveTimer;
    // Grounded
	public bool isGrounded
	{
		get { return m_CharController.isGrounded; }
	}
	// Initial vector null -> for ground tests by the unity Character controller
	protected Vector3 m_VectorNull;
	public Vector3 VectorNull
	{
		get { return m_VectorNull; }
	}

    //public void Character_Motor(Transform character, CharacterController controller, Character_Animator animator, Character_Physic physic)
	public virtual void Initialize(Transform character, CharacterController controller, Character_Animator animator, Character_Physic physic)
	{
		m_CharacterModel = character;
		m_CharacterAnimator = animator;
		m_CharacterPhysic = physic;
		m_CharController = controller;
		m_MoveVector = Vector3.zero;
		m_Run = false;
		m_WalkSpeed = 3f;
		m_RunSpeed = 7f;
        m_JumpHeight = 1.2f;
		m_JumpSpeed = 0.4f;
        m_Jump = false;
		m_FallSpeed = 0.5f;
		m_VerticalVelocity = 0f;
		m_VectorNull = new Vector3(0f,-0.01f,0f);
        m_IsOnPlatform = false;
        m_CanMoveOnPlatform = false;
        m_MoveSomewhere = false;
        m_MoveTimer = new Timer();
	}
	
	#region Update
	public virtual void UpdateMotor()
	{
		ProcessMotion();
	}
	
	protected virtual void ProcessMotion()
	{
        if (m_MoveSomewhere)
        {
            if (!m_MoveTimer.UpdateTime())
            {
                m_MoveVector = Vector3.Lerp(m_MoveInitialPos, m_MoveFinalPos, m_MoveTimer.GetHermiteProgress()) - m_CharacterModel.position;
            }
            else
            {
                m_MoveVector = m_MoveFinalPos - m_CharacterModel.position;
                m_MoveSomewhere = false;
            }
        }

        m_CharacterAnimator.SetSpeed(Mathf.Abs(m_MoveVector.x));

		// Look for character orientation
		CharacterOrientation();
		
		// Normalize MoveVector if its magnitude > 1
		if(m_MoveVector.magnitude > 1)
		{
			m_MoveVector = Vector3.Normalize(m_MoveVector);
		}

        if (!m_MoveSomewhere)
        {
            if (!m_Run) m_MoveVector *= m_WalkSpeed * Time.deltaTime;
            else m_MoveVector *= m_RunSpeed * Time.deltaTime;
        }
		
		ApplyGravity();
		
		ApplyMovement();
	}
	
	// Orientation
	protected void CharacterOrientation()
	{
		Vector3 target = m_MoveVector;
		target.y = 0f;
		target += m_CharacterModel.position;
		m_CharacterModel.LookAt(target);
	}
	
	// Gravity
	protected void ApplyGravity()
	{
		// Reapply vertical velocity
        if (m_IsOnPlatform)
        {
            if (!m_CanMoveOnPlatform)
            {
                m_MoveVector = Vector3.zero;
            }
            m_MoveVector += m_Platform.DeltaMove;
            return;
        }
        
        m_MoveVector.Set(m_MoveVector.x, m_VerticalVelocity, m_MoveVector.z);

        if (m_Jump)
            return;

		// Character's falling
		else if(!m_CharController.isGrounded)
		{
			if(m_MoveVector.y > -m_MaxVelocity)
			{
				m_MoveVector = new Vector3(m_MoveVector.x, m_MoveVector.y-m_FallSpeed*Time.deltaTime, m_MoveVector.z);
			}
		}
		else if(m_MoveVector.y < 0f)
		{
			m_MoveVector = new Vector3(m_MoveVector.x, -0.01f, m_MoveVector.z);
		}
	}
	
	protected void ApplyMovement()
	{
		Vector3 tmp = m_MoveVector;
		tmp.y = 0f;
		m_CharController.Move(m_MoveVector);
	}
	
	// Save the velocity
	public void ReinitMoveVector()
	{
		m_VerticalVelocity = m_MoveVector.y;
		m_MoveVector = Vector3.zero;
	}
	#endregion
	
	#region Tests
	#endregion

    #region State events
    public virtual void IsOnPlatform(bool isOn, bool canMove, Platform e)
    {
        m_IsOnPlatform = isOn;
        m_CanMoveOnPlatform = canMove;
        if (m_IsOnPlatform) m_Platform = e;
    }

    public virtual void MustGoSomewhere(Vector3 position, float duration)
    {
        m_MoveSomewhere = true;
        m_MoveInitialPos = m_CharacterModel.position;
        m_MoveFinalPos = position;
        m_MoveTimer.Restart(duration);
    }
    #endregion

    #region Inputs events
    public virtual void Jump()
	{
        if (m_CharController.isGrounded && !m_Jump)
		{
            m_VerticalVelocity = 0;
            m_Jump = true;
            StartCoroutine("C_Jump");
		}
	}

    private IEnumerator C_Jump()
    {
        // We use the cosinus function
        float timer = 0f;
        float lastHeight = 0f;
        float jumpSpeed2PI2 = Mathf.PI / 2f / m_JumpSpeed;
		bool wallOn = false;
        do
        {
            m_VerticalVelocity = (Mathf.Sin(timer * jumpSpeed2PI2) * m_JumpHeight) - lastHeight;
            m_MoveVector.y = m_VerticalVelocity;
            lastHeight += m_VerticalVelocity;
            timer += Time.deltaTime;
			wallOn = m_CharacterPhysic.WallOnHead();
            yield return 0;
        }
        while (timer <= m_JumpSpeed && !wallOn);
        //while (!m_CharController.isGrounded && timer <= m_JumpSpeed && !wallOn);

        m_VerticalVelocity = -0.01f;
        m_MoveVector.y = m_VerticalVelocity;
        m_Jump = false;
    }
	
	public void Run(bool run)
	{
		m_Run = run;
	}
	#endregion
}