
/** @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.26 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2005-02-18 22:17:20 $
 *
 *  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 "psCollectionsErrors.h"

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

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

    psArrayElementFree(psArr);

    psFree(psArr->data);
}

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

    // Create vector struct
    psArr = (psArray* ) psAlloc(sizeof(psArray));
    p_psMemSetDeallocator(psArr, (psFreeFcn) arrayFree);

    psArr->nalloc = nalloc;
    psArr->n = nalloc;

    // Create vector data array
    psArr->data = psAlloc(nalloc * sizeof(psPtr));

    return psArr;
}

psArray* psArrayRealloc(psArray* in, psU32 nalloc)
{
    if (in == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL,true,PS_ERRORTEXT_psArray_REALLOC_NULL);
        return NULL;
    } else 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));
        in->nalloc = nalloc;
    }

    return in;
}

psArray* psArrayAdd(psArray* psArr,
                    int delta,
                    psPtr data)
{
    if (psArr == NULL) {
        return psArr;
    }

    int n = psArr->n;

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

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

    return psArr;
}

psBool psArrayRemove(psArray* psArr,
                     psPtr data)
{
    psBool success = false;

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

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

    return success;
}

void psArrayElementFree(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* in, psComparePtrFcn compare)
{
    if (in == NULL) {
        return NULL;
    }

    qsort(in->data, in->n, sizeof(psPtr), (int (*)(const void* , const void*))compare);

    return in;
}

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

    if (position >= in->nalloc)
    {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                PS_ERRORTEXT_psArray_POSITION_BEYOND_NALLOC,
                position, in->nalloc);
        return false;
    }

    psFree(in->data[position]);
    in->data[position] = value;

    return true;
}

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

    if (position >= in->nalloc) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                PS_ERRORTEXT_psArray_POSITION_BEYOND_NALLOC,
                position, in->nalloc);
        return NULL;
    }

    return in->data[position];
}




