/*
 * Copyright (C) 2008-2016 TrinityCore 
 * Copyright (C) 2005-2009 MaNGOS 
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see .
 */
#ifndef TRINITY_POOLHANDLER_H
#define TRINITY_POOLHANDLER_H
#include "Define.h"
#include "Creature.h"
#include "GameObject.h"
#include "QuestDef.h"
struct PoolTemplateData
{
    uint32  MaxLimit;
};
struct PoolObject
{
    ObjectGuid::LowType  guid;
    float   chance;
    PoolObject(ObjectGuid::LowType _guid, float _chance) : guid(_guid), chance(std::fabs(_chance)) { }
};
class Pool                                                  // for Pool of Pool case
{
};
typedef std::set ActivePoolObjects;
typedef std::map ActivePoolPools;
class ActivePoolData
{
    public:
        template
        bool IsActiveObject(uint32 db_guid_or_pool_id) const;
        uint32 GetActiveObjectCount(uint32 pool_id) const;
        template
        void ActivateObject(uint32 db_guid_or_pool_id, uint32 pool_id);
        template
        void RemoveObject(uint32 db_guid_or_pool_id, uint32 pool_id);
        ActivePoolObjects GetActiveQuests() const { return mActiveQuests; } // a copy of the set
    private:
        ActivePoolObjects mSpawnedCreatures;
        ActivePoolObjects mSpawnedGameobjects;
        ActivePoolObjects mActiveQuests;
        ActivePoolPools   mSpawnedPools;
};
template 
class PoolGroup
{
    typedef std::vector PoolObjectList;
    public:
        explicit PoolGroup() : poolId(0) { }
        void SetPoolId(uint32 pool_id) { poolId = pool_id; }
        ~PoolGroup() { };
        bool isEmpty() const { return ExplicitlyChanced.empty() && EqualChanced.empty(); }
        void AddEntry(PoolObject& poolitem, uint32 maxentries);
        bool CheckPool() const;
        PoolObject* RollOne(ActivePoolData& spawns, uint32 triggerFrom);
        void DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType guid=0);
        void Despawn1Object(ObjectGuid::LowType guid);
        void SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom);
        void Spawn1Object(PoolObject* obj);
        void ReSpawn1Object(PoolObject* obj);
        void RemoveOneRelation(uint32 child_pool_id);
        uint32 GetFirstEqualChancedObjectId()
        {
            if (EqualChanced.empty())
                return 0;
            return EqualChanced.front().guid;
        }
        uint32 GetPoolId() const { return poolId; }
    private:
        uint32 poolId;
        PoolObjectList ExplicitlyChanced;
        PoolObjectList EqualChanced;
};
typedef std::multimap PooledQuestRelation;
typedef std::pair PooledQuestRelationBounds;
typedef std::pair PooledQuestRelationBoundsNC;
class PoolMgr
{
    private:
        PoolMgr();
        ~PoolMgr() { };
    public:
        static PoolMgr* instance()
        {
            static PoolMgr instance;
            return &instance;
        }
        void LoadFromDB();
        void LoadQuestPools();
        void SaveQuestsToDB();
        void Initialize();
        template
        uint32 IsPartOfAPool(uint32 db_guid_or_pool_id) const;
        template
        bool IsSpawnedObject(uint32 db_guid_or_pool_id) const { return mSpawnedData.IsActiveObject(db_guid_or_pool_id); }
        bool CheckPool(uint32 pool_id) const;
        void SpawnPool(uint32 pool_id);
        void DespawnPool(uint32 pool_id);
        template
        void UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id);
        void ChangeDailyQuests();
        void ChangeWeeklyQuests();
        PooledQuestRelation mQuestCreatureRelation;
        PooledQuestRelation mQuestGORelation;
    private:
        template
        void SpawnPool(uint32 pool_id, uint32 db_guid_or_pool_id);
        uint32 max_pool_id;
        typedef std::vector       PoolTemplateDataMap;
        typedef std::vector >   PoolGroupCreatureMap;
        typedef std::vector > PoolGroupGameObjectMap;
        typedef std::vector >       PoolGroupPoolMap;
        typedef std::vector >      PoolGroupQuestMap;
        typedef std::pair           SearchPair;
        typedef std::map            SearchMap;
        PoolTemplateDataMap    mPoolTemplate;
        PoolGroupCreatureMap   mPoolCreatureGroups;
        PoolGroupGameObjectMap mPoolGameobjectGroups;
        PoolGroupPoolMap       mPoolPoolGroups;
        PoolGroupQuestMap      mPoolQuestGroups;
        SearchMap mCreatureSearchMap;
        SearchMap mGameobjectSearchMap;
        SearchMap mPoolSearchMap;
        SearchMap mQuestSearchMap;
        // dynamic data
        ActivePoolData mSpawnedData;
};
#define sPoolMgr PoolMgr::instance()
// Method that tell if the creature is part of a pool and return the pool id if yes
template<>
inline uint32 PoolMgr::IsPartOfAPool(uint32 db_guid) const
{
    SearchMap::const_iterator itr = mCreatureSearchMap.find(db_guid);
    if (itr != mCreatureSearchMap.end())
        return itr->second;
    return 0;
}
// Method that tell if the gameobject is part of a pool and return the pool id if yes
template<>
inline uint32 PoolMgr::IsPartOfAPool(uint32 db_guid) const
{
    SearchMap::const_iterator itr = mGameobjectSearchMap.find(db_guid);
    if (itr != mGameobjectSearchMap.end())
        return itr->second;
    return 0;
}
// Method that tell if the quest is part of another pool and return the pool id if yes
template<>
inline uint32 PoolMgr::IsPartOfAPool(uint32 pool_id) const
{
    SearchMap::const_iterator itr = mQuestSearchMap.find(pool_id);
    if (itr != mQuestSearchMap.end())
        return itr->second;
    return 0;
}
// Method that tell if the pool is part of another pool and return the pool id if yes
template<>
inline uint32 PoolMgr::IsPartOfAPool(uint32 pool_id) const
{
    SearchMap::const_iterator itr = mPoolSearchMap.find(pool_id);
    if (itr != mPoolSearchMap.end())
        return itr->second;
    return 0;
}
#endif