using UnityEngine;
using System.Collections;
using System.Collections.Generic;

// INIT:
// create 
//    note: instantiate from disabled prefab doens't call awake
// PLAY 1st:
// awake, enable, pool_awake ... pool_destroy, disable
// PLAY 2nd:
//        enable, pool_awake ... pool_destroy, disable

public class FXObj : MonoBehaviour
{
	public bool m_autoDestroy = true;

	[System.NonSerialized]
	public FXObjProto m_protoFX;

    protected ParticleSystem  m_ps;

    public virtual void OnCreate() 
    { 
        // Debug.Log("fxobj create "+gameObject.name);
		m_ps = GetComponent<ParticleSystem>();
    }

    void Awake()
    {
		// Debug.Log("fxobj awake "+gameObject.name);
		if(m_protoFX == null)
			OnCreate();
    }

    // OnEnable
    // OnDisable

/*
  	public virtual void OnPoolAwake() 
    { 
        // Debug.Log("fxobj pool awake "+gameObject.name);
    }

	public virtual void OnPoolDestroy() 
    { 
        // Debug.Log("fxobj pool destroy "+gameObject.name);
    }
*/
	public virtual bool IsPlaying()
    {
        return m_ps.isPlaying;
    }

    public virtual bool IsAlive()
    {
        return m_ps.IsAlive();
    }

    // call this to activate the FX object
    public virtual void Activate()
    {
        gameObject.SetActive(true);
    }

	public virtual void Play()
    {
        m_ps.Play();
    }

	public virtual void Stop()
    {
        m_ps.Stop();
    }

	public virtual void Clear()
    {
        m_ps.Clear();
    }

    public virtual void SetStartRotation(float angle)
    {
        var main = m_ps.main;
        main.startRotation = angle * Mathf.Deg2Rad;
    }

    public virtual void SetStartSize(float size)
    {
        var main = m_ps.main;
        main.startSize = size;
    }

    public virtual void SetStartColor(Color color)
	{
        var main = m_ps.main;
        main.startColor = color;
    }

    public virtual void SetStartAlpha(float alpha)
    {
        var main = m_ps.main;
        Color color = main.startColor.color;
		color.a = alpha;
		main.startColor = color;
	}

    void Update()
    {
		if(m_autoDestroy)
        	if(!IsAlive())
            	DestroyFX();
    }

    // call this to detach the fx object from parent object
	public void Detach()
    {
        transform.parent = null;
    }

    // call this to destroy the fx, or pool it if managed by a proto fx
    public void DestroyFX()
    {
        Detach();

        if (m_protoFX != null)
		{
        	m_protoFX.DestroyFX(this);
        }
        else
        {
            DestroyNow();
        }
    }

    // reserved for interal use
    public void DestroyNow()
    {
        FXManager.instance.m_activeFX.Remove(this);

        Destroy(this.gameObject);
    }
}

[System.Serializable]
public class FXObjProto
{
	public FXObj m_prefabFX;
	public int m_poolSizeMin = 1;
	public int m_poolSizeMax = 1;

    private int m_poolSize;
	private List<FXObj> m_pool;

	public void Init()
	{
		m_pool = new List<FXObj>();		
        CacheFX(m_poolSizeMin);
	}

    public void Reset()
    {
        while(m_pool.Count > 0)
        {
            FXObj obj = RetreiveFX();
            obj.DestroyNow();
        }
    }

	/*
	public void OnUpdate()
	{
		foreach(var fx in m_pool)
		{
			if(fx.m_used)
				fx.OnUpdate();
		}
	}
	*/

	/*
	public void LooseParent(Transform parent)
	{
		foreach(var fx in m_pool)
		{
			if(fx.m_used && fx.transform.parent == parent)
			{
				fx.Stop();
				fx.Detach();
			}
		}
	}
	*/

	private void CacheFX(int count)
	{
        while(0 < count--)
		{
            m_poolSize++;
			GameObject obj = GameObject.Instantiate(m_prefabFX.gameObject);
			obj.SetActive(false);
            obj.name = m_prefabFX.gameObject.name + " " + m_poolSize;
			FXObj fx = obj.GetComponent<FXObj>();
			fx.m_protoFX = this;
			fx.OnCreate();

			fx.transform.parent = FXManager.instance.transform;
			m_pool.Add(fx);
		}
	}

    private FXObj RetreiveFX()
    {
		// out of stock?
		if(m_pool.Count == 0)
		{
			if(m_poolSize >= m_poolSizeMax)
			{
				// deny (or steal an old one?)
				return null;
			}
			else
			{
				// grow the pool
        		CacheFX(1);
        	}
        }

		int idx = m_pool.Count - 1;
		var fx = m_pool[idx];
		m_pool.RemoveAt(idx);

        FXManager.instance.m_activeFX.Add(fx);

        return fx;
    }

    public FXObj InstantiateFX(bool activate)
	{
		FXObj fx = RetreiveFX();
        if (fx)
        {
        	fx.transform.parent = FXManager.instance.m_fxParent;

            // fx.OnPoolAwake();

            if (activate)
                fx.Activate();
        }

        return fx;
	}

    public FXObj InstantiateFX(Transform parent, Vector3 pos, Quaternion rot, bool activate)
    {
		FXObj fx = RetreiveFX();
        if (fx)
        {
            fx.transform.parent = parent;
           	fx.transform.localPosition = pos;
           	fx.transform.localRotation = rot;

            // fx.OnPoolAwake();

            if(activate)
				fx.Activate();
        }

        return fx;
    }

    // internal use (by FXObj)
	public void DestroyFX(FXObj fx)
	{
        fx.Stop();
        fx.Clear();

        fx.gameObject.SetActive(false);

		// fx.OnPoolDestroy();

		fx.transform.parent = FXManager.instance.transform;

        FXManager.instance.m_activeFX.Remove(fx);
        m_pool.Add(fx);
	}
}
