﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class PlayerMoveController : MonoBehaviour {

	public const float BODY_HALF_SIZE_X = 0.6f;
	public const float BODY_HALF_SIZE_Z = 0.6f;
	public const float BODY_HALF_SIZE_Y = 1f;

	public enum XZ_MOVE_STATE{
		NONE,
		WALK,
		RUN
	}
	public enum Y_MOVE_STATE
	{
		NONE,
		JUMP,
		EXPLODE,
		FLY
	}

	private Transform trans;

	public Transform neckTrans;
	public Transform headTrans;
	public Transform leftArmTrans;
	public Transform rightArmTrans;
	public Transform leftLegTrans;
	public Transform rightLegTrans;

	private World world;

	private BodyAnimationController bodyAnimationController;

	//input
	private Vector3 mousePosDelta = new Vector3();
	private const float MOUSE_SPEED = 3f;
	private Dictionary<KeyCode,byte> dicKeyState;

	//
	private const float WALK_SPEED = 5f;
	private const float GRAVITY = -12f;
	private const float JUMP_SPEED = 5;
	private const float MAX_SPEED = 20;
	private float xspeed = 0f;
	private float yspeed = 0f;
	private float zspeed = 0f;
	private float gravityTimeClapsed = 0f;
	private float yJumpSpeed = 0f;
	//
	private XZ_MOVE_STATE xzMoveState;
	private Y_MOVE_STATE yMoveState;
	//
	void Awake()
	{
		world = GameObject.FindGameObjectWithTag ("GameController").GetComponent<World>();

		bodyAnimationController = GetComponent<BodyAnimationController>();

		dicKeyState = new Dictionary<KeyCode, byte> ();

		trans = GetComponent<Transform> ();
	}

	void Start () {
		xzMoveState = XZ_MOVE_STATE.NONE;
		yMoveState = Y_MOVE_STATE.NONE;
	}

	void Update () {

		handleInput ();

		handlePhysics ();

		bodyAnimationController.checkAnimationChange (xspeed,yspeed,zspeed);
	}
	

	//input handle-----------------------------------------------------------------------

	private void handleInput()
	{
		//turn head
		mousePosDelta.x = Input.GetAxis("Mouse X");
		mousePosDelta.y = Input.GetAxis("Mouse Y");

		if (mousePosDelta != Vector3.zero) {
			turnHead (mousePosDelta);
		}

		//move pos
		Vector3 dir = Vector3.zero;

		if(Input.GetKeyDown(KeyboardCommond.MOVE_FORWARD))
		{
			keyDown(KeyboardCommond.MOVE_FORWARD);
		}else if(Input.GetKeyDown(KeyboardCommond.MOVE_BACK))
		{
			keyDown(KeyboardCommond.MOVE_BACK);
		}
		if (Input.GetKeyDown (KeyboardCommond.MOVE_LEFT)) {
			keyDown (KeyboardCommond.MOVE_LEFT);
		} else if (Input.GetKeyDown (KeyboardCommond.MOVE_RIGHT)) {
			keyDown (KeyboardCommond.MOVE_RIGHT);
		}

		if(Input.GetKeyUp(KeyboardCommond.MOVE_FORWARD))
		{
			keyUp(KeyboardCommond.MOVE_FORWARD);
		}else if(Input.GetKeyUp(KeyboardCommond.MOVE_BACK))
		{
			keyUp(KeyboardCommond.MOVE_BACK);
		}
		if(Input.GetKeyUp(KeyboardCommond.MOVE_LEFT))
		{
			keyUp(KeyboardCommond.MOVE_LEFT);
		}else if(Input.GetKeyUp(KeyboardCommond.MOVE_RIGHT))
		{
			keyUp(KeyboardCommond.MOVE_RIGHT);
		}


		byte keyCommand;
		if (dicKeyState.TryGetValue (KeyboardCommond.MOVE_FORWARD, out keyCommand)) {
			if(keyCommand == 1)
			{
				dir += neckTrans.forward;
			}
		}
		if (dicKeyState.TryGetValue (KeyboardCommond.MOVE_BACK, out keyCommand)) {
			if(keyCommand == 1)
			{
				dir -= neckTrans.forward;
			}
		}
		if (dicKeyState.TryGetValue (KeyboardCommond.MOVE_LEFT, out keyCommand)) {
			if(keyCommand == 1)
			{
				dir -= neckTrans.right;
			}
		}
		if (dicKeyState.TryGetValue (KeyboardCommond.MOVE_RIGHT, out keyCommand)) {
			if(keyCommand == 1)
			{
				dir += neckTrans.right;
			}
		}

		dir.Normalize();

		if (Input.GetKeyDown (KeyboardCommond.MOVE_JUMP)) {
			dir.y = 1;
		}

		intentXZDir(dir);
		intentYDir (dir);
	}

	private void keyDown(KeyCode value)
	{
		if (!dicKeyState.ContainsKey (value)) {
			dicKeyState.Add (value, 1);
		} else {
			dicKeyState[value] = 1;
		}
	}

	private void keyUp(KeyCode value)
	{
		if (dicKeyState.ContainsKey (value)) {
			dicKeyState[value] = 0;
		}
	}

	//-------------------------------------simple Physics---------------------------------

	private void handlePhysics()
	{
		//check up down
		if (yMoveState == Y_MOVE_STATE.JUMP) {
			gravityTimeClapsed += Time.deltaTime;
			yspeed = yJumpSpeed + gravityTimeClapsed * GRAVITY;//瞬时y速度
			yspeed = Mathf.Clamp(yspeed,-MAX_SPEED,MAX_SPEED);
			//判断脚底
			if(yspeed <= 0)
			{
				if(world.isLand(new Vector3(trans.localPosition.x,trans.localPosition.y + yspeed*Time.deltaTime,trans.localPosition.z)))
				{
					yspeed = 0;
					yMoveState = Y_MOVE_STATE.NONE;
				}
			}else if(world.isLand(new Vector3(trans.localPosition.x,trans.localPosition.y + yspeed*Time.deltaTime + 1.5f * BODY_HALF_SIZE_Y,trans.localPosition.z)))
			{
				//判断头顶撞到阻挡
				yspeed = 0;
			}
		} else if (yMoveState == Y_MOVE_STATE.NONE) {
		}
		//check f b l r
		if (xzMoveState == XZ_MOVE_STATE.WALK) {
			if(yMoveState == Y_MOVE_STATE.NONE)
			{
				if (!world.isLand(new Vector3(trans.localPosition.x,trans.localPosition.y - 0.1f * CubeCell.BASIC_SIZE,trans.localPosition.z))) {
					yMoveState = Y_MOVE_STATE.JUMP;
					changeYSpeed(0);
				}
			}

			//检测行走方向碰撞
			if(xspeed != 0)
			{
				int dir = 1;
				if(xspeed < 0)
				{
					dir = -1;
				}
				//判断半身(1/4)和头顶(3/4)
				if (world.isLand(new Vector3(trans.localPosition.x + BODY_HALF_SIZE_X*dir + xspeed*Time.deltaTime,trans.localPosition.y + BODY_HALF_SIZE_Y * 0.5f,trans.localPosition.z))) {
					xspeed = 0;
				}else if(world.isLand(new Vector3(trans.localPosition.x + BODY_HALF_SIZE_X*dir + xspeed*Time.deltaTime,trans.localPosition.y + BODY_HALF_SIZE_Y * 1.5f,trans.localPosition.z)))
				{
					xspeed = 0;
				}
			}
			if(zspeed != 0)
			{
				int dir = 1;
				if(zspeed < 0)
				{
					dir = -1;
				}
				if (world.isLand(new Vector3(trans.localPosition.x,trans.localPosition.y + BODY_HALF_SIZE_Y * 0.5f,trans.localPosition.z +  BODY_HALF_SIZE_Z*dir + zspeed*Time.deltaTime))) {
					zspeed = 0;
				}else if(world.isLand(new Vector3(trans.localPosition.x,trans.localPosition.y + BODY_HALF_SIZE_Y * 1.5f,trans.localPosition.z +  BODY_HALF_SIZE_Z*dir + zspeed*Time.deltaTime)))
				{
					zspeed = 0;
				}
			}
		}

		//real speed
		trans.localPosition = new Vector3 (trans.localPosition.x + xspeed*Time.deltaTime, trans.localPosition.y + yspeed*Time.deltaTime, trans.localPosition.z + zspeed*Time.deltaTime);
	}

	//-----------------------------------------------------------------------------------

	//-----------------------------------------------------------------------------------

	private void intentXZDir(Vector3 dir)
	{
		bool isZeroDir = dir.x == 0 && dir.z == 0;

		switch (xzMoveState) {
		case XZ_MOVE_STATE.NONE:
			if(!isZeroDir)
			{
				xzMoveState = XZ_MOVE_STATE.WALK;
			}

			break;
		case XZ_MOVE_STATE.RUN:

			if(isZeroDir)
			{
				xzMoveState = XZ_MOVE_STATE.NONE;
			}

			break;
		case XZ_MOVE_STATE.WALK:

			if(isZeroDir)
			{
				xzMoveState = XZ_MOVE_STATE.NONE;
			}

			break;
		}

		if (!isZeroDir) {

			xspeed = dir.x * WALK_SPEED;
			zspeed = dir.z * WALK_SPEED;

			//body turn to neck dir,then neck turn back
			trans.LookAt (trans.position + neckTrans.forward);
			neckTrans.localRotation = Quaternion.identity;
		}else 
		{
			xspeed = 0;
			zspeed = 0;
		}
	}

	private void intentYDir(Vector3 dir)
	{
		if (dir.y != 0) {
			if(yMoveState == Y_MOVE_STATE.NONE)
			{
				if(dir.y > 0)
				{
					yMoveState = Y_MOVE_STATE.JUMP;
					changeYSpeed(JUMP_SPEED);
				}
			}else if(yMoveState == Y_MOVE_STATE.JUMP)
			{

			}
		}
	}

	private void changeYSpeed(float targetValue)
	{
		yspeed = targetValue;
		gravityTimeClapsed = 0;
		yJumpSpeed = yspeed;
	}

	//
	private void turnHead(Vector3 delta)
	{
		//rotate neck around y axis
		neckTrans.Rotate(Vector3.up * delta.x * MOUSE_SPEED);
		if (neckTrans.localEulerAngles.y > 60f && neckTrans.localEulerAngles.y < 300f) {
			//turn body
			trans.Rotate(trans.up * delta.x * MOUSE_SPEED);
		}

		//rotate head
		headTrans.Rotate(Vector3.right * -delta.y * MOUSE_SPEED);

		if (headTrans.localEulerAngles.x > 60f && headTrans.localEulerAngles.x < 90f) {
			headTrans.localEulerAngles = new Vector3(60f,headTrans.localRotation.eulerAngles.y,headTrans.localRotation.eulerAngles.z);
		} else if (headTrans.localEulerAngles.x >= 90f && headTrans.localEulerAngles.x < 300f) {
			headTrans.localEulerAngles = new Vector3(300f,headTrans.localEulerAngles.y,headTrans.localEulerAngles.z);
		}
	}
}