/** @file  psErrorCodes.c
 *
 *  @brief Contains the error codes for the error classes
 *
 *  @ingroup ErrorHandling
 *
 *  @author Robert DeSonia, MHPCC
 *
 *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2005-02-02 20:22:22 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 */

#include <string.h>

#include "psError.h"
#include "psErrorCodes.h"
#include "psList.h"
#include "psMemory.h"

#include "psSysUtilsErrors.h"

/* N.B., lines between '//~Start' and '//~End' are automatic generated from
 * the template following the '//~Start'.  The template is used to generate
 * the other lines by, for each error class in psErrorCodes.dat, the following
 * substitutions are made:
 *     $1  The error code name (first word in the psErrorCodes.dat lines)
 *     $2  The error description (rest of the line in psErrorCodes.dat)
 *     $n  The order of the source line in psErrorCodes.dat (comments excluded)
 * 
 * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
 */

static psErrorDescription staticErrorCodes[] = {
            {PS_ERR_NONE,"not an error"},
            {PS_ERR_BASE,"base error"},
            //~Start    {PS_ERR_$1,"$2"},
            {PS_ERR_UNKNOWN,"unknown error"},
            {PS_ERR_IO,"I/O error"},
            {PS_ERR_LOCATION_INVALID,"specified location is unknown"},
            {PS_ERR_MEMORY_CORRUPTION,"memory corruption detected"},
            {PS_ERR_MEMORY_DEREF_USAGE,"dereferenced memory still used"},
            {PS_ERR_BAD_PARAMETER_VALUE,"parameter is out-of-range"},
            {PS_ERR_BAD_PARAMETER_TYPE,"parameter is of unsupported type"},
            {PS_ERR_BAD_PARAMETER_NULL,"parameter is null"},
            {PS_ERR_BAD_PARAMETER_SIZE,"size of parameter's data is outside of acceptable range."},
            {PS_ERR_UNEXPECTED_NULL,"unexpected NULL found"},
            {PS_ERR_OS_CALL_FAILED,"unexpected result from an OS standard library call"},
            //~End
            {PS_ERR_N_ERR_CLASSES,"error classes end marker"}
        };

static psList* dynamicErrorCodes = NULL;
static const psErrorDescription* getErrorDescription(psErrorCode code);


static const psErrorDescription* getErrorDescription(psErrorCode code)
{
    // first, search the static error codes

    psS32 n = 0;
    while(staticErrorCodes[n].code != PS_ERR_N_ERR_CLASSES &&
            staticErrorCodes[n].code != code) {
        n++;
    }

    if (staticErrorCodes[n].code == code) {
        return &staticErrorCodes[n];
    } else {
        psErrorDescription* desc;
        // make sure there is a list to search
        if (dynamicErrorCodes == NULL) {
            return NULL;
        }

        // search dynamic list of error descriptions before giving up.
        psListIterator* iter = psListIteratorAlloc(dynamicErrorCodes,PS_LIST_HEAD,true);
        while ((desc = (psErrorDescription*)psListGetAndIncrement(iter)) != NULL) {
            if (desc->code == code) {
                psFree(iter);
                return desc;
            }
        }
        psFree(iter);
    }
    return NULL;
}

static void freeErrorDescription(psErrorDescription* err)
{
    psFree((psPtr)err->description);
}

psErrorDescription* psErrorDescriptionAlloc(psErrorCode code,
        const char *description)
{
    psErrorDescription* err = psAlloc(sizeof(psErrorDescription));
    err->code = code;
    if (description == NULL) {
        err->description = NULL;
    } else {
        err->description = psAlloc(sizeof(char)*strlen(description)+1);
        strcpy((char*)err->description,description);
    }

    p_psMemSetDeallocator(err,(psFreeFcn)freeErrorDescription);
    return err;
}


const char *psErrorCodeString(psErrorCode code)
{
    // Check input argument is non-negative
    if ( code < 0 ) {
        return NULL;
    }

    const psErrorDescription* desc = getErrorDescription(code);

    if (desc == NULL) {
        return NULL;
    }

    return desc->description;
}

void psErrorRegister(const psErrorDescription* errors,
                     psS32 nerror)
{
    if (nerror < 1) {
        return;
    }

    if (errors == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                PS_ERRORTEXT_psErrorCode_NULL_ERRORDESCRIPTION);
        return;
    }

    if (dynamicErrorCodes == NULL) {
        dynamicErrorCodes = psListAlloc(NULL);
        p_psMemSetPersistent(dynamicErrorCodes,true);

        p_psMemSetPersistent(dynamicErrorCodes->iterators,true);
        p_psMemSetPersistent(dynamicErrorCodes->iterators->data,true);
        for (int i = 0; i < dynamicErrorCodes->iterators->n;i++) {
            p_psMemSetPersistent(dynamicErrorCodes->iterators->data[i],true);
        }
    }

    for (psS32 i=0;i<nerror;i++) {
        psErrorDescription* err = psErrorDescriptionAlloc(
                                      errors[i].code, errors[i].description);
        p_psMemSetPersistent(err,true);
        p_psMemSetPersistent((psPtr)err->description,true);
        if (! psListAdd(dynamicErrorCodes,
                        PS_LIST_HEAD,
                        err) ) {

            psError(PS_ERR_UNKNOWN, false,
                    PS_ERRORTEXT_psErrorCode_ERRORCODE_REGISTER_FAILED,
                    i);
        }
        p_psMemSetPersistent(dynamicErrorCodes->head,true);
        psFree(err);
    }
}

psBool p_psErrorUnregister(psErrorCode code)
{
    // Check input argument is non-negative
    if ( code < 0 ) {
        return false;
    }

    const psErrorDescription* desc = getErrorDescription(code);

    if (desc == NULL) {
        return false;
    }

    if (dynamicErrorCodes == NULL) {
        return false;
    }

    return psListRemoveData(dynamicErrorCodes,(psPtr)desc);
}
