
/** @file  psArray.c
 *
 *  @brief Contains support for basic vector types
 *
 *  This file defines the basic type for a vector struct and functions useful
 *  in manupulating vectors.
 *
 *  @author Ross Harman, MHPCC
 *
 *  @version $Revision: 1.40.10.1 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2006-03-09 02:42:56 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 */

/******************************************************************************/

/*  INCLUDE FILES                                                             */

/******************************************************************************/
#include<stdlib.h>                         // for qsort, etc.
#include<string.h>

#include "psMemory.h"
#include "psError.h"
#include "psArray.h"
#include "psLogMsg.h"

#include "psErrorText.h"

/*****************************************************************************
  FUNCTION IMPLEMENTATION - LOCAL
 *****************************************************************************/
static void arrayFree(psArray* psArr);

static void arrayFree(psArray* psArr)
{
    if (psArr == NULL) {
        return;
    }

    psArrayElementsFree(psArr);

    psFree(psArr->data);
}

bool psMemCheckArray(psPtr ptr)
{
    return ( psMemGetDeallocator(ptr) == (psFreeFunc)arrayFree );
}


/*****************************************************************************
  FUNCTION IMPLEMENTATION - PUBLIC
 *****************************************************************************/
psArray* psArrayAlloc(long nalloc)
{
    psArray* psArr = NULL;

    // Create vector struct
    psArr = (psArray* ) psAlloc(sizeof(psArray));
    psMemSetDeallocator(psArr, (psFreeFunc) arrayFree);

    P_PSARRAY_SET_NALLOC(psArr,nalloc);
    psArr->n = nalloc;

    // Create vector data array
    psArr->data = psAlloc(nalloc * sizeof(psPtr));
    // Set everything to NULL
    memset(psArr->data, (int)NULL, nalloc*sizeof(psPtr));

    return psArr;
}

psArray* psArrayRealloc(psArray* in,
                        long nalloc)
{
    if (in == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL,true,PS_ERRORTEXT_psArray_REALLOC_NULL);
        return NULL;
    }
    if (in->nalloc != nalloc) {         // No need to realloc to same size
        if (nalloc < in->n) {
            for (psS32 i = nalloc; i < in->n; i++) {      // For reduction in vector size
                psFree(in->data[i]);
            }
            in->n = nalloc;
        }
        // Realloc after decrementation to avoid accessing freed array elements
        in->data = psRealloc(in->data, nalloc * sizeof(psPtr));
        if (nalloc > in->nalloc) {
            // Expanding the array: set new pointers to NULL
            memset(&in->data[in->nalloc], (int)NULL, nalloc - in->nalloc);
        }
        P_PSARRAY_SET_NALLOC(in,nalloc);
    }

    return in;
}

psArray* psArrayAdd(psArray* array,
                    long delta,
                    psPtr data)
{
    if (array == NULL) {
        int d = (delta > 0) ? delta : 10; // as spec'ed in SDRS.
        array = psArrayAlloc(d);
        array->n = 0;
    }

    int n = array->n;

    if (n >= array->nalloc) {
        // array needs to be expanded to make room for more elements
        int d = (delta > 0) ? delta : 10; // as spec'ed in SDRS.
        array = psArrayRealloc(array, n+d);
    }

    // add the element to the end of the array.
    array->data[n] = psMemIncrRefCounter(data);
    array->n = n+1;

    return array;
}

bool psArrayRemove(psArray* array,
                   const psPtr data)
{
    bool success = false;

    if (array == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                PS_ERRORTEXT_psArray_ARRAY_NULL);
        return false;
    }

    psS32 n = array->n;
    psPtr* psArrData = array->data;
    for (psS32 i = n-1; i >= 0; i--) {
        if (psArrData[i] == data) {
            memmove(&array->data[i],&array->data[i+1],(n-i-1)*sizeof(psPtr));
            psFree(data); // free the removed item (see Bug #449)
            n--;
            success = true;
        }
    }
    array->n = n; // reset the array size to indicate the removed item(s)

    return success;
}

void psArrayElementsFree(psArray* psArr)
{

    if (psArr == NULL) {
        return;
    }

    for (psS32 i = 0; i < psArr->n; i++) {
        psFree(psArr->data[i]);
        psArr->data[i] = NULL;
    }
}

psArray* psArraySort(psArray* array,
                     psComparePtrFunc func)
{
    if (array == NULL) {
        return NULL;
    }

    qsort(array->data, array->n, sizeof(psPtr), (int (*)(const void* , const void*))func);

    return array;
}

/// Set an element in the array.
bool psArraySet(psArray* array,                      ///< input array to set element in
                long position,                      ///< the element position to set
                psPtr data)                        ///< the value to set it to
{
    if (array == NULL)
    {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                PS_ERRORTEXT_psArray_ARRAY_NULL);
        return false;
    }

    if (position > array->n)
    {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                "Specified position, %ld, is greater than n+1 of the array, %ld.",
                position, array->n);
        return false;
    }

    if (position < 0)
        position += array->n;
    if (position < 0)
    {
        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Negative number too large\n");
        return false;
    }

    if (position == array->n)
    {
        if (position >= array->nalloc) {
            psError(PS_ERR_BAD_PARAMETER_NULL, true,
                    PS_ERRORTEXT_psArray_POSITION_BEYOND_NALLOC,
                    position, array->nalloc);
            return false;
        }
        array->n++;
    }
    psFree(array->data[position]);
    array->data[position] = psMemIncrRefCounter(data);

    return true;
}

/// Get an element in the array.
psPtr psArrayGet(const psArray* array,
                 long position )
{
    if (array == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                PS_ERRORTEXT_psArray_ARRAY_NULL);
        return NULL;
    }

    if (position >= array->n) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                "Specified position, %ld, is greater than n+1 of the array, %ld.",
                position, array->n);
        return NULL;
    }
    if (position < 0)
        position += array->n;
    if (position < 0) {
        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Negative number too large\n");
        return NULL;
    }
    return psMemIncrRefCounter(array->data[position]);
}
