/** @file ippToPsps.c
 *
 *  @ingroup ippToPsps
 *
 *  Source for all methods in the Batch class
 *
 *  @author IfA
 *  Copyright 2009 Institute for Astronomy, University of Hawaii
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <sys/stat.h>

#include <pslib.h>
#include <psmodules.h>

#include "Batch.h"

// private class members
static bool haveResultsPath = false;
static bool haveFitsInPath = false;
static bool haveFitsOutPath = false;
static bool haveConfig = false;
static bool haveSurveyType = false;

// some getter methods
static bool gotResultsPath() {return haveResultsPath;}
static bool gotFitsInPath() {return haveFitsInPath;}
static bool gotFitsOutPath() {return haveFitsOutPath;}
static bool gotConfig() {return haveConfig;}
static bool gotSurveyType() {return haveSurveyType;}

/**
  Gets uncalibrated instrumental flux from magnitude
  */
__inline bool getFlux(
        const float exposureTime, 
        const float magnitude, 
        float* flux,
        const float magnitudeErr,
        float* fluxErr) {

    *flux = powf(10.0, -0.4*magnitude) / exposureTime;
    if (!isfinite(*flux) || *flux < 0.000001) return false;
    if (fluxErr) *fluxErr = fabsf((magnitudeErr * *flux)/1.085736);

    return true;
}

/**
  Destructor
  */
static void destroy(Batch* this) {

    // if we created an output file...
    if (this->fitsOut) {

        // if unsuccessful, then delete FITS file
        if (this->exitCode != PS_EXIT_SUCCESS) {

            this->logger->print(this->logger, MSG_ERROR, "Batch", "Failed, so deleting fits file\n");
            this->fitsOut->delete(this->fitsOut);
        }

        this->fitsOut->destroy(this->fitsOut);
    }

    // save XML document for results
    if (this->resultsXmlDoc) {

        xmlSaveFormatFileEnc(this->resultsPath, this->resultsXmlDoc, "UTF-8", 1);

        xmlFreeDoc(this->resultsXmlDoc);
        xmlCleanupParser();
        xmlMemoryDump();
    }

    psFree(this->pmconfig);

    for(uint32_t i=0; i<this->numOfInputFiles; i++) free(this->inputFiles[i]);
    free(this->inputFiles);

    if (this->dvoConfig != NULL) dvoConfigFree(this->dvoConfig);

    // destroy objects
    this->fitsGenerator->destroy(this->fitsGenerator);
    this->initData->destroy(this->initData);

    pmConfigDone();

    return;
}

/**
  Reads file-input file and populate array of paths
  */
static bool readInputFilePaths(Batch* this) {

    FILE* file = fopen (this->fitsInPath, "r");

    if (file == NULL) {

        this->logger->print(this->logger, MSG_ERROR, "Batch", "Unable to open file at %s\n", this->fitsInPath);
        return false;
    }

    this->numOfInputFiles = 0;
    char line[1000];
    while (fgets(line, 1000, file) != NULL) this->numOfInputFiles++;

    this->logger->print(this->logger, MSG_INFO, "Batch", 
            "%d input file%s found\n", this->numOfInputFiles, (this->numOfInputFiles > 1) ? "s" : "");

    if (this->numOfInputFiles < 1) return false;

    this->inputFiles = (char**)calloc(this->numOfInputFiles, sizeof(char*));
    for(uint32_t i=0; i<this->numOfInputFiles; i++)
        this->inputFiles[i] = (char*)calloc(1000, 1);

    rewind(file);
    uint16_t i = 0;
    while (fgets(line, 1000, file) != NULL) {

        sprintf(this->inputFiles[i], "%s", line);

        // remove newline
        for(uint16_t j=0;j<strlen(this->inputFiles[i]);j++)
            if (this->inputFiles[i][j] == '\n') this->inputFiles[i][j] = '\0';
        i++;
    }

    fclose(file);

    return true;
}

/**
  Sets-up and reads command-line arguments
*/
static bool parseArguments(Batch* this, int argc, char **argv, char* configsDir, char* fitsOutFile) {

    // first deal with DVO database
    bool haveDvo = false;
    for (int i=0;i<argc; i++) {

        if (strcmp(argv[i], "CATDIR") == 0) {haveDvo = true; break;}
    }
    if (haveDvo) this->dvoConfig = dvoConfigRead(&argc, argv);
    else this->dvoConfig = NULL;

    // setup pmconfig
    this->pmconfig = pmConfigRead(&argc, argv, NULL);
    if (!this->pmconfig) {

        this->logger->print(this->logger, MSG_ERROR, "Batch", "Unable to read configuration\n");
        this->exitCode = PS_EXIT_CONFIG_ERROR;
        return false;
    }
    assert(this->pmconfig);

    haveResultsPath = false;
    haveFitsInPath = false;
    haveFitsOutPath = false;
    haveConfig = false;
    haveSurveyType = false;

    char fitsOutPath[1000];
    char configsBaseDir[1000];

    // decode arguments
    int32_t optind = 1;
    while( ( optind < argc ) ) {

        char * sw = argv[optind];

        if( argv[optind][0] == '-' ) {

            if(strcmp(sw, "-input") == 0 ) {
                optind++; if( optind > ( argc-1 ) ) break;
                strcpy(this->fitsInPath, argv[optind]);
                haveFitsInPath = true;
            }
            else if(strcmp(sw, "-output") == 0 ) {
                optind++; if( optind > ( argc-1 ) ) break;
                strcpy(fitsOutPath, argv[optind]);
                haveFitsOutPath = true;
            }
            else if(strcmp(sw, "-results") == 0 ) {
                optind++; if( optind > ( argc-1 ) ) break;
                strcpy(this->resultsPath, argv[optind]);
                haveResultsPath = true;
            }
            else if(strcmp(sw, "-config") == 0 ) {
                optind++; if( optind > ( argc-1 ) ) break;
                strcpy(configsBaseDir, argv[optind]);
                haveConfig = true;
            }
            else if(strcmp(sw, "-survey") == 0 ) {
                optind++; if( optind > ( argc-1 ) ) break;
                strcpy(this->surveyType, argv[optind]);
                haveSurveyType = true;
            }
            else if(strcmp(sw, "-test") == 0 ) {
                this->testMode = true;
            }
        }

        optind++;
    }

    // check we have some input paths
    if (haveFitsInPath && !readInputFilePaths(this)) {

        this->exitCode = PS_EXIT_DATA_ERROR;
        return false;
    }

    // create an InitData object and get survey ID
    this->initData = new_InitData(configsBaseDir, this->logger);
    if (strlen(this->surveyType) > 0 && 
            !this->initData->getSurveyId(this->initData, this->surveyType, &this->surveyID)) {

        this->exitCode = PS_EXIT_CONFIG_ERROR;
        return false;
    }

    // create a FitsGenerator object
    strcat(configsBaseDir, configsDir);
    this->fitsGenerator = new_FitsGenerator(this->logger, configsBaseDir);

    // create full FITS out path
    sprintf(fitsOutPath, "%s/%s", fitsOutPath, fitsOutFile);

    // create an output FITS file
    this->fitsOut = new_Fits(fitsOutPath, this->logger);
    if (this->fitsOut->getFilePtr(this->fitsOut) == NULL) return false;

    // create XML document for results 
    if (this->resultsPath) {

        this->resultsXmlDoc = xmlNewDoc(BAD_CAST "1.0");
        xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST "ippToPsps_Results");
        xmlDocSetRootElement(this->resultsXmlDoc, rootNode);
        xmlNewChild(rootNode, NULL, BAD_CAST "filename", BAD_CAST fitsOutFile);
    }

    return true;
}

/**
  Print-out of this class
  */
static void print(Batch* this) {

    this->logger->print(this->logger, MSG_INFO, "Batch", "\n");
    this->logger->print(this->logger, MSG_INFO, "Batch", "     Class fields:\n");
    this->logger->print(this->logger, MSG_INFO, "Batch", "surveyType      : %s\n", this->surveyType);
    this->logger->print(this->logger, MSG_INFO, "Batch", "surveyID        : %d\n", this->surveyID);
    this->logger->print(this->logger, MSG_INFO, "Batch", "resultsPath     : %s\n", this->resultsPath);
    this->logger->print(this->logger, MSG_INFO, "Batch", "numOfInputFiles : %d\n", this->numOfInputFiles);
    this->logger->print(this->logger, MSG_INFO, "Batch", "fitsInPath      : %s\n", this->fitsInPath);
    this->logger->print(this->logger, MSG_INFO, "Batch", "testMode        : %s\n", this->testMode ? "yes" : "no");
}



#if 0
psDB *dbh = psMemIncrRefCounter(pmConfigDB(this->pmconfig));
if (!dbh) {
    psError(PS_ERR_UNKNOWN, false, "Can't configure database");
    ret = false;;
}



psString query = psStringCopy("SELECT * FROM camMask");

if (!p_psDBRunQuery(dbh, query)) {
    psError(PS_ERR_UNKNOWN, false, "database error");
    psFree(query);
    return false;
}
psFree(query);

psArray *output = p_psDBFetchResult(dbh);
if (!output) {
    psError(PS_ERR_UNKNOWN, false, "database error");
    return false;
}
if (!psArrayLength(output)) {
    psTrace("camtool", PS_LOG_INFO, "no rows found");
    psFree(output);
    return true;
}

psError(PS_ERR_UNKNOWN, false, "FOUND %ld rows\n", psArrayLength(output));


psFree(output);

#endif


/**
  Constructor. Takes a Batch object and initialises it.
  */
bool new_Batch(Logger* logger, Batch *this) {

    this->logger = logger;
    this->surveyID = -1;
    this->resultsXmlDoc = NULL;
    this->numOfInputFiles = 0;
    this->inputFiles = NULL;
    this->fitsOut = NULL;
    this->fitsGenerator = NULL;
    this->pmconfig = NULL;
    this->exitCode = PS_EXIT_SUCCESS;
    this->testMode = false;

    // set up function pointers
    this->parseArguments = parseArguments;
    this->print = print;
    this->gotResultsPath = gotResultsPath;
    this->gotFitsInPath = gotFitsInPath;
    this->gotFitsOutPath = gotFitsOutPath;
    this->gotConfig = gotConfig;
    this->gotSurveyType = gotSurveyType;
    this->destroy = destroy;

    assert(this);

    // sort out current time
    time_t now = time(NULL);;
    struct tm *ts = gmtime(&now); // TODO should be UTC
    strftime(this->todaysDate, sizeof(this->todaysDate), "%Y-%m-%d", ts);

    return true;
}


