/*
 * Dynamically Resized Array
 *
 * Authors: Alfredo K. Kojima <kojima@windowmaker.org>
 *          Dan Pascu         <dan@windowmaker.org>
 *
 * This code is released to the Public Domain, but
 * proper credit is always appreciated :)
 */


#include <stdlib.h>
#include <string.h>

#include "WUtil.h"


#define INITIAL_SIZE 8
#define RESIZE_INCREMENT 8


typedef struct W_Array {
    void **items;		 /* the array data */
    int itemCount;	         /* # of items in array */
    int allocSize;	         /* allocated size of array */
    WMFreeDataProc *destructor;  /* the destructor to free elements */
} W_Array;


WMArray*
WMCreateArray(int initialSize)
{
    return WMCreateArrayWithDestructor(initialSize, NULL);
}


WMArray*
WMCreateArrayWithDestructor(int initialSize, WMFreeDataProc *destructor)
{
    WMArray *array;

    array = wmalloc(sizeof(WMArray));

    if (initialSize <= 0) {
        initialSize = INITIAL_SIZE;
    }

    array->items = wmalloc(sizeof(void*) * initialSize);

    array->itemCount = 0;
    array->allocSize = initialSize;
    array->destructor = destructor;

    return array;
}


WMArray*
WMCreateArrayWithArray(WMArray *array)
{
    WMArray *newArray;

    newArray = wmalloc(sizeof(WMArray));

    newArray->items = wmalloc(sizeof(void*) * array->allocSize);
    memcpy(newArray->items, array->items, sizeof(void*)*array->itemCount);

    newArray->itemCount = array->itemCount;
    newArray->allocSize = array->allocSize;
    newArray->destructor = NULL;

    return newArray;
}


void
WMEmptyArray(WMArray *array)
{
    if (array->destructor) {
        while (array->itemCount > 0) {
            array->itemCount--;
            array->destructor(array->items[array->itemCount]);
        }
    }
    /*memset(array->items, 0, array->itemCount * sizeof(void*));*/
    array->itemCount = 0;
}


void
WMFreeArray(WMArray *array)
{
    WMEmptyArray(array);
    wfree(array->items);
    wfree(array);
}


int
WMGetArrayItemCount(WMArray *array)
{
    return array->itemCount;
}


void
WMAppendArray(WMArray *array, WMArray *other)
{
    if (other->itemCount == 0)
        return;

    if (array->itemCount + other->itemCount > array->allocSize) {
        array->allocSize += other->allocSize;
        array->items = wrealloc(array->items, sizeof(void*)*array->allocSize);
    }

    memcpy(array->items+array->itemCount, other->items,
           sizeof(void*)*other->itemCount);
    array->itemCount += other->itemCount;
}


void
WMAddToArray(WMArray *array, void *item)
{
    if (array->itemCount >= array->allocSize) {
        array->allocSize += RESIZE_INCREMENT;
        array->items = wrealloc(array->items, sizeof(void*)*array->allocSize);
    }
    array->items[array->itemCount] = item;

    array->itemCount++;
}


void
WMInsertInArray(WMArray *array, int index, void *item)
{
    wassertr(index >= 0 && index <= array->itemCount);

    if (array->itemCount >= array->allocSize) {
        array->allocSize += RESIZE_INCREMENT;
        array->items = wrealloc(array->items, sizeof(void*)*array->allocSize);
    }
    if (index < array->itemCount) {
        memmove(array->items+index+1, array->items+index,
                sizeof(void*)*(array->itemCount-index));
    }
    array->items[index] = item;

    array->itemCount++;
}


void*
WMReplaceInArray(WMArray *array, int index, void *item)
{
    void *old;

    wassertrv(index >= 0 && index <= array->itemCount, NULL);

    /* is it really useful to perform append if index == array->itemCount ? -Dan */
    if (index == array->itemCount) {
        WMAddToArray(array, item);
        return NULL;
    }

    old = array->items[index];
    array->items[index] = item;

    return old;
}


int
WMDeleteFromArray(WMArray *array, int index)
{
    wassertrv(index >= 0 && index < array->itemCount, 0);

    if (array->destructor) {
        array->destructor(array->items[index]);
    }

    if (index < array->itemCount-1) {
        memmove(array->items+index, array->items+index+1,
                sizeof(void*)*(array->itemCount-index-1));
    }

    array->itemCount--;

    return 1;
}


int
WMRemoveFromArrayMatching(WMArray *array, WMMatchDataProc *match, void *cdata)
{
    int i;

    if (match != NULL) {
        for (i = 0; i < array->itemCount; i++) {
            if ((*match)(array->items[i], cdata)) {
                WMDeleteFromArray(array, i);
                return 1;
            }
        }
    } else {
        for (i = 0; i < array->itemCount; i++) {
            if (array->items[i] == cdata) {
                WMDeleteFromArray(array, i);
                return 1;
            }
        }
    }

    return 0;
}


void*
WMGetFromArray(WMArray *array, int index)
{
    if (index < 0 || index >= array->itemCount)
        return NULL;

    return array->items[index];
}


void*
WMPopFromArray(WMArray *array)
{
    array->itemCount--;

    return array->items[array->itemCount];
}


int
WMFindInArray(WMArray *array, WMMatchDataProc *match, void *cdata)
{
    int i;

    if (match!=NULL) {
        for (i = 0; i < array->itemCount; i++) {
            if ((*match)(array->items[i], cdata))
                return i;
        }
    } else {
        for (i = 0; i < array->itemCount; i++) {
            if (array->items[i] == cdata)
                return i;
        }
    }

    return WANotFound;
}


int
WMCountInArray(WMArray *array, void *item)
{
    int i, count;

    for (i=0, count=0; i<array->itemCount; i++) {
        if (array->items[i] == item)
            count++;
    }

    return count;
}


void
WMSortArray(WMArray *array, WMCompareDataProc *comparer)
{
    qsort(array->items, array->itemCount, sizeof(void*), comparer);
}


void
WMMapArray(WMArray *array, void (*function)(void*, void*), void *data)
{
    int i;

    for (i=0; i<array->itemCount; i++) {
        (*function)(array->items[i], data);
    }
}


WMArray*
WMGetSubarrayWithRange(WMArray* array, WMRange aRange)
{
    WMArray *newArray;

    if (aRange.count <= 0)
        return WMCreateArray(0);

    if (aRange.position < 0)
        aRange.position = 0;
    if (aRange.position >= array->itemCount)
        aRange.position = array->itemCount - 1;
    if (aRange.position + aRange.count > array->itemCount)
        aRange.count = array->itemCount - aRange.position;

    newArray = WMCreateArray(aRange.count);
    memcpy(newArray->items, array->items+aRange.position,
           sizeof(void*)*aRange.count);
    newArray->itemCount = aRange.count;

    return newArray;
}


void*
WMArrayFirst(WMArray *array, WMArrayIterator *iter)
{
    if (array->itemCount == 0) {
        *iter = WANotFound;
        return NULL;
    } else {
        *iter = 0;
        return array->items[0];
    }
}


void*
WMArrayLast(WMArray *array, WMArrayIterator *iter)
{
    if (array->itemCount == 0) {
        *iter = WANotFound;
        return NULL;
    } else {
        *iter = array->itemCount-1;
        return array->items[*iter];
    }
}


void*
WMArrayNext(WMArray *array, WMArrayIterator *iter)
{
    if (*iter >= 0 && *iter < array->itemCount-1) {
        return array->items[++(*iter)];
    } else {
        *iter = WANotFound;
        return NULL;
    }
}


void*
WMArrayPrevious(WMArray *array, WMArrayIterator *iter)
{
    if (*iter > 0 && *iter < array->itemCount) {
        return array->items[--(*iter)];
    } else {
        *iter = WANotFound;
        return NULL;
    }
}



