﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
using DG.Tweening;
using UnityEngine.Rendering.PostProcessing;

public class RocketKartController : MonoBehaviour {

	[Header ("Player Kart")]
	public Transform kartNormal;
	public LayerMask trackMask;
	public Transform[] kartWheels;
	public CharacterController controller;

	[Header ("Acceleration")]
	public float currentAcceleration = 0f;
	public float acceleration = 0.5f;
	public float decceleration = -1f;

	[Header ("Velocity")]
	public Vector3 velocity;
	public float currentVelocity = 0f;
	public float initialVelocity = 0f;
	public float maxVelocity = 3f;
	public float goalVelocity = 0f;

	[Header ("Time")]
	public float currentTime = 0f;

	[Header ("Wheels & Turning")]
	public Transform[] frontWheels, backWheels;
	public float turnValue;
	public bool accelerating = false;
	public float keySensitivity = 0.5f;

	[Header("Bools")]
	public bool drifting;

	[Header("Parameters")]
	public LayerMask layerMask;

	[Header("Model Parts")]
	public List<Transform> wheel;
	public Transform steeringWheel;
	public Rigidbody rBody;


	public float distance = 2.0f;
	public float smoothRatio = 0.2f;


	public Transform backLeft;
	public Transform backRight;
	public Transform frontLeft;
	public Transform frontRight;
	public RaycastHit lr;
	public RaycastHit rr;
	public RaycastHit lf;
	public RaycastHit rf;
	public Vector3 upDir;

	public float vertSpeed = 0f;
	public float curDir = 0f;
	public Vector3 curNormal = Vector3.up;
	public Collider kartCol;
	public bool onGround = false;


	void Start() {
		//kartAcceleration = (goalVelocity - currentKartVelocity) / (goalTime - currentTime);
	}

	void Update() {

		//GetRays ();
		//HandleTrackNormals ();

		if (Input.GetMouseButtonDown (0)) {
			initialVelocity = currentVelocity;
			currentTime = 0f;
			accelerating = true;
			currentAcceleration = acceleration;
		} else if (Input.GetMouseButtonUp (0)) {
			initialVelocity = currentVelocity;
			currentTime = 0f;
			accelerating = false;
			currentAcceleration = decceleration;
		}

		if (Input.GetMouseButton (0)) {
			goalVelocity = maxVelocity;
		} else {
			goalVelocity = 0f;
		}

		SpinWheels ();

		if ((currentVelocity < goalVelocity && accelerating) || (currentVelocity > goalVelocity && !accelerating)) {
			currentVelocity = initialVelocity + currentAcceleration * currentTime;
			currentTime += Time.deltaTime;
		} else {
			currentVelocity = goalVelocity;
		}

		if (currentVelocity != 0) {

		}

		//transform.up = kartNormal.up;

		//transform.eulerAngles += new Vector3 (0, KeyRotate (), 0f);

		//Normal Rotation
		//kartNormal.Rotate(0, transform.eulerAngles.y, 0);
		//HitTestWithRoad();
		OtherNormalCalculator ();
		//transform.Rotate (kartNormal.up, KeyRotate (), Space.Self);

		//rBody.velocity = 5 * currentVelocity * (transform.forward);
		//Debug.Log (rBody.velocity);
		//rBody.AddTorque (transform.up * KeyRotate (), ForceMode.Acceleration);

		//velocity = currentVelocity * (transform.forward);
		//controller.Move (velocity);


	}

	void SpinWheels () {

		frontWheels[0].transform.Rotate (new Vector3 (((180f * currentTime * currentVelocity) / (0.136f * Mathf.PI)), 0f, 0f));
		frontWheels[1].transform.Rotate (new Vector3 (((180f * currentTime * currentVelocity) / (0.136f * Mathf.PI)), 0f, 0f));
		backWheels[0].transform.Rotate (new Vector3 (((180f * currentTime * currentVelocity) / (0.16f * Mathf.PI)), 0f, 0f));
		backWheels[1].transform.Rotate (new Vector3 (((180f * currentTime * currentVelocity) / (0.16f * Mathf.PI)), 0f, 0f));

		//frontWheels[0].transform.localEulerAngles = new Vector3 (frontWheels[0].transform.eulerAngles.x, turnValue, frontWheels[0].transform.eulerAngles.z);
		//frontWheels[1].transform.localEulerAngles = new Vector3 (frontWheels[1].transform.eulerAngles.x, turnValue, frontWheels[1].transform.eulerAngles.z);
	}

	void HandleTrackNormals () {
		List<RaycastHit> rayHits = new List<RaycastHit>();
		RaycastHit groundCheck;

		if (Physics.Raycast (transform.position, -transform.up, out groundCheck, 1.0f, trackMask)) {
			rayHits.Add (groundCheck);
		}

		for (int i = 0; i < 4; i++) {
			RaycastHit tempHit;
			if (Physics.Raycast (kartWheels[i].position, -transform.up, out tempHit, 3.0f, trackMask)) {
				rayHits.Add (tempHit);
			}
		}

		Vector3 adjustedKartNormal = Vector3.zero;
		for (int r = 0; r < rayHits.Count; r++) {
			adjustedKartNormal += rayHits [r].normal;
		}
		if (rayHits.Count > 0) {
			adjustedKartNormal = adjustedKartNormal / (float)rayHits.Count;
		}

		kartNormal.up = adjustedKartNormal;

	}

	float KeyRotate () {
		float yAxis = 0f;

		if (Input.GetKey (KeyCode.A)) {
			yAxis -= 1 * keySensitivity;
		}
		if (Input.GetKey (KeyCode.D)) {
			yAxis += 1 * keySensitivity;
		}

		return yAxis;
	}

	void GetRays () {
		Debug.DrawRay (transform.position, -transform.up, Color.magenta);
		Debug.DrawRay (kartWheels[0].position, -transform.up, Color.magenta);
		Debug.DrawRay (kartWheels[1].position, -transform.up, Color.magenta);
		Debug.DrawRay (kartWheels[2].position, -transform.up, Color.magenta);
		Debug.DrawRay (kartWheels[3].position, -transform.up, Color.magenta);
	}


	void HitTestWithRoad () {
		Vector3 position = transform.position + transform.TransformDirection(Vector3.up) * 0.2f;
		Vector3 direction = transform.TransformDirection(Vector3.down);
		Ray ray = new Ray(position, direction);
		RaycastHit hit;

		Debug.DrawLine(ray.origin, ray.origin + ray.direction * distance, Color.red);
		//bool inGround = false;
		if (Physics.Raycast(ray, out hit, distance, trackMask)) {
			//inGround = true;
			this.transform.position = hit.point;

			Debug.DrawLine(hit.point, hit.point + hit.normal, Color.green);

			Vector3 current = position - hit.point;
			Vector3 target = hit.normal;
			Debug.DrawLine(hit.point, hit.point + current.normalized, Color.white);

			Quaternion targetQ = new Quaternion();
			//TODO: Using "velocity.normalize" instead of "Vector3(0, 1.0, 1.0)"
			Vector3 fPosition = transform.position + transform.TransformDirection(new Vector3(0f, 1.0f, 1.0f));
			Vector3 fDirection = transform.TransformDirection(Vector3.down);
			Ray fRay = new Ray(fPosition, fDirection);
			RaycastHit fHit;
			float fDistance = 2f;
			Debug.DrawLine(fRay.origin, fRay.origin + fRay.direction * fDistance, Color.cyan);
			if (Physics.Raycast(fRay, out fHit, fDistance, trackMask)) {
				Debug.DrawLine(fHit.point, fHit.point + fHit.normal * fDistance, Color.magenta);
				targetQ.SetLookRotation(fHit.point - transform.position, target);
			}
			if (targetQ == null) {
				targetQ.SetLookRotation(transform.TransformDirection(Vector3.forward), target);
			}
			this.gameObject.transform.rotation = Quaternion.Slerp(this.gameObject.transform.rotation, targetQ, smoothRatio);
		}

		//return inGround;
	}

	void OtherNormalCalculator () {
		List<RaycastHit> rayHits = new List<RaycastHit>();
		RaycastHit groundCheck;

		float turn = Input.GetAxis("Horizontal") * keySensitivity * 100 * Time.deltaTime;
		curDir = (curDir + turn) % 360; // rotate angle modulo 360 according to input

		if (Physics.Raycast (transform.position, -transform.up, out groundCheck, 3.0f, trackMask)) {
			rayHits.Add (groundCheck);
		}

		for (int i = 0; i < 4; i++) {
			RaycastHit tempHit;
			if (Physics.Raycast (kartWheels[i].position, -transform.up, out tempHit, 3.0f, trackMask)) {
				rayHits.Add (tempHit);
			}
		}

		Vector3 adjustedKartNormal = Vector3.zero;
		Vector3 gravity = Vector3.zero;
		for (int r = 0; r < rayHits.Count; r++) {
			adjustedKartNormal += rayHits [r].normal;
		}
		if (rayHits.Count > 0) {
			adjustedKartNormal = adjustedKartNormal / (float)rayHits.Count;
			vertSpeed = 0;
			RaycastHit testHit;
			if(Physics.Raycast (transform.position, -transform.up, out testHit, Mathf.Infinity, trackMask)){
				Debug.DrawLine (transform.position, testHit.point);
				Debug.Log (Vector3.Distance (transform.position, testHit.point));
			}

			if (Physics.Raycast (controller.transform.position, -transform.up, 1f, trackMask)) {
				vertSpeed = 0;
				Debug.Log ("we good");
			} else {
				vertSpeed += 9.8f * Time.deltaTime; // apply gravity
			}
		} else {
			vertSpeed += 9.8f * Time.deltaTime; // apply gravity
		}

		curNormal = Vector3.Lerp(curNormal, adjustedKartNormal, 60*Time.deltaTime);
		Quaternion grndTilt = Quaternion.FromToRotation(Vector3.up, curNormal);
		transform.rotation = grndTilt * Quaternion.Euler(0, curDir, 0);



		/*
		float turn = Input.GetAxis("Horizontal") * keySensitivity * 100 * Time.deltaTime;
		curDir = (curDir + turn) % 360; // rotate angle modulo 360 according to input
		RaycastHit hit;
		if (Physics.Raycast(transform.position, -curNormal, out hit, 3f, trackMask)){
			curNormal = Vector3.Lerp(curNormal, hit.normal, 4*Time.deltaTime);
			Quaternion grndTilt = Quaternion.FromToRotation(Vector3.up, curNormal);
			transform.rotation = grndTilt * Quaternion.Euler(0, curDir, 0);
		}
		*/
		Vector3 movDir;
		movDir = transform.forward*currentVelocity;//*Input.GetAxis("Vertical")*10;
		// moves the character in horizontal direction (gravity changed!)

		//movDir.y = vertSpeed; // keep the current vert speed
		gravity = -transform.up * vertSpeed;
		controller.Move(movDir+gravity/*Time.deltaTime*/);

	}
}
