using UnityEngine;
using System.Collections;

public class Chef : MonoBehaviour 
{
    public Rigidbody myRigidbody;
    public AIMode aiMode = AIMode.Pursuing;
    public float turnSpeed = 3.5f;
    public float walkSpeed = 10f;
    public float maxSpeed = 2f;
    public float dignity = 3f;
    public float jumpForce = 10f;
    public bool wasKilled;
    public float baseThrowStrength = 20f;
    public GameObject[] foodPrefabs;
    public float throwCooldown = 5f;
    public int scoreLevel = 3;
    public string[] retorts;

    public float walkRaw;
    public float turnRaw;

    public float heading;
    private bool canJump;
    private PlayerController playerController;
    private float throwTimer;
    public Grabber grabber;
    public float launchPitch = 25f;
    private AudioSource footstepAudio;

    private bool CanJump()
    {
        //return Physics.Raycast(new Ray(transform.position + new Vector3(0f, 0.01f, 0f), Vector3.down), 0.021f, Layers.GetMask(Layers.Floor) | Layers.GetMask(Layers.Furnishing));
        return Physics.SphereCast(new Ray(transform.position + new Vector3(0f, 0.36f, 0f), Vector3.down), 0.35f, 0.021f, Layers.GetMask(Layers.Floor) | Layers.GetMask(Layers.Furnishing));
    }

    private void LaunchProjectile()
    {
        if (grabber.heldRigidbody != null)
        {
            var launchAngle = Quaternion.Euler(launchPitch, heading, 0f);
            var launchForce = launchAngle * new Vector3(0f, 0f, baseThrowStrength) + myRigidbody.velocity;
            grabber.LaunchHeld(launchForce);
            //Debug.Log(launchForce + ":" + myRigidbody.velocity);
        }
    }

	private void FleeingUpdate()
    {
        var targetPosition = new Vector3(0f, 0f, -12.5f);
        var desiredHeading = VectorXZToAngle(targetPosition - transform.position);

        walkRaw = 1f;
        turnRaw = Mathf.Sign(desiredHeading - heading);

    }

    private void PursuingUpdate()
    {
        var targetPosition = playerController.transform.position;
        throwTimer -= Time.deltaTime;
        if (throwTimer < 0f && grabber.heldRigidbody != null && (targetPosition - transform.position).sqrMagnitude <= 100f)
        {
            //Debug.Log("Throw now");
            aiMode = AIMode.Throwing;
            throwTimer = 1f;
        }
        
        var desiredHeading = VectorXZToAngle(targetPosition - transform.position);
        //Debug.Log(desiredHeading);

        walkRaw = 1f;
        turnRaw = Mathf.Sign(desiredHeading - heading);

    }

    private void ThrowingUpdate()
    {
        var targetPosition = playerController.transform.position;
        if((targetPosition - transform.position).sqrMagnitude > 100f || grabber.heldRigidbody == null)
        {
            aiMode = AIMode.Pursuing;
        }

        throwTimer -= Time.deltaTime;
        var desiredHeading = VectorXZToAngle(targetPosition - transform.position);

        walkRaw = 0f;
        turnRaw = Mathf.Sign(desiredHeading - heading);

        //Debug.Log(desiredHeading - heading);
        if(Mathf.Abs(desiredHeading - heading) < 1f && throwTimer < 0f)
        {
            LaunchProjectile();
            throwTimer = throwCooldown;
            aiMode = AIMode.Pursuing;
        }
    }

    private void MovementUpdate()
    {
        if (Mathf.Abs(turnRaw) > 0.0f)
        {
            heading += turnSpeed * turnRaw;
        }

        heading = ClampAngle(heading, -360f, 360f);
        Quaternion xQuaternion = Quaternion.AngleAxis(heading, Vector3.up);
        transform.localRotation = xQuaternion;
    }

    private void Jump()
    {
        myRigidbody.AddForce(new Vector3(0f, jumpForce, 0f), ForceMode.VelocityChange);
    }

    void Awake()
    {
        footstepAudio = GetComponent<AudioSource>();
        playerController = FindObjectOfType<PlayerController>();
        grabber = GetComponentInChildren<Grabber>();
    }

    void Start()
    {
        heading = transform.rotation.eulerAngles.y;
    }

	void Update () 
    {
        if(grabber.heldRigidbody == null)
        {
            var gameObject = Instantiate(foodPrefabs[Random.Range(0, foodPrefabs.Length)], grabber.holdTransform.position, grabber.holdTransform.rotation) as GameObject;
            grabber.Pickup(gameObject);
        }
        
        switch(aiMode)
        {
            case AIMode.Pursuing:
                PursuingUpdate();
                break;
            case AIMode.Fleeing:
                FleeingUpdate();
                break;
            case AIMode.Throwing:
                ThrowingUpdate();
                break;
        }
        MovementUpdate();
	
	}

    void FixedUpdate()
    {
        canJump = CanJump();

        if (canJump)
        {
            if (Mathf.Abs(walkRaw) > 0.0f && myRigidbody.velocity.sqrMagnitude < maxSpeed * maxSpeed)
            {
                myRigidbody.AddForce(Quaternion.Euler(0f, heading, 0f) * ((Vector3.forward * walkRaw) * walkSpeed), ForceMode.Acceleration);
            }
            else
            {
                if (Mathf.Abs(walkRaw) > 0.0f && myRigidbody.velocity.sqrMagnitude < maxSpeed * maxSpeed)
                {
                    myRigidbody.AddForce(Quaternion.Euler(0f, heading, 0f) * ((Vector3.forward * walkRaw) * walkSpeed * 0.1f), ForceMode.Acceleration);
                }
            }
        }
    }

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.layer == Layers.Furnishing || collision.gameObject.layer == Layers.Target)
        { 
            if(canJump)
            {
                Jump();
            }
        }

        if (collision.gameObject.layer == Layers.Player && aiMode == AIMode.Pursuing)
        {
            collision.gameObject.SendMessageUpwards("Splatted", false);
            aiMode = AIMode.Fleeing;
            footstepAudio.Stop();
            gameObject.SetLayerRecursively(Layers.Fleeing);
        }
    }

    void Splatted()
    {
        playerController.message = retorts[Random.Range(0, retorts.Length)];

        dignity -= 1f;

        if (dignity <= 0f && !wasKilled)
        {
            aiMode = AIMode.Fleeing;
            footstepAudio.Stop();
            gameObject.SetLayerRecursively(Layers.Fleeing);
            wasKilled = true;
            playerController.AwardKill(scoreLevel);
        }
    }

    void FoundExit()
    {
        var spawner = FindObjectOfType<ProgressiveMobSpawner>();
        spawner.DecreaseSpawnCount(0);

        Destroy(gameObject);
    }

    private float VectorXZToAngle(Vector3 vector)
    {
        return (float)Mathf.Atan2(vector.x, vector.z) / Mathf.PI * 180.0f;
    }

    private static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360F)
            angle += 360F;
        if (angle > 360F)
            angle -= 360F;
        return Mathf.Clamp(angle, min, max);
    }

}
