using UnityEngine;
using System;
using ScrambleKit.Hits;
using ScrambleKit.FX;

public class Tire : MonoBehaviour {
    public Transform SkidPrefab;

	public Transform Chassis;
	public bool Turning = false;
	public bool Powered = true;
	public bool Braking = true;
	public float Resistance=0.1f;
	public float TractionLimit=20;
    public string DEBUG;
		
	public float CurrentPower; 
	public float CurrentBraking;
	public float CurrentAsk; 
	public float CurrentGive;
	
	public static float ExtraTractionFactor = 0.6f;
    private Rigidbody2D vr2;

    private float stunnedUntil;
    private float lastSkidSound;

	void Start()
    {
        vr2 = Chassis.GetComponent<Rigidbody2D>();
    }

    void TakeHit(HitInfo hit)
    {
        stunnedUntil = Mathf.Max(stunnedUntil, Time.fixedTime + hit.StunTime);
    }

    public void FixedUpdate () {
        if (Time.fixedTime < stunnedUntil)
            return;

        //DEBUG = "" + vr2.velocity.magnitude;
        Vector2 totalForce = Vector2.zero;
		totalForce += GetSlideResistance();
		totalForce += GetPower();
		totalForce += GetBraking();
		totalForce += GetRollingResistance();
		
		totalForce = HandleTraction(totalForce);

        //if (totalForce.magnitude > 0)
        //    Debug.DrawLine(transform.position, transform.position + (Vector3)totalForce, Color.white);

        Vector2 instantForce = vr2.mass * totalForce;// / Time.fixedDeltaTime;

		//Debug.Log ("Tire applying force " + totalForce);
        vr2.AddForceAtPosition(instantForce, transform.position);
	}

    private Vector2 GetSlideResistance()
	{
        Vector2 vel = vr2.GetPointVelocity(transform.position);
		float dot = Vector2.Dot(hforward(), vel);
        Vector2 projection = dot * hforward();  // the part of vel that is okay b/c of rolling
        Vector2 stick = -(vel - projection); // force from fighting the part of vel that's sideways for the tire

        if (stick.magnitude > 0)
            Debug.DrawLine(transform.position, transform.position + (Vector3)stick, Color.cyan);

        return stick;
	}

    private Vector2 GetPower()
	{
        Vector2 vForce = hforward().normalized * (CurrentPower);

        if (vForce.magnitude > 0)
            Debug.DrawLine(transform.position, transform.position + (Vector3)vForce, Color.green);

        return vForce;
	}

    private Vector2 GetBraking()
	{
        Vector2 vel = vr2.GetPointVelocity(transform.position);
        // Vector2 vForce = vel.normalized * (-CurrentBraking);	
		
		//// make sure the braking isn't greater than the remaining speed, else we'll overshoot zero and twitch badly
		//// TODO is this hack needed because of rolling resistance?
		//if(vForce.magnitude > vel.magnitude)
		//	vForce = -vel * 0.95f;

        Vector2 vForce = vel.normalized * Mathf.Max(-CurrentBraking, -5*vel.magnitude);
        DEBUG = vel + ", " + vForce;

        if (vForce.magnitude > 0)
            Debug.DrawLine(transform.position, transform.position + (Vector3)vForce, Color.yellow);

        return vForce;
	}

    private Vector2 GetRollingResistance()
	{
		if(CurrentBraking != 0f)       // bit of a cheat - I skip RR if we're braking, simplifies preventing of overshooting a stop
            return Vector2.zero;

        Vector2 vel = vr2.GetPointVelocity(transform.position);
        Vector2 rolling = Vector2.Dot(hforward(), vel) * hforward();  // the part of vel that is okay b/c of rolling

        //Vector2 vForce = rolling.normalized * (-Resistance * vel.magnitude);
        Vector2 vForce = rolling.normalized * (-Resistance * rolling.magnitude);

        if (vForce.magnitude > 0)
            Debug.DrawLine(transform.position, transform.position + (Vector3)vForce, Color.magenta);

        return vForce;
	}

    private Vector2 HandleTraction(Vector2 force)
	{
		//Debug.Log(" force.magnitude" + force.magnitude);
		// check if we've exceeded our TractionLimit and go into a skid if so
		if(force.magnitude > TractionLimit)
		{
			float extraAsk = force.magnitude - TractionLimit;
			float extra1 = (float)Math.Min (TractionLimit/10, Math.Max (extraAsk, 0));  // ask(100), TL 80, 1=8
			float extra2 = (float)Math.Min (3*TractionLimit/10, Math.Max (extraAsk - TractionLimit/10, 0)); // (92)72, 2 = 24
			float extra3 = (float)Math.Min (3*TractionLimit/10, Math.Max (extraAsk - 4*TractionLimit/10, 0)); // (68)48, 3 = 24
			float extra4 = (float)Math.Min (3*TractionLimit/10, Math.Max (extraAsk - 7*TractionLimit/10, 0));  // (44)24, 4 = 24
			float extra5 = (float)Math.Max (extraAsk-TractionLimit, 0);  // (20) 0, 5=(20)

            float extraGive = 0.5f*extra1 + 0.3f*extra2 + 0.15f*extra3 + 0.07f*extra4 + 0.02f*extra5;

            DropSkid(Mathf.Min(1, extraAsk / TractionLimit));

			Vector3 skidForce = force.normalized * (extraGive + TractionLimit);

            float skidStrength = Mathf.Min(1, extraAsk / TractionLimit);
            if (skidStrength > 0.25f && Time.time - lastSkidSound > 1f)
            {
                SoundFX.PlaySound("Skid", skidStrength);
                lastSkidSound = Time.time;
            }

            CurrentAsk = force.magnitude;
			CurrentGive = skidForce.magnitude;

            if (skidForce.magnitude > 0)
                Debug.DrawLine(transform.position, transform.position + (Vector3)(force.normalized * (extraAsk-extraGive)), Color.red);

			return skidForce;
		}
		else
		{
			CurrentAsk = CurrentGive = force.magnitude;
			return force;
		}
    }

    private void DropSkid(float weight)
    {
        Transform skid = Instantiate(SkidPrefab);
        //skid.localScale = weight * Vector3.one;
        skid.position = transform.position;
        skid.eulerAngles = transform.eulerAngles;

        skid.GetComponent<SpriteRenderer>().color = new Color(1, 1, 1, weight);
    }
	
    private Vector3 hforward()
	{
        
		return transform.up;
	}
}
