//
// Array.cpp
//

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

namespace Raym
{

Array::Array()
{
    DebugLog2("Array::Array()");

    _array.clear();
}

Array::~Array()
{
    for (unsigned int i = 0; i < _array.size(); ++i)
    {
        Object *obj = _array.at(i);
        obj->release();
    }
    _array.clear();

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

Array *Array::arrayWithCapacity(UInteger numItems)
{
    DebugLog2("Array::arrayWithCapacity(numItems)");

    Array *result = Array::alloc()->initWithCapacity(numItems);
    if (result != NULL)
    {
        result->autorelease();
    }
    return result;
}

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

    return new Array();
}

Array *Array::initWithCapacity(UInteger numItems)
{
    DebugLog2("Array::initWithCapacitiy(numItems)");

    return this;
}

Array *Array::retain()
{
    DebugLog2("Array::retain()");

    Object::retain();
    return this;
}

Array *Array::autorelease()
{
    DebugLog2("Array::autorelease()");

    Object::autorelease();
    return this;
}

UInteger Array::count()
{
    DebugLog2("Array::count()");

    UInteger result = 0;
    RaymLock(this);
    result = (UInteger)_array.size();
    RaymUnlock(this);

    return result;
}

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

    if (object != NULL)
    {
        RaymLock(this);
        object->retain();
        _array.push_back(object);
        RaymUnlock(this);
    }
}

void Array::addObjectsFromArray(Array *array)
{
    DebugLog2("Array::addObjectsFromArray(array)");

    if (array != NULL)
    {
        RaymLock(this);
        for (UInteger i = 0; i < array->count(); ++i)
        {
            Object *object = array->objectAtIndex(i);
            object->retain();
            _array.push_back(object);
        }
        RaymUnlock(this);
    }
}

Object *Array::objectAtIndex(UInteger index)
{
    DebugLog2("Array::objectAtIndex(index)");

    Object *result = NULL;
    RaymLock(this);
    if (index < _array.size())
    {
        result = _array.at(index);
    }
    RaymUnlock(this);

    return result;
}

void Array::insertObject(Object *object, UInteger index)
{
    DebugLog2("Array::insertObject(object,index)");

    RaymLock(this);
    if (object != NULL)
    {
        std::vector<Object *>::iterator it;
        UInteger idx = 0;
        for (it = _array.begin(); it != _array.end(); ++it)
        {
            if (idx == index)
            {
                object->retain();
                _array.insert(it, object);
                break;
            }
            ++idx;
        }
    }
    RaymUnlock(this);
}

void Array::removeObject(Object *object)
{
    DebugLog2("Array::removeObject(object)");

    RaymLock(this);
    std::vector<Object *>::iterator it;
    for (it = _array.begin(); it != _array.end(); ++it)
    {
        if (*it == object)
        {
            (*it)->release();
            _array.erase(it);
            break;
        }
    }
    RaymUnlock(this);
}

void Array::removeObjectAtIndex(UInteger index)
{
    DebugLog2("Array::removeObjectAtIndex(index)");

    RaymLock(this);
    std::vector<Object *>::iterator it;
    UInteger idx = 0;
    for (it = _array.begin(); it != _array.end(); ++it)
    {
        if (idx == index)
        {
            (*it)->release();
            _array.erase(it);
            break;
        }
        ++idx;
    }
    RaymUnlock(this);
}

void Array::removeAllObjects()
{
    DebugLog2("Array::removeAllObjects()");

    RaymLock(this);
    for (unsigned int i = 0; i < _array.size(); ++i)
    {
        Object *obj = _array.at(i);
        obj->release();
    }
    _array.clear();
    RaymUnlock(this);
}

Array *Array::sortedArrayUsingFunction(Integer (*function)(Object *, Object *, void *), void *context)
{
    DebugLog2("Array::sortedArrayUsingFunction()");

    Array *result = Array::arrayWithCapacity(0);

    RaymLock(this);

    for (unsigned int i = 0; i < _array.size(); ++i)
    {
        Object *obj = _array.at(i);
        bool inserted = false;
        if (result->count() > 0)
        {
            for (unsigned int j = 0; j < result->count(); ++j)
            {
                Object *obj2 = result->objectAtIndex(j);
                if (function(obj, obj2, context) == OrderedAscending)
                {
                    result->insertObject(obj, j);
                    inserted = true;
                    break;
                }
            }
        }
        if (!inserted)
        {
            result->addObject(obj);
        }
    }

    RaymUnlock(this);

    return result;
}

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

} // Raym
