/*
 * zList.c
 * 
 * The Zen API 
 * Copyright (C) 2010 by LMI Technologies Inc.
 * 
 * Licensed under The MIT License.
 * Redistributions of files must retain the above copyright notice.
 */
#include <z/zzList.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>

ZAPI_EXPORT(zStatus) zList_Construct(zList* list, zUIntPtr itemSize, z32u initialCapacity)
{
    zList output = zNIL; 
    zListClass *obj = zNULL;
    zStatus exception; 

    if (itemSize == 0)
    {
        return zERROR_PARAMETER; 
    }

    zCHECK(zCalloc(&output, 1, sizeof(zListClass))); 
    obj = zHANDLE_POINTER(output); 

    zEXCEPT_TRY
    {
        obj->itemSize = itemSize; 

        zEXCEPT_CHECK(zList_EnsureCapacity(output, initialCapacity)); 

        *list = output;         
    }
    zEXCEPT_CATCH(&exception)
    {
        zFree(obj->items); 
        zFree(obj); 

        zEXCEPT_SET(exception); 
    }
    zEXCEPT_FINALLY
    {
        zEXCEPT_CLOSE(); 
    }
    
    return zOK; 
}

ZAPI_EXPORT(zStatus) zList_Destroy(zList list)
{
    zListClass *obj = zHANDLE_POINTER(list); 

    if (!zIS_NULL(obj))
    {
        zFree(obj->items); 
        zFree(obj); 
    }

    return zOK; 
}

ZAPI_EXPORT(zStatus) zList_Alloc(zList list, zUIntPtr itemSize, z32u initialCapacity)
{
    zListClass *obj = zHANDLE_POINTER(list); 

    if (itemSize == 0)
    {
        return zERROR_PARAMETER; 
    }

    zCHECK(zFree(obj->items)); 
    
    obj->items = zNULL; 
    obj->itemSize = itemSize; 
    obj->capacity = 0; 
    obj->count = 0; 

    zCHECK(zList_EnsureCapacity(list, initialCapacity)); 

    return zOK; 
}

ZAPI_EXPORT(zStatus) zList_Assign(zList list, zList source)
{
    zListClass *obj = zHANDLE_POINTER(list); 
    zListClass *srcObj = zHANDLE_POINTER(source); 

    if ((obj->itemSize != srcObj->itemSize) || (obj->capacity != srcObj->capacity))
    {
        zFree(obj->items); 
        obj->items = zNULL; 
        obj->capacity = 0; 
    }

    if (zIS_NULL(obj->items) && (srcObj->capacity > 0))
    {
        zCHECK(zCalloc(&obj->items, srcObj->capacity, srcObj->itemSize)); 
    }

    obj->itemSize = srcObj->itemSize; 
    obj->capacity = srcObj->capacity; 
    obj->count = srcObj->count; 

    memcpy(obj->items, srcObj->items, obj->itemSize*obj->count); 

    return zOK;   
}

ZAPI_EXPORT(zStatus) zList_Clear(zList list)
{
    zListClass *obj = zHANDLE_POINTER(list); 

    obj->count = 0; 

    return zOK; 
}

ZAPI_EXPORT(zStatus) zList_EnsureCapacity(zList list, z32u minimumCapacity)
{
    zListClass *obj = zHANDLE_POINTER(list); 

    if (obj->capacity < minimumCapacity)
    {
        z32u newCapacity = 0; 
        void* newItems = zNULL; 

        newCapacity = zMATH_MAX(minimumCapacity, zLIST_GROWTH_FACTOR*obj->capacity);
        newCapacity = zMATH_MAX(newCapacity, zLIST_MIN_CAPACITY); 

        zCHECK(zCalloc(&newItems, newCapacity, obj->itemSize)); 

        memcpy(newItems, obj->items, obj->itemSize*obj->count);        
        zFree(obj->items); 

        obj->items = newItems; 
        obj->capacity = newCapacity; 
    }
    
    return zOK; 
}


ZAPI_EXPORT(zStatus) zList_Add(zList list, const void* item)
{
    return zList_Insert(list, zList_Count(list), item); 
}

ZAPI_EXPORT(zStatus) zList_SetItem(zList list, z32u index, const void* item)
{
    zListClass *obj = zHANDLE_POINTER(list); 

    if (index >= obj->count)
    {
        return zERROR_PARAMETER; 
    }

    memcpy(zList_At(list, index), item, obj->itemSize); 

    return zOK; 
}

ZAPI_EXPORT(zStatus) zList_Item(zList list, z32u index, void* item)
{
    zListClass *obj = zHANDLE_POINTER(list); 

    if (index >= obj->count)
    {
        return zERROR_PARAMETER; 
    }

    memcpy(item, zList_At(list, index), obj->itemSize); 

    return zOK; 
}

ZAPI_EXPORT(zStatus) zList_Insert(zList list, z32u before, const void* item)
{
    zListClass *obj = zHANDLE_POINTER(list); 

    if (before > obj->count)
    {
        return zERROR_PARAMETER; 
    }

    if (obj->count == obj->capacity)
    {
        zCHECK(zList_EnsureCapacity(list, obj->capacity + 1)); 
    }

    if (before != obj->count)
    {
        memmove(zList_At(list, before + 1), zList_At(list, before), obj->itemSize*(obj->count - before)); 
    }

    memcpy(zList_At(list, before), item, obj->itemSize); 

    obj->count++; 

    return zOK; 
}

ZAPI_EXPORT(zStatus) zList_Remove(zList list, z32u index, void* item)
{
    zListClass *obj = zHANDLE_POINTER(list); 

    if (index >= obj->count)
    {
        return zERROR_PARAMETER; 
    }

    if (!zIS_NULL(item))
    {
        memcpy(item, zList_At(list, index), obj->itemSize); 
    }

    if (index != (obj->count-1))
    {
        memmove(zList_At(list, index), zList_At(list, index + 1), obj->itemSize*(obj->count - index - 1)); 
    }

    obj->count--; 

    return zOK; 
}

ZAPI_EXPORT(void*) zList_Data(zList list)
{
    zListClass *obj = zHANDLE_POINTER(list); 
    return obj->items; 
}

ZAPI_EXPORT(void*) zList_At(zList list, z32u index)
{
    zListClass *obj = zHANDLE_POINTER(list); 
    return (zByte*)(obj->items) + obj->itemSize*index; 
}

ZAPI_EXPORT(zUIntPtr) zList_ItemSize(zList list)
{
    zListClass *obj = zHANDLE_POINTER(list); 
    return obj->itemSize; 
}

ZAPI_EXPORT(z32u) zList_Count(zList list)
{
    zListClass *obj = zHANDLE_POINTER(list); 
    return obj->count; 
}
