/** @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.28 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2008-08-08 18:05:08 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

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

#include "psAbort.h"
#include "psTrace.h"
#include "psString.h"
#include "psTime.h"
#include "psThread.h"
#include "psEarthOrientation.h"
#include "psError.h"
#include "psFFT.h"
#include "psConfigure.h"
#include "psMemory.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

static const char *cvsTag = "$Name: not supported by cvs2svn $"; // CVS tag name

psString psLibVersion(void)
{
    psString version = NULL;            // Version, to return
    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
    return version;
}

psString psLibVersionLong(void)
{
    psString version = psLibVersion();    // Version, to return
    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag

#ifdef HAVE_PSDB
    psStringAppend(&version, " (cvs tag %s), %s, %s with psDB", tag, __DATE__, __TIME__);
#else
    psStringAppend(&version, " (cvs tag %s), %s, %s without psDB", tag, __DATE__, __TIME__);
#endif
    psFree(tag);
    return version;
}

// 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) {
        psWarning("%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
    nCorrupted = psMemCheckCorruption(memCheckFile, false);
    if (nCorrupted > 0) {
        psWarning("%d memory blocks corrupted; list written to %s.\n", nCorrupted, memCheckName);
    } else {
        psLogMsg(__func__, PS_LOG_INFO, "No memory corruption found.\n");
    }

    fclose(memCheckFile);

    return;
}


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

    if (timeConfig && strlen(timeConfig) > 0) {
        if (!psTimeInit(timeConfig)) {
            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
                    _("Failed to initialize %s."), "psTime");
            return false;
        }
    }

    // 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);
    }

    return true;
}

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

    // Stop timers
    psTimerStop();

    // Clean up FFTW threads
    psFFTThreads(0);

    // Clean up threads
    psThreadPoolFinalize();

    // Free the time tables
    if (!p_psTimeFinalize()) {
        psAbort(_("Failed to finalize psTime."));
    }

    // Free the precession tables
    if (!p_psEOCFinalize()) {
        psAbort(_("Failed to finalize psEOC."));
    }

    // Free the trace system
    psTraceReset();

    // Free the error system
    psErrorClear();
}
