using UnityEngine;
using System.Collections;

public class PlayerSniper_Rifle : MonoBehaviour
{
    public enum HoldState
    {
        AIM,
        RELAX,
        BUSY,
        SCOPE,
        SCOPE_FIRE,
        FIRE,
        BOLT
    };
    public enum UseState
    {
        NORMAL,
        BOLT,
        BOLT_CLOSE,
        RELOAD
    };

    private Transform m_player;
    private Transform m_camera;
    private Transform m_bolt;
    private Transform m_bullet;
    private Transform m_bulletRef;
    private Vector3 m_bulletRefPosition;
    private Quaternion m_bulletRefRotation;
    private Transform m_scope;
    private Transform m_model;

    private HoldState m_holdstate;
    private HoldState m_holdstate_previous;
    private UseState m_usestate;

    private Quaternion  m_offset_rotation;
    private Vector3     m_offset_position;

    private Quaternion  m_offset_rotation_hold;
    private Vector3     m_offset_position_hold;

    private float m_offset_lerp;

    private bool m_busy;
    private float m_busyTimer;
    private bool m_blocked;
    private bool m_spinBullet;
    private float m_spinBulletTime;
    private float m_spinBulletAmount;

    public AudioClip m_sndGunFire;
    public AudioClip m_sndBoltOpen;
    public AudioClip m_sndBoltClose;

    public GameObject m_fxBloodEffect;

    public void SetHoldState(HoldState n_holdType)
    {
        // RELAX is a special input. It doesn't mean relax, it actually means that we're no longer busy and we should stop being busy next frame
        if (n_holdType == HoldState.BUSY)
        {
            m_holdstate = n_holdType;
            m_busy = true;
            m_busyTimer = 0.0f;
        }
        else if (n_holdType == HoldState.RELAX)
        {
            m_busy = false;
        }
    }

	void Start ()
    {
        m_player = transform.parent;
        m_camera = m_player.Find("Camera");
        m_bolt = transform.Find("model").Find("bolt");
        m_bullet = transform.Find("model").Find("bullet");
        m_bulletRef = transform.Find("model");
        m_bulletRefPosition = m_bullet.localPosition;
        m_bulletRefRotation = m_bullet.localRotation;
        m_scope = transform.Find("scope");
        m_scope.gameObject.SetActive(false);
        m_model = transform.Find("model");

        m_holdstate = HoldState.RELAX;
        m_holdstate_previous = HoldState.RELAX;
        m_busy = false;
        m_usestate = UseState.NORMAL;
        m_spinBullet = false;
	}

    void Update()
    {
        // Check if the gun is blocked
        m_blocked = false;
        {
            RaycastHit t_hitInfo;
            if (Physics.Raycast(m_camera.position,(transform.position - m_camera.position).normalized, out t_hitInfo,2.5f, (1 << 8)))
            {
                m_blocked = true;
            }
        }

        // Check for the scope
        if ((m_holdstate == HoldState.SCOPE || m_holdstate == HoldState.SCOPE_FIRE) && m_offset_lerp > 0.99f)
        {
            m_player.GetComponent<PlayerSniper_Movement>().SetZoomOffset(-70);
            m_player.GetComponent<PlayerSniper_Movement>().SetTurnSpeed(0.04f);
            m_player.GetComponent<PlayerSniper_Movement>().SetTurnPersist(true);
            m_scope.gameObject.SetActive(true);
            m_model.gameObject.SetActive(false);
        }
        else
        {
            m_player.GetComponent<PlayerSniper_Movement>().SetZoomOffset(0);
            m_player.GetComponent<PlayerSniper_Movement>().SetTurnSpeed(1.0f);
            m_player.GetComponent<PlayerSniper_Movement>().SetTurnPersist(false);
            m_scope.gameObject.SetActive(false);
            m_model.gameObject.SetActive(true);
        }

        // Apply animation thinking here
        if (m_usestate == UseState.NORMAL)
        {
            if (!m_busy)
            {
                m_busyTimer += Time.deltaTime * (Random.value + 0.5f);
                if (m_busyTimer > 0.75f && m_holdstate == HoldState.BUSY)
                {
                    m_holdstate = HoldState.RELAX;
                }

                // Perform aiming animations
                if (Input.GetAxis("Aim") > 0.5f || Input.GetAxis("Fire") > 0.5f)
                {
                    if (m_holdstate != HoldState.AIM && m_holdstate != HoldState.SCOPE && m_holdstate != HoldState.FIRE && m_holdstate != HoldState.SCOPE_FIRE)
                    {
                        m_holdstate = HoldState.AIM;
                    }
                }
                if (Input.GetAxis("Aim") > 0.5f)
                {
                    if ((m_holdstate == HoldState.AIM && m_offset_lerp > 0.5f) || m_holdstate == HoldState.SCOPE)
                    {
                        m_holdstate = HoldState.SCOPE;
                    }
                }
                else if (m_holdstate == HoldState.SCOPE)
                {
                    m_holdstate = HoldState.AIM;
                }
                // If fired the rifle and done with firing animation, then need to pull the bolt
                if ((m_holdstate == HoldState.FIRE || m_holdstate == HoldState.SCOPE_FIRE) && m_offset_lerp >= 0.99f)
                {
                    m_spinBullet = false;
                    m_holdstate = HoldState.BOLT;
                    m_usestate = UseState.BOLT;
                }

                // Blocked overrides all
                if (m_blocked && m_holdstate != HoldState.BUSY && m_holdstate != HoldState.RELAX)
                {
                    m_holdstate = HoldState.RELAX;
                }

                // Can only fire if aiming or looking down the sight and gun is ready
                if ((m_holdstate == HoldState.AIM && m_offset_lerp > 0.5f) || m_holdstate == HoldState.SCOPE)
                {
                    if (Input.GetAxis("Fire") > 0.5f)
                    {
                        // Fire the gun
                        GameObject obj = new GameObject();
                        obj.transform.position = transform.position - transform.rotation * Vector3.forward * 1.2f;
                        obj.transform.parent = this.transform;
                        AudioSource source = obj.AddComponent<AudioSource>();
                        source.clip = m_sndGunFire;
                        source.minDistance = 8.0f;
                        source.maxDistance = 200.0f;
                        source.rolloffMode = AudioRolloffMode.Logarithmic;
                        source.dopplerLevel = 0.0f;
                        source.spread = 120.0f;
                        source.volume = 2.0f;
                        source.Play();
                        obj.AddComponent<KillOnSoundEnd>();
                        
                        // Raytrace from the camera to hit an enemy
                        RaycastHit  t_hitInfo;
                        if (Physics.Raycast(m_camera.position, m_camera.rotation * Vector3.forward, out t_hitInfo, 1000, (1 << 10)))
                        {
                            GameObject bloodEffect = (GameObject)GameObject.Instantiate(m_fxBloodEffect, t_hitInfo.point, Quaternion.FromToRotation(Vector3.forward, t_hitInfo.normal));
                            if (t_hitInfo.transform)
                            {
                                GameObject.Destroy(t_hitInfo.transform.parent.gameObject);
                            }
                            //m_fxBloodEffect

                        }
                        // Punch the view
                        m_player.GetComponent<PlayerSniper_Movement>().PunchView(new Vector3(-10, 0, Random.Range(-3.0f, 3.0f)));
                        Vector3 t_kickAmount = Random.onUnitSphere;
                        t_kickAmount.x -= 5;
                        t_kickAmount.z = 0;
                        m_player.GetComponent<PlayerSniper_Movement>().KickView(t_kickAmount);

                        /*if (m_holdstate == HoldState.SCOPE)
                        {
                            m_holdstate = HoldState.SCOPE_FIRE;
                        }
                        else
                        {*/
                            m_holdstate = HoldState.FIRE;
                        //}
                    }
                }
            }
        }
        else if (m_usestate == UseState.BOLT || m_usestate == UseState.BOLT_CLOSE)
        {
            if (m_busy || m_blocked)
            {
                m_player.GetComponent<PlayerSniper_Movement>().SetTurnSpeed(1.0f);
                m_holdstate = HoldState.BUSY;
            }
            else if (m_holdstate == HoldState.SCOPE_FIRE)
            {
                Debug.Log("Woop.");
                // Nothing.
                if (Input.GetAxis("Aim") < 0.5f)
                {
                    m_holdstate = HoldState.BOLT;
                }
            }
            else
            {
                const float t_minBolt = 0.249f;
                const float t_maxBolt = 0.631f;

                // Reduce player turning speed
                m_player.GetComponent<PlayerSniper_Movement>().SetTurnSpeed(0.2f);

                // Set the hold state
                m_holdstate = HoldState.BOLT;

                // Grab player input
                Vector3 m_turnInput = Vector3.zero;
                m_turnInput.x -= (Input.GetAxis("TurnY") + (Input.GetAxis("TurnY_JS") * Time.deltaTime * 100)) * 5;
                m_turnInput.y += (Input.GetAxis("TurnX") + (Input.GetAxis("TurnX_JS") * Time.deltaTime * 100)) * 5;
                m_turnInput.x = Mathf.Min(Mathf.Abs(m_turnInput.x), 480 * Time.deltaTime) * Mathf.Sign(m_turnInput.x);
                m_turnInput.y = Mathf.Min(Mathf.Abs(m_turnInput.y), 480 * Time.deltaTime) * Mathf.Sign(m_turnInput.y);
                // Move the bolt
                Vector3 t_newPosition = m_bolt.localPosition;
                t_newPosition.z += m_turnInput.y * 0.002f;
                // Check for states
                if (m_usestate == UseState.BOLT)
                {
                    if (t_newPosition.z > t_maxBolt)
                    {
                        m_usestate = UseState.BOLT_CLOSE;
                        m_spinBullet = true;

                        // Play the sound
                        GameObject obj = new GameObject();
                        obj.transform.position = transform.position + transform.rotation * Vector3.forward * 0.1f;
                        obj.transform.parent = this.transform;
                        AudioSource source = obj.AddComponent<AudioSource>();
                        source.clip = m_sndBoltOpen;
                        source.minDistance = 8.0f;
                        source.maxDistance = 200.0f;
                        source.rolloffMode = AudioRolloffMode.Logarithmic;
                        source.dopplerLevel = 0.0f;
                        source.spread = 120.0f;
                        source.Play();
                        obj.AddComponent<KillOnSoundEnd>();
                    }
                }
                else if (m_usestate == UseState.BOLT_CLOSE)
                {
                    if (t_newPosition.z < t_minBolt)
                    {
                        if (Input.GetAxis("Aim") > 0.5f)
                        {
                            m_holdstate = HoldState.SCOPE;
                        }
                        else
                        {
                            m_holdstate = HoldState.AIM;
                        }
                        m_usestate = UseState.NORMAL;
                        m_player.GetComponent<PlayerSniper_Movement>().SetTurnSpeed(1.0f);

                        // Play the sound
                        GameObject obj = new GameObject();
                        obj.transform.position = transform.position + transform.rotation * Vector3.forward * 0.1f;
                        obj.transform.parent = this.transform;
                        AudioSource source = obj.AddComponent<AudioSource>();
                        source.clip = m_sndBoltClose;
                        source.minDistance = 8.0f;
                        source.maxDistance = 200.0f;
                        source.rolloffMode = AudioRolloffMode.Logarithmic;
                        source.dopplerLevel = 0.0f;
                        source.spread = 120.0f;
                        source.Play();
                        obj.AddComponent<KillOnSoundEnd>();
                    }
                }
                // 
                t_newPosition.z = Mathf.Max(t_minBolt, Mathf.Min(t_maxBolt, t_newPosition.z));
                m_bolt.localPosition = t_newPosition;
            }
        }

        // Spin the bullet when it pops out
        if (m_spinBullet)
        {
            m_bullet.parent = null;
            m_spinBulletTime += Time.deltaTime;

            Vector3 t_nextPosition = m_bullet.localPosition;
            t_nextPosition += m_bullet.rotation * Vector3.right * Time.deltaTime * 1.7f;
            t_nextPosition.y += 0.5f * m_spinBulletTime - 2.24f * m_spinBulletTime * m_spinBulletTime;
            m_bullet.localPosition = t_nextPosition;

            m_bullet.localRotation *= Quaternion.Euler(m_spinBulletAmount * Time.deltaTime, 0, 0);
        }
        else
        {
            m_spinBulletTime = 0;
            m_bullet.parent = m_bulletRef;
            m_bullet.localPosition = m_bulletRefPosition;
            m_bullet.localRotation = m_bulletRefRotation;
            m_spinBulletAmount = Random.Range(-180.0f, 180.0f);
        }
    }

	void LateUpdate ()
    {
	    // Apply transforms
        Quaternion t_targetRotation = Quaternion.identity;
        Vector3 t_targetPosition = Vector3.zero;

        // If not same state, then start a new lerp period
        if (m_holdstate_previous != m_holdstate)
        {
            m_offset_rotation_hold = m_offset_rotation;
            m_offset_position_hold = m_offset_position;
            m_offset_lerp = 0;
            m_holdstate_previous = m_holdstate;
        }
        m_offset_lerp = Mathf.Min(m_offset_lerp + Time.deltaTime*2.0f, 1.0f);

        if (m_holdstate == HoldState.AIM)
        {
            t_targetRotation = Quaternion.Euler(0, 180, 0);
            t_targetPosition = new Vector3(-0.7f, -0.5f, -1.7f);
        }
        else if (m_holdstate == HoldState.SCOPE || m_holdstate == HoldState.SCOPE_FIRE)
        {
            if (m_holdstate == HoldState.SCOPE_FIRE)
            {
                m_offset_lerp = 1.0f;
            }
            t_targetRotation = Quaternion.Euler(0, 180, 0);
            t_targetPosition = new Vector3(0.0f, 0.0f, -1.3f);
        }
        else if (m_holdstate == HoldState.BOLT)
        {
            t_targetRotation = Quaternion.Euler(20, 110, 0);
            t_targetPosition = new Vector3(-0.7f, 0.1f, -0.7f);
        }
        else if (m_holdstate == HoldState.RELAX)
        {
            t_targetRotation = Quaternion.Euler(0, 120, 0);
            t_targetPosition = new Vector3(-0.7f, -0.5f, -0.7f);
        }
        else if (m_holdstate == HoldState.BUSY)
        {
            t_targetRotation = Quaternion.Euler(80, 180, -10);
            t_targetPosition = new Vector3(-1, -0.7f, 1);
        }
        else if (m_holdstate == HoldState.FIRE)
        {
            m_offset_lerp = Mathf.Max(m_offset_lerp, 0.20f);
            m_offset_lerp = Mathf.Min(m_offset_lerp + Time.deltaTime * 2.0f, 1.0f);
            t_targetRotation = Quaternion.Euler(15, 180, 0);
            t_targetPosition = new Vector3(-0.5f, -0.2f, 0.0f);
        }
        //m_offset_rotation = Quaternion.Slerp(m_offset_rotation, t_targetRotation, Mathf.Min(0.5f, 10 * Time.deltaTime));
        //m_offset_position = Vector3.Lerp(m_offset_position, t_targetPosition, Mathf.Min(0.5f, 10 * Time.deltaTime));
        float t_smoothStep = Mathf.SmoothStep(0, 1, m_offset_lerp);
        m_offset_rotation = Quaternion.Slerp(m_offset_rotation_hold, t_targetRotation, t_smoothStep);
        m_offset_position = Vector3.Lerp(m_offset_position_hold, t_targetPosition, t_smoothStep);

        transform.rotation = m_camera.rotation * m_offset_rotation;

        Vector3 t_tempPosition;
        float t_breatheOffset = Mathf.Sin(Time.time) * 0.02f;
        t_tempPosition = m_player.position;
        t_tempPosition.y = m_camera.position.y + t_breatheOffset;
        t_tempPosition += transform.rotation * (m_offset_position + new Vector3(0, t_breatheOffset, 0));
        transform.position = t_tempPosition;

        if (m_holdstate == HoldState.SCOPE || m_holdstate == HoldState.SCOPE_FIRE)
        {
            transform.position = Vector3.Lerp(transform.position, m_camera.position - m_camera.rotation * new Vector3(0, 0, -1.1f), t_smoothStep);
        }
	}
}
