using UnityEngine;
using System.Collections;
using System;

public class CharacterControl : MonoBehaviour {

    // Cardinal points of the scene
    public enum DIRECTION
    {
        NORTH,
        EAST,
        SOUTH,
        WEST
    }
    
    // Rotation angle
    private int rotationAngle = 90;

    // leteral move speed of the character
    [SerializeField]
    private float speed = 1.0f;

    // The direction the character is lokking at
    private DIRECTION facing = DIRECTION.NORTH;

    // Position in the grid of the level
    // This is not the transform.position
    [HideInInspector]
    public int positionX = 0;
    [HideInInspector]
    public int positionY = 0;
    [HideInInspector]
    public int positionZ = 0;

    // Temporary variables saving the next target onto move
    private float worldPositionX = 0;
    private float worldPositionY = 0;
    private float worldPositionZ = 0;

    // The camera looking at the character
    private GameObject followingCamera;

    // Tells if the character is rotating
    [HideInInspector]
    public bool isRotating;

    // Tells if the character is jumping 
    public bool isJumping = false;

    // Tells if the character is moving
    public bool isMoving = false;

    // Set at angleRotation at the beginning of a rotation, 
    // it decreases at every move of the camera until the remaining nb of degrees if 0
    private int remainingRotatingNumberOfDegrees;

    // tells if the current or last move is left or not (so right)
    private bool movingDirectionIsLeft;

    // Like a mutex, it's used to lock some actions if one is currently doing
    public bool actionsLocked;

    // Tells if a jump have been started
    private float jumpElapsedTime = 0;



    private Animator m_anim;
    
    public float turningSpeed = 20f;

    [SerializeField]
    private GameObject m_model;

    public bool spawnLock = true;


    // Use this for initialization
    void Start () {
        movingDirectionIsLeft = true;
        actionsLocked = false;
        followingCamera = GameObject.FindGameObjectWithTag("MainCamera");
        m_anim = m_model.GetComponent<Animator>();


    }
	
	void Update ()
    {

        if (spawnLock)
        {
            return;
        }
       
        // Check if an input have been detected
        bool changedDirection = DetectInputs();


        m_anim.SetBool("isRunning", isMoving);
        m_anim.SetBool("isJumping", isJumping);
        if (changedDirection)
        {
            m_model.transform.Rotate(new Vector3(0, 180, 0));
        }


        // If a move have been started, the character move
        if (isMoving)
        {
            Move(movingDirectionIsLeft);
        }

        // If a jump have been started, the character jump
        if (isJumping)
        {
            // Jump only at first frame of the move
            if (jumpElapsedTime == 0)
            {
                Jump();
                jumpElapsedTime += Time.deltaTime;
            }
        }

        // If the camera need to rotate, do the rotation
        if (remainingRotatingNumberOfDegrees > 0)
        {
            remainingRotatingNumberOfDegrees--;
            Rotate(movingDirectionIsLeft);
        }
        
        // If no action is currently done, update the height level
        if (!actionsLocked)
        {
            positionY = Mathf.CeilToInt(transform.position.y) -1 ;
        }



    }

    // Detects if a custom input have been detected
    private bool DetectInputs()
    {
        bool directionChanged = movingDirectionIsLeft;

        // put here all possible input that active non competitive actions
        
        if (actionsLocked)
            return false;


        // put here all possible input that active competitive actions


        // Check for going to the left (and jump by the way)
        if ((Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.Q)))
        {
            movingDirectionIsLeft = true;

            if(Input.GetKey(KeyCode.Space) && CanJump())
            {
                isJumping = true;
                actionsLocked = true;
                FindTargetPosition();
                isMoving = true;
            }

            else if (CanWalk())
            {
                GetComponent<AudioSource>().Play();
                FindTargetPosition();
                actionsLocked = true;
                isMoving = true;
            }
        }

        // Check for going to the right (and jump by the way)
        else if ((Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D)))
        {
            movingDirectionIsLeft = false;

            if (Input.GetKey(KeyCode.Space) && CanJump())
            {
                isJumping = true;
                actionsLocked = true;
                FindTargetPosition();
                isMoving = true;
            }
            else if (CanWalk())
            {
                FindTargetPosition();
                actionsLocked = true;
                isMoving = true;
                GetComponent<AudioSource>().Play();
            }
        }

        // Check for rotate the camera
        else if (Input.GetKey(KeyCode.A))
        {
            actionsLocked = true;
            movingDirectionIsLeft = true;
            isRotating = true;
            remainingRotatingNumberOfDegrees = Mathf.RoundToInt(speed);
            SoundDispenser.instance.rotationLeft.Play();
        }

        // there too
        else if (Input.GetKey(KeyCode.E))
        {
            actionsLocked = true;
            movingDirectionIsLeft = false;
            isRotating = false;
            remainingRotatingNumberOfDegrees = Mathf.RoundToInt(speed);
            SoundDispenser.instance.rotationRight.Play();
        }


        // Check if the character can use an object
        else if (Input.GetKeyDown(KeyCode.Return))
        {
            Debug.Log("Tentative d'utilisation");
            DoAction();
        }

        return (directionChanged != movingDirectionIsLeft);

    }

    // Check if the facing block is an interactive block
    private void DoAction()
    {
        Vector3 target = IsLookingAt();
        GameObject block = LevelReader.instance.GetBlockAt(Mathf.RoundToInt(target.x), Mathf.RoundToInt(target.y)+1, Mathf.RoundToInt(target.z));

        if(block!= null)
        {
            InteractiveElement[] interactiveElements = block.GetComponentsInChildren<InteractiveElement>();
            if (interactiveElements.Length>0)
            {
                Debug.Log("Action!");

            }
        }

    }


    // Try to find the next place to reach
    private void FindTargetPosition()
    {
        float x = transform.position.x;
        float y = transform.position.y + (isJumping ? 1 : 0);
        float z = transform.position.z;
        switch (facing)
        {
            case DIRECTION.NORTH:
                if (movingDirectionIsLeft)
                    x--;
                else
                    x++;
                break;
            case DIRECTION.EAST:
                if (movingDirectionIsLeft)
                    z++;
                else
                    z--;
                break;
            case DIRECTION.SOUTH:
                if (movingDirectionIsLeft)
                    x++;
                else
                    x--;
                break;
            case DIRECTION.WEST:
                if (movingDirectionIsLeft)
                    z--;
                else
                    z++;
                break;
        }
        worldPositionX = Mathf.Round(x);
        worldPositionY = y;
        worldPositionZ = Mathf.Round(z);
    }

    // Move the character
    private void Move(bool left)
    {
        float factor = speed * 0.1f * Time.deltaTime;
        Vector3 target = new Vector3(worldPositionX, worldPositionY, worldPositionZ);
        gameObject.transform.position = Vector3.MoveTowards(gameObject.transform.position, target, factor);

        if (Vector3.Distance(transform.position, target) < 0.005)
        {
            actionsLocked = false;
            isMoving = false;

            if(isJumping)
            {
                positionY++;
                jumpElapsedTime = 0;
            }

            isJumping = false;

            gameObject.transform.position = target;

            int x = positionX;
            int y = positionY;
            int z = positionZ;
            switch (facing)
            {
                case DIRECTION.NORTH:
                    if (left)
                        x--;
                    else
                        x++;
                    break;
                case DIRECTION.EAST:
                    if (left)
                        z++;
                    else
                        z--;
                    break;
                case DIRECTION.SOUTH:
                    if (left)
                        x++;
                    else
                        x--;
                    break;
                case DIRECTION.WEST:
                    if (left)
                        z--;
                    else
                        z++;
                    break;
            }
            positionX = x;
            positionY = y;
            positionZ = z;

            //Debug.Log(transform.position.x + " " + transform.position.y + " " + transform.position.z);
            //Debug.Log(positionX + " " + positionY + " " + positionZ);
        }

    }


    //Rotate the camera
    private void Rotate(bool left)
    {
        gameObject.transform.Rotate(Vector3.up * (left ? 1 : -1) * rotationAngle/speed );

        followingCamera.transform.RotateAround(transform.position,Vector3.up, (left ? 1 : -1) * rotationAngle / speed);

        if (remainingRotatingNumberOfDegrees == 0)
        {
            actionsLocked = false;
            isRotating = false;

            if (!left)
                facing--;
            else
                facing++;

            if (facing < DIRECTION.NORTH)
                facing = DIRECTION.WEST;
            else
                if (facing > DIRECTION.WEST)
                facing = DIRECTION.NORTH;

            IsLookingAt();

            LevelReader.instance.FadeOut(facing, new Vector3(positionX, positionY, positionZ));
        }
    }

    // Make the character jump
    private void Jump()
    {
        
        GetComponent<Rigidbody>().AddForce(Vector3.up * 8f, ForceMode.Impulse);
        SoundDispenser.instance.jump.Play();

    }

    // Check if the character can walk on the targeted block
    private bool CanWalk()
    {
        if (GetComponent<Rigidbody>().velocity.y < -0.5f)
            return false;

        int x = positionX;
        int y = positionY;
        int z = positionZ;

        switch (facing)
        {
            case DIRECTION.NORTH:
                if (movingDirectionIsLeft)
                    x--;
                else
                    x++;
                break;
            case DIRECTION.EAST:
                if (movingDirectionIsLeft)
                    z++;
                else
                    z--;
                break;
            case DIRECTION.SOUTH:
                if (movingDirectionIsLeft)
                    x++;
                else
                    x--;
                break;
            case DIRECTION.WEST:
                if (movingDirectionIsLeft)
                    z--;
                else
                    z++;
                break;
        }

        bool result = LevelReader.instance.CanWalkOn(x,y,z);

        return result;
    }

    // Check if the character can jump on the targeted block
    private bool CanJump()
    {


        float x = positionX;
        float y = positionY;
        float z = positionZ;

        switch (facing)
        {
            case DIRECTION.NORTH:
                if (movingDirectionIsLeft)
                    x--;
                else
                    x++;
                break;
            case DIRECTION.EAST:
                if (movingDirectionIsLeft)
                    z++;
                else
                    z--;
                break;
            case DIRECTION.SOUTH:
                if (movingDirectionIsLeft)
                    x++;
                else
                    x--;
                break;
            case DIRECTION.WEST:
                if (movingDirectionIsLeft)
                    z--;
                else
                    z++;
                break;
        }

        if (LevelReader.instance.CanWalkOn(Mathf.RoundToInt(x), Mathf.RoundToInt(y + 1), Mathf.RoundToInt(z))
             && LevelReader.instance.isEmpty(positionX, positionY + 1, positionZ))
        {
            return true;
        }
        else
            return false;

        
    }

    // Returns the grid position of the block that the character is looking
    public Vector3 IsLookingAt()
    {
        Vector3 result = new Vector3();

        result.x = positionX + ((facing == DIRECTION.EAST) ? 1 : 0) - ((facing == DIRECTION.WEST) ? 1 : 0);
        result.z = positionZ + ((facing == DIRECTION.NORTH) ? 1 : 0) - ((facing == DIRECTION.SOUTH) ? 1 : 0);
        
        return result;

    }

    public void OnCollisionEnter(Collision other)
    {
        spawnLock = false;
    }

    public void spawnConfig()
    {
        positionX = Mathf.RoundToInt(transform.position.x);
        positionY = Mathf.RoundToInt(transform.position.y);
        positionZ = Mathf.RoundToInt(transform.position.z);
    }

}
