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

public enum PooledObjectsEnum
{
    PhoneUnit,
    MessageGetParticle,
    PlayerSuccessParticle,
    PlayerSwapPar,
    PlayerDeadParticle,

    Count
}

#region Structs & Classes
public struct PoolableObjectStruct
{
    public string m_ObjectName;
    public GameObject m_GameObject;
    public int m_PoolAmount;

    public PoolableObjectStruct(string _ObjectName, GameObject _GameObject, int _PoolAmount)
    {
        m_ObjectName = _ObjectName;
        m_GameObject = _GameObject;
        m_PoolAmount = _PoolAmount;
    }
}

public struct PoolingDictionary
{
    public Queue<GameObject> m_PooledGameObjects;
    public GameObject m_GameObject;

    public PoolingDictionary(Queue<GameObject> _PooledGameObjects, GameObject _GameObject)
    {
        m_PooledGameObjects = _PooledGameObjects;
        m_GameObject = _GameObject;
    }
}

public class PoolableObject : MonoBehaviour
{
    string m_ObjectName;

    public virtual void InitiateObject(string _newName)
    {
        m_ObjectName = _newName;
    }

    public virtual void RemoveObject()
    {
        ObjectPooler.instance.DestroyObject(m_ObjectName, gameObject);
    }
}
#endregion

public class ObjectPooler : GenericSingleton<ObjectPooler>
{
    Dictionary<string, PoolingDictionary> m_ObjectPoolDictionary = new Dictionary<string, PoolingDictionary>();
    List<PoolableObjectStruct> m_PoolableObjects = new List<PoolableObjectStruct>();

    protected override void Awake()
    {
        singletonNonOneSceneEvent += (() => 
        {
            try
            {
                InitiatePoolableObjectsList();
                InitiatePoolingDictionary();
            }
            catch (System.Exception _Exception)
            {
                Debug.LogError("ObjectPooler Error: " + _Exception.ToString());
                Debug.LogWarning("No objects are pooled, or an error has occured!");
            }
        });

        base.Awake();
    }

    #region Awake Functions
    void InitiatePoolableObjectsList()
    {
        TextAsset _GetObjectPoolTextAsset = Resources.Load<TextAsset>("Prefabs/_ObjectPooler");
        JsonData _GetJsonData = JsonMapper.ToObject(_GetObjectPoolTextAsset.text);

        JsonData _GetSinglePoolableObj = _GetJsonData["ObjectPooler"];
        GameObject _GiveGameObject = null;
        string _GetPrefabPath = "";
        string _CurrentObjectName = "";

        for (int i = 0; i < _GetSinglePoolableObj.Count; i++)
        {
            _CurrentObjectName = _GetSinglePoolableObj[i]["PrefabName"].ToString();
            _GetPrefabPath = "Prefabs/" + _CurrentObjectName;
            _GiveGameObject = Resources.Load<GameObject>(_GetPrefabPath);
            m_PoolableObjects.Add(new PoolableObjectStruct(_CurrentObjectName, _GiveGameObject, System.Convert.ToInt32(_GetSinglePoolableObj[i]["InitialCount"].ToString())));
        }
    }

    public void InitiatePoolingDictionary()
    {
        for (int i = 0; i < m_PoolableObjects.Count; i++)
        {
            PoolableObjectStruct _CurrentObject = m_PoolableObjects[i];
            _CurrentObject.m_GameObject.SetActive(false);

            Queue<GameObject> _GetGameObjects = new Queue<GameObject>();

            for (int _gameObjects = 0; _gameObjects < _CurrentObject.m_PoolAmount; _gameObjects++)
            {
                GameObject _CreatedObject = CloneNewPooledObject(_CurrentObject.m_GameObject) as GameObject;
                _CreatedObject.transform.SetParent(transform);
                _GetGameObjects.Enqueue(_CreatedObject);
            }

            m_ObjectPoolDictionary.Add(_CurrentObject.m_ObjectName, new PoolingDictionary(_GetGameObjects, _CurrentObject.m_GameObject));
        }
    }
    #endregion

    GameObject MainObjectCreation(PooledObjectsEnum _WhichObject = PooledObjectsEnum.Count, Vector2? _Vector2Pos = null, Vector3? _Vector3Pos = null, Quaternion? _Rotation = null, Transform _GetTransform = null, bool _StayToWorldPos = true)
    { 
        GameObject _NewGameObject = GrabGameObjectFromDictionary(_WhichObject.ToString());
        _NewGameObject.SetActive(true);

        Transform _GetObjectTransform = _NewGameObject.transform;
        _GetObjectTransform.SetParent(_GetTransform ?? null, _StayToWorldPos);
        _GetObjectTransform.position = _Vector2Pos ?? Vector3.zero;

        //For Vector3 because the Z pos won't return above
        if (_Vector3Pos != null)
            _GetObjectTransform.position = _Vector3Pos.Value;

        _GetObjectTransform.localScale = Vector3.one;
        _GetObjectTransform.rotation = _Rotation ?? Quaternion.identity;

        return _NewGameObject;
    }

    GameObject GrabGameObjectFromDictionary(string _WhatObject)
    {
        Queue<GameObject> _GetGameObjectsQueue = m_ObjectPoolDictionary[_WhatObject].m_PooledGameObjects;

        GameObject _NewGameObject = (_GetGameObjectsQueue.Count > 0) ? _GetGameObjectsQueue.Dequeue() :
            CloneNewPooledObject(m_ObjectPoolDictionary[_WhatObject].m_GameObject) as GameObject;

        _NewGameObject.GetComponent<PoolableObject>().InitiateObject(_WhatObject);
        return _NewGameObject;
    }

    GameObject CloneNewPooledObject(GameObject _ReferenceGameObject)
    { 
        GameObject _GiveGameObject = Instantiate(_ReferenceGameObject);

        if (_GiveGameObject.GetComponent<PoolableObject>() == null)
            _GiveGameObject.AddComponent<PoolableObject>();

        return _GiveGameObject;
    }

    public void DestroyObject(string _objectName, GameObject _whichObject)
    {
        m_ObjectPoolDictionary[_objectName].m_PooledGameObjects.Enqueue(_whichObject);
        _whichObject.transform.SetParent(transform);
        _whichObject.SetActive(false);
    }

    #region CreateObject References
    //2D Object
    public GameObject CreateObject(PooledObjectsEnum _WhichObject, Vector2 _Position, Quaternion _GetRotation)
    {
        return MainObjectCreation(_WhichObject: _WhichObject, _Vector2Pos: _Position, _Rotation: _GetRotation);
    }

    //3D Object
    public GameObject CreateObject(PooledObjectsEnum _WhichObject, Vector3 _Position, Quaternion _GetRotation)
    {
        return MainObjectCreation(_WhichObject: _WhichObject, _Vector3Pos: _Position, _Rotation: _GetRotation);
    }

    //UI Object
    public GameObject CreateObject(PooledObjectsEnum _WhichObject, Transform _GetTransform, bool _StayToWorldPos)
    {
        return MainObjectCreation(_WhichObject: _WhichObject, _GetTransform: _GetTransform, _StayToWorldPos: false);
    }
    #endregion
}