/** @file  psConfigure.c
 *
 *  @brief Contains the declarations for initialization, memory finalization,
 *   and configuration.
 *
 *  These functions initalize psLib data before the beginning of a run and
 *  remove (finalize) the same data after the run is complete. A function is
 *  also provided to return the current psLib version.
 *
 *  @ingroup Configure
 *
 *  @author Ross Harman, MHPCC
 *  @author Robert DeSonia, MHPCC
 *
 *  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2006-06-23 01:59:15 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "psMemory.h"
#include "psTrace.h"
#include "psString.h"
#include "psTime.h"
#include "psEarthOrientation.h"
#include "psError.h"
#include "psConfigure.h"
#include "psErrorText.h"
#include "config.h"

static char *memCheckName = NULL;       // Filename to which to write results of mem check
static FILE *memCheckFile = NULL;       // File to which to write results of mem check

char* psLibVersion(void)
{
    char version[80];
    snprintf(version,80,"%s-v%s",PACKAGE_NAME,PACKAGE_VERSION);

    return psStringCopy(version);
}

// Print details of a memory problem to the appropriate file
static void memoryProblem(const psMemBlock *ptr, // the pointer to the problematic memory block.
                          const char *file, // the file in which the problem originated
                          int lineno    // the line number in which the problem originated
                         )
{
    fprintf(memCheckFile,
            "Memory corruption detected in memBlock %lu\n"
            "\tFile %s, line %d, size %zd\n"
            "\tPosts: %p %p %p\n",
            ptr->id, file, lineno, ptr->userMemorySize, ptr->startblock, ptr->endblock,
            (ptr + 1 + ptr->userMemorySize));
}

// Check the memory; intended for use on exit, but might be used elsewhere
void p_psMemoryCheck(void)
{
    if (!memCheckName || strlen(memCheckName) == 0) {
        return;
    }

    memCheckFile = fopen(memCheckName, "w"); // File to write leaks to
    if (!memCheckFile) {
        psError(PS_ERR_IO, true, "Unable to open leaks file, %s\n", memCheckName);
        return;
    }

    int nLeaks = psMemCheckLeaks(0, NULL, memCheckFile, false); // Number of leaks
    if (nLeaks > 0) {
        psLogMsg(__func__, PS_LOG_WARN, "%d memory leaks found; list written to %s.\n", nLeaks, memCheckName);
    } else {
        psLogMsg(__func__, PS_LOG_INFO, "No memory leaks found.\n");
    }

    int nCorrupted;                     // Number of corrupted memory blocks
    (void)psMemProblemCallbackSet((psMemProblemCallback)memoryProblem); // Set callback for corruption
    nCorrupted = psMemCheckCorruption(false);
    if (nCorrupted > 0) {
        psError(PS_ERR_UNKNOWN, true, "%d memory blocks corrupted; list written to %s.\n", nCorrupted);
    } else {
        psLogMsg(__func__, PS_LOG_INFO, "No memory corruption found.\n");
    }

    fclose(memCheckFile);

    return;
}


void psLibInit(const char* timeConfig)
{
    // XXX: Still needs error codes to be set

    if (timeConfig && strlen(timeConfig) > 0) {
        if (!p_psTimeInit(timeConfig)) {
            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
                    PS_ERRORTEXT_psConfigure_INITIALIZATION_FAILED, "psTime");
            return;
        }
    }
    if (!p_psEOCInit()) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
                PS_ERRORTEXT_psConfigure_INITIALIZATION_FAILED, "psEOC");
        return;
    }

    // Does the user want memory checking at exit?
    memCheckName = getenv("PS_ALLOC_CHECK"); // The value of PS_ALLOC_CHECK
    if (memCheckName && strlen(memCheckName) > 0) {
        atexit(&p_psMemoryCheck);
    }
}

void psLibFinalize(void)
{
    // Users of persistent memory should free them in this function

    // Stop timers
    psTimerStop();

    // Free the time tables
    if (!p_psTimeFinalize()) {
        psError(PS_ERR_UNKNOWN, false,
                PS_ERRORTEXT_psConfigure_FINALIZATION_FAILED, "psTime");
        return;
    }

    // Free the precession tables
    if (!p_psEOCFinalize()) {
        psError(PS_ERR_UNKNOWN, false,
                PS_ERRORTEXT_psConfigure_FINALIZATION_FAILED, "psEOC");
        return;
    }

    // Free the trace system
    psTraceReset();

    // Free the error system
    psErrorClear();

}
