// step3/memory_pool.h

#include <set>
#include <stdexcept>
#include <string>
#include <stddef.h>
#include <assert.h>

/* Defines Work-around for Microsoft Visual C++ 6.0 and Borland C++ 5.5.1 */
# if (defined(_MSC_VER) && _MSC_VER < 1300) \
        || (defined(__BORLANDC__) && __BORLANDC__ < 0x600)
#   define __PRIVATE public
# else
#   define __PRIVATE private
# endif

class MemoryPoolBase
{
public:
    virtual ~MemoryPoolBase() {};
    virtual void recycle() = 0;
    static void* allocSys(size_t uSize);
    static void deallocSys(void* pvReturn);
    struct BlockList { BlockList* m_pNext; };
};

class MemoryPoolSet
{
public:
    static MemoryPoolSet& instance();
    static void recycleMemoryPools();

    void add(MemoryPoolBase* pMemoryPool)
    {
        m_oMemoryPoolSet.insert(pMemoryPool);
    }
    void remove(MemoryPoolBase* pMemoryPool)
    {
        m_oMemoryPoolSet.erase(pMemoryPool);
    }

__PRIVATE:
    ~MemoryPoolSet();
private:
    MemoryPoolSet();
    void recycle();
    std::set<MemoryPoolBase*> m_oMemoryPoolSet;

    /* Forbid their use */
    MemoryPoolSet(const MemoryPoolSet&);
    const MemoryPoolSet& operator=(const MemoryPoolSet&);
};

template <size_t Sz>
class MemoryPool : public MemoryPoolBase
{
public:
    static MemoryPool& instance()
    {
        if (!s_pInstance)
        {
            createInstance();
        }
        return *s_pInstance;
    }
    static MemoryPool& instanceKnown()
    {
        assert(s_pInstance != NULL);
        return *s_pInstance;
    }
    void* allocate()
    {
        void* pvResult;
        if (s_pMemoryPool)
        {
            pvResult = s_pMemoryPool;
            s_pMemoryPool = s_pMemoryPool->m_pNext;
        }
        else
        {
            pvResult = allocSys(align(Sz));
        }
        return pvResult;
    }
    void deallocate(void* pvReturn)
    {
        assert(pvReturn != NULL);
        BlockList* pBlockList = reinterpret_cast<BlockList*>(pvReturn);
        pBlockList->m_pNext = s_pMemoryPool;
        s_pMemoryPool = pBlockList;
    }
    virtual void recycle();

private:
    MemoryPool()
    {
        MemoryPoolSet::instance().add(this);
    }
    ~MemoryPool()
    {
#   ifdef _DEBUG
        // Empty the pool to avoid false memory leakage alarms.  This is
        // generally not necessary for release binaries.
        BlockList* pBlockList = s_pMemoryPool;
        while (pBlockList)
        {
            BlockList* pBlockNext = pBlockList->m_pNext;
            deallocSys(pBlockList);
            pBlockList = pBlockNext;
        }
        s_pMemoryPool = NULL;
#   endif
        MemoryPoolSet::instance().remove(this);
        s_pInstance = NULL;
        s_fDestroyed = true;
    }
    static void onDeadReference()
    {
        throw std::runtime_error("dead reference detected");
    }
    static size_t align(size_t uSize)
    {
        return uSize >= sizeof(BlockList) ? uSize : sizeof(BlockList);
    }
    static void createInstance();

    static bool s_fDestroyed;
    static MemoryPool* s_pInstance;
    static BlockList* s_pMemoryPool;

    /* Forbid their use */
    MemoryPool(const MemoryPool&);
    const MemoryPool& operator=(const MemoryPool&);
};

template <size_t Sz> bool
        MemoryPool<Sz>::s_fDestroyed = false;
template <size_t Sz> MemoryPool<Sz>*
        MemoryPool<Sz>::s_pInstance = NULL;
template <size_t Sz> MemoryPoolBase::BlockList*
        MemoryPool<Sz>::s_pMemoryPool = NULL;

template <size_t Sz>
void MemoryPool<Sz>::recycle()
{
    BlockList* pBlockList = s_pMemoryPool;
    while (pBlockList)
    {
        if (BlockList* pBlockTemp = pBlockList->m_pNext)
        {
            BlockList* pBlockNext = pBlockTemp->m_pNext;
            pBlockList->m_pNext = pBlockNext;
            deallocSys(pBlockTemp);
            pBlockList = pBlockNext;
        }
        else
        {
            break;
        }
    }
}

template <size_t Sz>
void MemoryPool<Sz>::createInstance()
{
    if (s_fDestroyed)
    {
        onDeadReference();
    }
    else
    {
        if (!s_pInstance)
        {
            MemoryPoolSet::instance();  // Force its creation
            s_pInstance = new MemoryPool();
        }
    }
}

#undef __PRIVATE