//
//  AutoreleasePool.cpp
//

#define DBG_LEVEL 0
#include <Raym/Log.h>
#include <Raym/AutoreleasePool.h>

namespace Raym
{

DEFINE_STATIC_MUTEX(poolsLock_);
typedef std::vector<AutoreleasePool *>      PoolStack;
#ifdef _WIN32
typedef std::map<DWORD, PoolStack>          PoolMap;
#else
typedef std::map<pthread_t, PoolStack>      PoolMap;
#endif
static PoolMap                              pools_;
static AutoreleasePool *                    rootPool_;

AutoreleasePool::AutoreleasePool()
{
    DebugLog2("AutoreleasePool::AutoreleasePool()");
}

AutoreleasePool::~AutoreleasePool()
{
    poolsLock_.lock();

#ifdef _WIN32
    PoolMap::iterator it = pools_.find(GetCurrentThreadId());
#else
    PoolMap::iterator it = pools_.find(pthread_self());
#endif
    if (it != pools_.end())
    {
        AutoreleasePool *pool = (*it).second.back();
        if (pool == this)
        {
            (*it).second.pop_back();
        }
        else
        {
            DebugLog0("Incorrect nesting of \'AutoreleasePool\'");
//            abort();
            exit(0);
        }
    }
    else
    {
        DebugLog0("\'AutoreleasePool\' is unavailable.");
//        abort();
        exit(0);
    }

    poolsLock_.unlock();

#if 0
    std::vector<Object *>::iterator it2;
    for (it2 = _objects.begin(); it2 != _objects.end(); ++it2)
    {
        (*it2)->release();
    }
    _objects.clear();
#else
    while (_objects.size() > 0)
    {
        _objects.back()->release();
        _objects.pop_back();
    }
#endif

    DebugLog2("AutoreleasePool::~AutoreleasePool()");
}

AutoreleasePool *AutoreleasePool::alloc()
{
    DebugLog2("AutoreleasePool::alloc()");

    return new AutoreleasePool();
}

AutoreleasePool *AutoreleasePool::init()
{
    DebugLog2("AutoreleasePool::init()");

    poolsLock_.lock();

#ifdef _WIN32
    PoolMap::iterator it = pools_.find(GetCurrentThreadId());
#else
    PoolMap::iterator it = pools_.find(pthread_self());
#endif
    if (it == pools_.end())
    {
        PoolStack stack;
        stack.push_back(this);
#ifdef _WIN32
        pools_.insert(std::make_pair(GetCurrentThreadId(), stack));
#else
        pools_.insert(std::make_pair(pthread_self(), stack));
#endif
    }
    else
    {
        (*it).second.push_back(this);
    }

    if (rootPool_ == NULL)
    {
        rootPool_ = this;
    }

    poolsLock_.unlock();

    return this;
}

void AutoreleasePool::add(Object *object)
{
    DebugLog2("AutoreleasePool::add(object)");

    if (object != NULL)
    {
        RaymLock(this);
        _objects.push_back(object);
        RaymUnlock(this);
    }
}

void AutoreleasePool::addObject(Object *object, bool rootPool)
{
    DebugLog2("AutoreleasePool::addObject(object,rootPool)");

    if (object != NULL)
    {
        poolsLock_.lock();

        if (rootPool)
        {
            if (rootPool_ != NULL)
            {
                rootPool_->add(object);
            }
            else
            {
                DebugLog0("\'AutoreleasePool\' is unavailable.");
//                abort();
                exit(0);
            }
        }
        else
        {
#ifdef _WIN32
            PoolMap::iterator it = pools_.find(GetCurrentThreadId());
#else
            PoolMap::iterator it = pools_.find(pthread_self());
#endif
            if (it != pools_.end())
            {
                (*it).second.back()->add(object);
            }
            else
            {
                DebugLog0("\'AutoreleasePool\' is unavailable.");
//                abort();
                exit(0);
            }
        }

        poolsLock_.unlock();
    }
}


void AutoreleasePool::addObject(Object *object)
{
    DebugLog2("AutoreleasePool::addObject(object)");

    addObject(object, false);
}

const char *AutoreleasePool::className()
{
    return "AutoreleasePool";
}

} // Raym
