﻿using UnityEngine;
using System.Collections;

[RequireComponent (typeof(CharacterController))]
public class RelativeMovement : MonoBehaviour {

    [SerializeField]
    private Transform target;

    public float moveSpeed = 6.0f;
    public float rotSpeed = 15.0f;

    public float jumpSpeed = 15.0f;
    public float gravity = -9.8f;
    public float terminalVelocity = -20.0f;
    public float minFall = -1.5f;

    //[SerializeField]
    //private WeaponStates weaponState;

    public float pushForce = 3.0f; // Amount of force to apply.

    private float vertSpeed;
    private ControllerColliderHit contact;

    private CharacterController charController;
    private Animator animator;
    

    void Awake()
    {
        charController = GetComponent<CharacterController>();
        animator = GetComponent<Animator>();
    }

    // Use this for initialization
    void Start () {
        //Managers.Player.WeaponState = WeaponState;
        vertSpeed = minFall;

        // animator.SetBool("NonCombat", true);
    }
	
	// Update is called once per frame
	void Update ()
    {
        // Get the key pressed
        string keyPressed = Input.inputString;
        
        // Check that a key was pressed and it was a digit (numeric) key
        if (!string.IsNullOrEmpty(keyPressed) && char.IsDigit(keyPressed, 0)) {
            // Convert it to the number
            int numberKey = int.Parse(keyPressed);

            // Check that it is a valid weapon state and if yes, change the character's weapon
            if (System.Enum.IsDefined(typeof(WeaponStates), numberKey))
            {
                ChangeWeapon(numberKey);
            }
        }
        

        // start with zero and add movement components progressively
        Vector3 movement = Vector3.zero;

        // x z movement transformed relative to target
        float horInput = Input.GetAxis("Horizontal");
        float vertInput = Input.GetAxis("Vertical");

        // Only handle movement if the arrow keys are pressed.
        if (horInput != 0 || vertInput != 0)
        {
            animator.SetBool("Idling", false);
            //if (Input.GetMouseButton(0))
            //{
            //    if(!IsAttacking)
            //    {
            //        // Ray ray = 
            //        // movementTargetPosition
            //        if (false)
            //        {
                        
            //            //isAttacking = true;
            //        }
            //    }
            //}


            // Get the keyboard input for movement and apply movement speed
            movement.x = horInput * moveSpeed;
            movement.z = vertInput * moveSpeed;

            // limit the diagonal movement to the same speed as movement along an axis
            movement = Vector3.ClampMagnitude(movement, moveSpeed);

            Quaternion tmp = target.rotation;   // read the current rotation of the camera to restore
            target.eulerAngles = new Vector3(0, target.eulerAngles.y, 0); // apply the rotation around y (turning)
            movement = target.TransformDirection(movement);
            target.rotation = tmp;

            // face movement direction
            //transform.rotation = Quaternion.LookRotation(movement);

            // store rotated direction in variable
            Quaternion direction = Quaternion.LookRotation(movement);
            // interpolate (transition) the change gradually
            transform.rotation = Quaternion.Lerp(transform.rotation, direction, rotSpeed * Time.deltaTime);
        }
        else
        {
            animator.SetBool("Idling", true);
        }

        // raycast down to address steep slopes and dropoff edge
        bool hitGround = false;
        RaycastHit hit;

        // Check if the player is falling
        if (vertSpeed < 0 && Physics.Raycast(transform.position, Vector3.down, out hit))
        {
            // The distance to check against (extend slightly beyond the bottom of the capsule
            // (The height of the character + radius of capsule)  divided by 2 (as ray is being cast from the centre)
            // Make it 1.9 to check for a little beyond the character to account for inaccuracies in the raycasting
            float check = (charController.height + charController.radius) / 1.9f;
            hitGround = hit.distance <= check;  // to be sure check slightly beyond bottom of capsule
        }

        // y movement: possibly jump impulse up, always accel down
        // could charController.isGrounded instead, but then cannot workaround dropoff edge
        if (hitGround)
        {
            if (Input.GetButtonDown("Jump"))
            {
                vertSpeed = jumpSpeed;
            }
            else
            {
                // minFall set to negative value to implement some downward force
                vertSpeed = minFall;
            }
        }
        else
        {
            // apply gravity until terminal velocity is reached
            vertSpeed += gravity * 5 * Time.deltaTime;
            if (vertSpeed < terminalVelocity)
            {
                vertSpeed = terminalVelocity;
            }

            // Step 4
            if (contact != null)
            {   // not right at level start
                //animator.SetBool("Jumping", true);
            }

            //// workaround for standing on dropoff edge
            // Raycasting didn't detect the ground, but the capsule is touching the ground
            if (charController.isGrounded)
            {
                // Respond differently depending on direction character is facing
                // Is character facing the contact point/not?

                // The collision data contains a normal vector of the surface hit
                // Dot product returns 1 if they point in exactly the same direction, -1 if they point in completely opposite directions and zero if the vectors are perpendicular.
                if (Vector3.Dot(movement, contact.normal) < 0)
                {
                    // replace the movement
                    movement = contact.normal * moveSpeed;
                }
                else
                {
                    // continue the movment - add to previous movement to keep forward momentum
                    movement += contact.normal * moveSpeed;
                }
            }
        }

        // Step 2
        movement.y = vertSpeed;

        movement *= Time.deltaTime;
        charController.Move(movement);
    }

    // called when the controller hits a collider while performing a Move.
    // can be used to push objects when they collide with the character.
    void OnControllerColliderHit(ControllerColliderHit hit)
    {
        // Store the collision data in the callback when a collision is detected
        contact = hit;

        // Get the rigid body attached to the game object collided with
        // Check that it is NOT kinematic as kinematic bodies will not be affected by physics (forces, collisions, etc.)
        Rigidbody body = hit.collider.attachedRigidbody;
        if (body != null && !body.isKinematic)
        {
            // Applies the force to the rigid body, adding an instant velocity change to the body.
            // do not modify the velocity directly as this can result in unrealistic behaviour
            //body.velocity = hit.moveDirection * pushForce;
            body.AddForce((hit.moveDirection * pushForce), ForceMode.VelocityChange);
        }
    }

    

    void ChangeWeapon(int weaponNumber)
    {
        WeaponStates newWeaponState = (WeaponStates) weaponNumber;
        if (newWeaponState != Managers.Player.WeaponState)
        {
            //Messenger<WeaponStates, string>.Broadcast(GameEvent.WEAPON_STATE_CHANGED, newWeaponState, gameObject.tag);
        }
    }
}
