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

[Serializable]
public class ObjectPool
{
	public GameObject prefab;
	public int initialCount;
	public bool expandIfNeeded = true;
	public Transform poolContainer;

	public int remainingObjectsCount {
		get {
			return this.poolObjects.Count;
		}
	}

	private Queue<GameObject> poolObjects;
	private HashSet<GameObject> poolObjectsHashset;

	public void Initialize ()
	{
		this.poolObjects = new Queue<GameObject> (this.initialCount);
		this.poolObjectsHashset = new HashSet<GameObject> ();
		for (int i = 0; i < this.initialCount; i++) {
			var go = this.InstantiateGameObject ();
			this.poolObjects.Enqueue (go);
			this.poolObjectsHashset.Add (go);
		}
	}

	public void Initialize (GameObject prefab, int initialCount, bool expandIfNeeded)
	{
		this.prefab = prefab;
		this.initialCount = initialCount;
		this.expandIfNeeded = expandIfNeeded;
		
		this.Initialize ();
	}

	public GameObject GetFromPool ()
	{
		GameObject result = null;
		if (this.poolObjects.Count > 0) {
			result = this.poolObjects.Dequeue ();
			this.poolObjectsHashset.Remove (result);
		} else {
			#if UNITY_EDITOR
			Debug.Log ("Pool expanded - " + this.prefab.name);
			#endif
			
			if (this.expandIfNeeded == true) {
				result = this.InstantiateGameObject ();
			} else {
				throw new InvalidOperationException ("The pool has no more objects and expandIfNeeded is set to false");
			}
		}
		
		result.SetActive (true);
		//result.SendMessage ("Start");

		return result;
	}

	public GameObject GetFromPool (Vector3 position, Quaternion rotation)
	{
		var result = this.GetFromPool ();
		result.transform.position = position;
		result.transform.rotation = rotation;
		return result;
	}

	public void ReturnToPool (GameObject poolObject)
	{
		if (this.poolObjectsHashset.Contains (poolObject) == false) {
			poolObject.SetActive (false);
			poolObject.transform.parent = this.poolContainer;
			this.poolObjects.Enqueue (poolObject);
			this.poolObjectsHashset.Add (poolObject);
		}
	}

	//	public void Clear ()
	//	{
	//		foreach (var item in this.poolObjects) {
	//			GameObject.Destroy (item);
	//		}
	//		this.poolObjects.Clear ();
	//	}
	//
	//	public void Reset ()
	//	{
	//		this.poolObjects.Clear ();
	//		foreach (var item in this.poolObjects) {
	//			item.SetActive (false);
	//			availableGameObjects.Push (item);
	//		}
	//	}
	
	private GameObject InstantiateGameObject ()
	{
		var go = GameObject.Instantiate (prefab);
		go.name = this.prefab.name;
		go.transform.parent = this.poolContainer;
		go.AddComponent<AttachedValues> ();
		go.SetActive (false);
		return go;
	}
}