using UnityEngine;
using System.Collections;
using System.Linq;

[System.Serializable]
public class ShotInfo
{
    public GameObject ProjectileToFire;
    public int ShotsInBurst;
    public float DelayBetweenShotsInBurst;
    public float DelayBetweenBursts;

    private int iShotsRemainingInBurst;

    private float fBetweenBurstsTimer;
    private float fBetweenShotsTimer;

    private bool bDuringBurst;

    public bool OnceBurstStartsAutoFireOn;

    public void Start( bool bIsMonster )
    {
        if (bIsMonster)
        {
            fBetweenBurstsTimer = DelayBetweenBursts * Random.value;
            bDuringBurst = false;
        }

        iShotsRemainingInBurst = ShotsInBurst;
    }

    public void Update()
    {
        TickTimers();
    }

    void TickTimers()
    {
        if (!bDuringBurst)
        {
            if (fBetweenBurstsTimer > 0.0f)
                fBetweenBurstsTimer -= Time.deltaTime;

            if (fBetweenBurstsTimer <= 0.0f)
            {
                bDuringBurst = false;
                fBetweenShotsTimer = 0.0f;
            }
        }
        else
        {
            //if we're a burstfire shot, tick down the between burst clock
            if (ShotsInBurst > 1)
            {
                if (fBetweenShotsTimer > 0.0f)
                    fBetweenShotsTimer -= Time.deltaTime;
            }
        }
    }

    public bool ShouldAutoFire()
    {
        return (bDuringBurst && OnceBurstStartsAutoFireOn);
    }

    public bool TryFire(Vector3 vOrigin, Vector2 direction, float fSpeed)
    {
        bool bShot = false;
        //fire only if all timers are 0.
        if (fBetweenBurstsTimer <= 0.0f && fBetweenShotsTimer <= 0.0f)
        {
            //shoot!
            bShot = true;
            GameObject newProjectileGameObject = GameObject.Instantiate(ProjectileToFire);
            newProjectileGameObject.transform.position = vOrigin;

            Projectile pscript = newProjectileGameObject.GetComponent<Projectile>();

            if (pscript.UsePhysicsToLaunch)
            {
                Vector2 vDir = pscript.vDirection;
                vDir.x += pscript.PhysicsLaunchDrift*-1.0f + (Random.value*pscript.PhysicsLaunchDrift*2.0f);
                vDir.y += pscript.PhysicsLaunchDrift * -1.0f + (Random.value * pscript.PhysicsLaunchDrift * 2.0f);

                vDir.Normalize();
                Debug.Log("Firing in this direction: " + vDir + " at speed " + pscript.fSpeed);

                Rigidbody2D bodyProjectile = pscript.GetComponent<Rigidbody2D>();
                bodyProjectile.AddForce(vDir * pscript.fSpeed);
            }
            else
            {
                pscript.fSpeed = fSpeed;
                if (direction != Vector2.zero)
                    pscript.vDirection = direction;

                Vector2 vDir = pscript.vDirection;
                vDir.x += pscript.PhysicsLaunchDrift * -1.0f + (Random.value * pscript.PhysicsLaunchDrift * 2.0f);
                vDir.y += pscript.PhysicsLaunchDrift * -1.0f + (Random.value * pscript.PhysicsLaunchDrift * 2.0f);

                vDir.Normalize();
                pscript.vDirection = vDir;

            }

            //reduce the amount of ammo in the clip
            iShotsRemainingInBurst--;
            fBetweenShotsTimer = DelayBetweenShotsInBurst;

            //if we cleared the burst, start that timer
            if (iShotsRemainingInBurst <= 0)
            {
                fBetweenBurstsTimer = DelayBetweenBursts;
                bDuringBurst = false;
                iShotsRemainingInBurst = ShotsInBurst;
            }
            else
            {
                bDuringBurst = true;
            }
        }

        return bShot;
        
    }


}

public class EnemyController : MonoBehaviour {

    public float JumpForce = 1200.0f;

    public float MoveTowardPlayerSpeed = 1.0f;

    protected Rigidbody2D m_myBody;

    protected MasterControllerScript mcs;
    protected PlayerController playerScript;

    private int iJumpCounter = 0;
    private float fHackAssNoJumpDelay = 0.0f;

    public GameObject[] collisionFeet;

    public LayerMask m_whatIsGround;

    private SpriteRenderer m_mySpriteRenderer;

    public Sprite[] idleSprites;
    private Sprite[] currentSpriteCollection;
    private int iSpriteIndex;
    private float fAnimTimer;


    public float Health = 1;
    protected float m_health;

    public Vector2 MoveDirection = new Vector2(-1, 0);


    public ShotInfo[] Attacks;

    public bool bHandledDead;

    public bool bStaticObstacle;

    // Use this for initialization
    protected virtual void Start()
    {
        m_myBody = GetComponent<Rigidbody2D>();
        m_mySpriteRenderer = GetComponent<SpriteRenderer>();

        currentSpriteCollection = idleSprites;
        iSpriteIndex = 0;

        mcs = GameObject.Find("MasterController").GetComponent<MasterControllerScript>();
        playerScript = GameObject.Find("TestMans").GetComponent<PlayerController>();

        m_health = Health;

        for (int t = 0; t < Attacks.Count(); t++)
        {
            Attacks[t].Start(true);
        }

        if( transform.position.y < -10.0f)
            Destroy(gameObject);


    }

    public void SetMaxHealth(float i)
    {
        Health = i;
        m_health = Health;
    }

    protected virtual bool UpdateGrounded()
    {
        if (fHackAssNoJumpDelay > 0.0f)
            return false;

        bool bLanded = false;
        foreach (GameObject go in collisionFeet)
        {
            Collider2D[] colliders = Physics2D.OverlapCircleAll(new Vector2(go.transform.position.x, go.transform.position.y), 0.1f, m_whatIsGround); // (groundCollisionCircle.position, fGroundCheckRadius, m_WhatIsGround);
            for (int i = 0; i < colliders.Length; i++)
            {
                if (colliders[i].gameObject != gameObject)
                {
                    bLanded = true;
                    iJumpCounter = 0;
                }
            }
        }
        return bLanded;
    }

	// Update is called once per frame
	public virtual void Update ()
	{
	    if (bHandledDead)
	        return;

        UpdateGrounded();
        UpdateSprites();
        UpdateMotion();
        UpdateAttacks();


        //tickdown

        if (fAnimTimer > 0.0f)
            fAnimTimer -= Time.deltaTime;

        if (fHackAssNoJumpDelay > 0.0f)
            fHackAssNoJumpDelay -= Time.deltaTime;
	}

    public virtual void UpdateAttacks()
    {
        for (int t = 0; t < Attacks.Count(); t++)
        {
            Attacks[t].Update();

            if (transform.position.x < 12.0f)
            {
                float fShotSpeed = 12.0f + (3.5f*mcs.GetEnemySpeedMultiplier());
                Attacks[t].TryFire(transform.position, Vector2.zero, fShotSpeed);
            }
        }
    }

    public virtual void StayVertical()
    {
        //keep baddies upright
        Vector3 vEuler = transform.rotation.eulerAngles;
        vEuler.z = 0.0f;
        transform.rotation = Quaternion.Euler(vEuler);
    }


    public virtual void Died()
    {
        if (bHandledDead)
            return;

        transform.parent = null;

        bHandledDead = true;
        if (m_myBody != null)
        {
            m_myBody.isKinematic = false;
            m_myBody.fixedAngle = false;
            m_myBody.AddTorque(Random.value*200.0f);
            m_myBody.AddForce(Vector2.up*(600.0f + Random.value*600.0f));
        }
        gameObject.layer = LayerMask.NameToLayer("NoCollide");
        Destroy(gameObject,4.0f);

        mcs.AddScore((int)Health * 100);
    }


    public virtual void UpdateMotion()
    {
        if (mcs == null)
            return;

        float fSpeed = MoveTowardPlayerSpeed;
        fSpeed += fSpeed * 0.5f * mcs.GetEnemySpeedMultiplier();

        Vector3 vPos = transform.position;
        float oldz = vPos.z;
        vPos += new Vector3(MoveDirection.x, MoveDirection.y,0.0f) * fSpeed * Time.deltaTime;
        vPos.z = oldz;
        transform.position = vPos;

        StayVertical();
    }

    void UpdateSprites()
    {
        if (fAnimTimer > 0.0f)
            return;

        if (currentSpriteCollection == null)
            return;

        if (iSpriteIndex >= currentSpriteCollection.Count())
            iSpriteIndex = 0;

        m_mySpriteRenderer.sprite = currentSpriteCollection[iSpriteIndex];
        iSpriteIndex++;

        fAnimTimer = 0.2f;
    }


    public virtual void TakeDamage(Projectile p)
    {
        m_health -= p.DamageOnHit;
        if (m_health <= 0)
        {
            //foom
            Died();
        }
        else
        {
            playerScript.Speakerbox.PlayOneShot( playerScript.wavShotHitAThing);
        }
    }

    public virtual void TakeDamage(float fDamage)
    {
        m_health -= fDamage;
        if (m_health <= 0)
        {
            //foom
            Died();
        }
        else
        {
            playerScript.Speakerbox.PlayOneShot(playerScript.wavShotHitAThing);
        }

    }

    public virtual void OnDestroy()
    {
        
    }

    public virtual void OnCollisionEnter2D(Collision2D other)
    {
        //if we're a static obstacle, and hit the player, damage the player
        PlayerController pc = other.gameObject.GetComponent<PlayerController>();
        if (pc != null)
        {
            //if we stomped the enemy, and we're not a static obstacle, kill the enemy
            if( !bStaticObstacle && pc.transform.position.y > transform.position.y)
                TakeDamage(3.0f);
            else
                pc.TakeDamage(1.0f);
    
            Rigidbody2D otherBody = pc.GetComponent<Rigidbody2D>();
            if (otherBody != null)
                otherBody.AddForce(new Vector2(0, 400.0f));

            if (bStaticObstacle)
                Died();
            
        }
    }
}
