using UnityEngine;
using System;
using System.Collections;

public class Shooting : MonoBehaviour
{
    public GameObject projectilePrefab;

    public float fireRate;
    public int maxAmunition;
    public float rechargeTime;

    public float channelTime;

    public bool activateOnPlayerSight;
    public Frustum viewFrustum;

    public float Cooldown { get; private set; }
    public float ChannelProgress { get; private set; }
    public int Amunition { get { return _amunition; } }

    private bool _playerInSight;
    private int _amunition;

    // Use this for initialization
    void Start()
    {
        _playerInSight = false;

        if (activateOnPlayerSight)
            CreateFrustumTrigger();
        else
            StartCoroutine(Shoot());
    }

    private void CreateFrustumTrigger()
    {
        float heightNear = Mathf.Tan(viewFrustum.verticalFoV * Mathf.Deg2Rad) * viewFrustum.nearPlane;
        float widthNear = Mathf.Tan(viewFrustum.horizontalFoV * Mathf.Deg2Rad) * viewFrustum.nearPlane;

        float heightFar = Mathf.Tan(viewFrustum.verticalFoV * Mathf.Deg2Rad) * viewFrustum.farPlane;
        float widthFar = Mathf.Tan(viewFrustum.horizontalFoV * Mathf.Deg2Rad) * viewFrustum.farPlane;

        Quaternion rotation = Quaternion.Euler(viewFrustum.orientation);

        Vector3 topLeftNear = rotation * new Vector3(-widthNear, heightNear, viewFrustum.nearPlane);
        Vector3 topRightNear = rotation * new Vector3(widthNear, heightNear, viewFrustum.nearPlane);
        Vector3 bottomLeftNear = rotation * new Vector3(-widthNear, -heightNear, viewFrustum.nearPlane);
        Vector3 bottomRightNear = rotation * new Vector3(widthNear, -heightNear, viewFrustum.nearPlane);

        Vector3 topLeftFar = rotation * new Vector3(-widthFar, heightFar, viewFrustum.farPlane);
        Vector3 topRightFar = rotation * new Vector3(widthFar, heightFar, viewFrustum.farPlane);
        Vector3 bottomLeftFar = rotation * new Vector3(-widthFar, -heightFar, viewFrustum.farPlane);
        Vector3 bottomRightFar = rotation * new Vector3(widthFar, -heightFar, viewFrustum.farPlane);

        MeshCollider frustumCol = gameObject.AddComponent<MeshCollider>();

        Mesh frustumMesh = new Mesh();
        frustumMesh.vertices = new Vector3[] {
            topLeftNear,
            topRightNear,
            bottomRightNear,
            bottomLeftNear,
            topLeftFar,
            topRightFar,
            bottomRightFar,
            bottomLeftFar
        };

        frustumMesh.triangles = new int[] {
            0,1,2,
            2,3,0,
            4,5,6,
            6,7,4,
            4,0,3,
            3,7,4,
            1,5,6,
            6,3,1,
            0,4,5,
            5,2,0,
            3,7,6,
            6,2,3
        };

        frustumCol.sharedMesh = frustumMesh;
        frustumCol.isTrigger = true;
        frustumCol.convex = true;
    }

    private void OnTriggerEnter(Collider other)
    {
        if (!activateOnPlayerSight)
            return;

        if (other.tag == "Player")
        {
            _playerInSight = true;
            StartCoroutine(Shoot());
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (!activateOnPlayerSight)
            return;

        if (other.tag == "Player")
            _playerInSight = false;
    }

    private IEnumerator Shoot()
    {
        float elapsedTime = 0f;

        while (!activateOnPlayerSight || _playerInSight)
        {
            if (_amunition > 0)
            {
                while (elapsedTime < fireRate)
                {
                    yield return null;
                    elapsedTime += Time.deltaTime;
                }

                yield return StartCoroutine(Channeling());

                Instantiate(projectilePrefab, transform.position, transform.rotation);
                _amunition--;
                elapsedTime = 0f;
                ChannelProgress = 0f;
            }
            else
                yield return StartCoroutine(Recharge());
        }
    }

    private IEnumerator Recharge()
    {
        float elapsedTime = 0f;

        while (elapsedTime < rechargeTime)
        {
            yield return null;
            elapsedTime += Time.deltaTime;
            Cooldown = rechargeTime - elapsedTime;
        }

        _amunition = maxAmunition;
        Cooldown = 0f;
    }

    private IEnumerator Channeling()
    {
        float elapsedTime = 0f;

        while (elapsedTime < channelTime)
        {
            yield return null;
            elapsedTime += Time.deltaTime;
            ChannelProgress = elapsedTime / channelTime;
        }
    }

    private void OnDrawGizmos()
    {
        if (!activateOnPlayerSight)
            return;

        float heightNear = Mathf.Tan(viewFrustum.verticalFoV * Mathf.Deg2Rad) * viewFrustum.nearPlane;
        float widthNear = Mathf.Tan(viewFrustum.horizontalFoV * Mathf.Deg2Rad) * viewFrustum.nearPlane;

        float heightFar = Mathf.Tan(viewFrustum.verticalFoV * Mathf.Deg2Rad) * viewFrustum.farPlane;
        float widthFar = Mathf.Tan(viewFrustum.horizontalFoV * Mathf.Deg2Rad) * viewFrustum.farPlane;

        Quaternion rotation = Quaternion.Euler(viewFrustum.orientation) * transform.rotation;

        Vector3 topLeftNear = rotation * new Vector3(-widthNear, heightNear, viewFrustum.nearPlane) + transform.position;
        Vector3 topRightNear = rotation * new Vector3(widthNear, heightNear, viewFrustum.nearPlane) + transform.position;
        Vector3 bottomLeftNear = rotation * new Vector3(-widthNear, -heightNear, viewFrustum.nearPlane) + transform.position;
        Vector3 bottomRightNear = rotation * new Vector3(widthNear, -heightNear, viewFrustum.nearPlane) + transform.position;

        Vector3 topLeftFar = rotation * new Vector3(-widthFar, heightFar, viewFrustum.farPlane) + transform.position;
        Vector3 topRightFar = rotation * new Vector3(widthFar, heightFar, viewFrustum.farPlane) + transform.position;
        Vector3 bottomLeftFar = rotation * new Vector3(-widthFar, -heightFar, viewFrustum.farPlane) + transform.position;
        Vector3 bottomRightFar = rotation * new Vector3(widthFar, -heightFar, viewFrustum.farPlane) + transform.position;

        Gizmos.DrawLine(topLeftNear, topRightNear);
        Gizmos.DrawLine(topRightNear, bottomRightNear);
        Gizmos.DrawLine(bottomRightNear, bottomLeftNear);
        Gizmos.DrawLine(bottomLeftNear, topLeftNear);

        Gizmos.DrawLine(topLeftFar, topRightFar);
        Gizmos.DrawLine(topRightFar, bottomRightFar);
        Gizmos.DrawLine(bottomRightFar, bottomLeftFar);
        Gizmos.DrawLine(bottomLeftFar, topLeftFar);

        Gizmos.DrawLine(topLeftNear, topLeftFar);
        Gizmos.DrawLine(topRightNear, topRightFar);
        Gizmos.DrawLine(bottomRightNear, bottomRightFar);
        Gizmos.DrawLine(bottomLeftNear, bottomLeftFar);
    }

    [Serializable]
    public class Frustum
    {
        public float verticalFoV;
        public float horizontalFoV;
        public float nearPlane;
        public float farPlane;

        public Vector3 orientation;
    }
}
