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

[System.Serializable]
public class TypePool
{
	#region Public Members
	
	// The name of the pool, used for spawning
	[SerializeField]
	public string PoolName;
	
	// The type that the pool contains
	[SerializeField]
	public PoolType PooledType;
	
	// The prefab to be instantiated
	[SerializeField]
	public GameObject Prefab;
	
    [SerializeField]
	public LayerMask Layer;
	
	// The Max size of the pool, threshold before culling
	[SerializeField]
	public int PoolSize;
	
	// Whether or not to instantiate more when the limit is reached
	[SerializeField]
	public bool LimitToSize;
	
	// Whether or not to fill the pool in awake
	[SerializeField]
	public bool PopulatePool;
	
	// The amount to fill the pool to in initialization
	[SerializeField]
	public int PopulateCount;
	
	// Debug Log messages or not
	[SerializeField]
	public bool LogMessages;
	
	// Load from a resources path instead of a prefab
	[SerializeField]
	public bool ResourcesLoad;
	
	// The Resources/ path to load from
	[SerializeField]
	public string ResourcePath;
	
	// Use the prefab path ( if it was placed )
	[SerializeField]
	public bool UsePrefabPath;
	
	// Automatically destroy or disable after x time ( particles )
	[SerializeField]
	public bool AutoDespawn;
	
	// The amount of time to disable or destroy after 
	[SerializeField]
	public float DespawnTime;
	
	[SerializeField]
	public bool SpawnNotifications;
	
	// Whether or not to add a script, or if the user already has one
	[SerializeField]
	public bool AddScript;
	
	// This implies that the items are static, and not disabled/destroyed
	[SerializeField]
	public bool IsScenery;
	
	// Load positions and everything from XML Instructions ( Pools must be in place )
	[SerializeField]
	public bool XmlLoad;
	
	// The XML file to load the scenery from
	[SerializeField]
	public TextAsset XmlFile;
	
	#endregion
		
	#region Private Members
	
	// The pool of prefabs that will be initialized on load
	private List<PoolableType> m_pool;
	
	// The parent for the spawned effects
	private Transform m_container;
	
	#endregion
	
	#region Startup
	
	// Run from PoolController. Sets up the basics
	public void Initialize(GameObject container)
	{
		// Initialize the pool list
		m_pool = new List<PoolableType>();
		
		// set the parent container 
		m_container = container.transform;	
		
		// Populate if selected
		if(PopulatePool)
		{
			Populate();
		}
	}	
	
	// Preloads the pool if enabled
	private void Populate()
	{
		for(var i = 0; i < PopulateCount; i++)
		{
			NewInstance(Vector3.zero, true, false);
		}
		
		// Add notification here
	}	
	
	#endregion	
	
	#region Pool Methods
	
	public void Spawn(Vector3 position)
	{
		// make sure the pool isn't null from init
		if(m_pool == null)
		{
#if UNITY_DEBUG			
			LogMessage("Pool is null");
#endif
			return;
		}
		
		// Check to respawn an instance from the pool
		if(ReInstance(position))
		{
			LogMessage("Reused an instance");
			return;
		}
		
		// Check to create a new instance if we have space	
		if(m_pool.Count < PoolSize)
        {	
		    if(NewInstance(position, true, true))
		    {
				LogMessage("Created a new pooled instance");
			    return;
			}
		}
		
		// Check to return if we don't create unpooled instances
		if(LimitToSize && m_pool.Count >= PoolSize)
		{
			LogMessage("This spawn of type " + PoolName + " exceeds the limit");
			return;
		}
		
		// Create an unpooled instance
		if(m_pool.Count >= PoolSize)
		{
			LogMessage("Created an unpooled instance");
		    NewInstance(position, false, true);
		}
	}
	
	public void Despawn(int index, bool destroy, float destroyTime)
	{
		if(index > m_pool.Count)
		{
			LogMessage("Despawn index is out of range");
			return;
		}
		
		if(destroy)
		{
		    m_pool.RemoveAt(index);
			GameObject.Destroy(m_pool[index].gameObject, destroyTime);
			return;
		}	
		
		m_pool[index].Despawn();
	}	
	
	private bool NewInstance(Vector3 position, bool isPooled, bool isActive)
	{
		GameObject go;
		
		if(ResourcesLoad)
		{
			if(ResourcePath == string.Empty)
			{
				LogMessage("Resource Path is empty");
				return false;
			}
			
		    go = GameObject.Instantiate(Resources.Load(ResourcePath), position, Quaternion.identity) as GameObject;
		}
		else
		{
		    go = GameObject.Instantiate(Prefab, position, Quaternion.identity) as GameObject;
		}
		
		if(!isActive)
		{
			go.SetActive(false);
		}

		go.transform.parent = m_container;
		go.layer = Layer;
		
		if(isPooled)
		{
		    go.name = go.name + " 00" + m_pool.Count;
		}
		else
		{
	        go.name = go.name + "Unpooled";		
		}
		
		var script = go.AddComponent<PoolableType>();
		script.AutoDespawn = AutoDespawn;
		script.DespawnTime = DespawnTime;
		script.PooledType = PooledType;
		script.Initialize();

		if(m_pool.Count >= PoolSize)
		{
			script.DestroyOnComplete = true;
		}
		else
		{
			m_pool.Add(script);
		}
		
		if(!isActive)
		{
			return true;
		}
		
		script.Spawn(position);
		return true;
	}
	
	private bool ReInstance(Vector3 position)
	{
		
#if UNITY_FLASH		
		
		foreach(var pooledObject in m_pool)
		{
			if(pooledObject.gameObject.activeSelf == false)
			{
				pooledObject.Spawn(position);
				LogMessage("Instance Respawned");
				return true;
			}
		}
		
		LogMessage("No inactive Instances to use");
		return false;
		
#else
		var inactiveInstance = m_pool.FirstOrDefault(p => p.gameObject.activeSelf == false);
		
		if (inactiveInstance == null)
		{
			LogMessage("No inactive Instances to use");
			return false;
		}	
		
		inactiveInstance.Spawn(position);
		LogMessage("Respawn Successful");		
		return true;
#endif	

	}
		
	#endregion
	
	#region private methods
	
	private void LogMessage(string message)
	{
		if(LogMessages)
		{
			Debug.Log (message);
		}
	}
	
	#endregion
}
