/** @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) {

            psError(PS_ERR_UNKNOWN, false, "Failed, so deleting %s", this->fitsOutPath);

            // if process failed, we need to delete that FITS file
            if (remove(this->fitsOutPath) == -1) {

                psError(PS_ERR_UNKNOWN, false, "Unable to delete %s", this->fitsOutPath);
            }
        }
        // ...otherwise, close file
        else {

            int status = 0;
            if (fits_close_file(this->fitsOut, &status)) {

                psError(PS_ERR_IO, false, "Unable to close FITS file %s", this->fitsOutPath);
                fits_report_error(stderr, status);
            }
        }
    }

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

    ippToPspsConfig_Destructor(this->config);

    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) {

        psError(PS_ERR_UNKNOWN, false, "Unable to open file at %s", this->fitsInPath);
        return false;
    }

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

    printf ("Number of input files %d\n", this->numOfInputFiles);

    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) {

    // 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) {

        psError(PS_ERR_UNKNOWN, false, "Unable to read configuration.");
        this->exitCode = PS_EXIT_CONFIG_ERROR;
        return false;
    }
    assert(this->pmconfig);

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

    // 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(this->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(this->configsDir, argv[optind]);
                haveConfig = true;
            }
            else if(strcmp(sw, "-survey") == 0 ) {
                optind++; if( optind > ( argc-1 ) ) break;
                strcpy(this->surveyType, argv[optind]);
                haveSurveyType = true;
            }
        }

        optind++;
    }


    // setup command line arguments
    //psMetadataAddStr(this->arguments, PS_LIST_TAIL, "-survey", 0, "Survey type", NULL);


    // now check rest of arguments
    //if (argc < 2 || !psArgumentParse(this->arguments, &argc, argv)) {

      //  usage(this, argv[0]);
        //this->exitCode = PS_EXIT_CONFIG_ERROR;
        //return false;
   // }

    //this->surveyType = psMemIncrRefCounter(psMetadataLookupStr(NULL, this->arguments, "-survey"));

    if (haveFitsInPath && !readInputFilePaths(this)) {

        this->exitCode = PS_EXIT_DATA_ERROR;
        return false;
    }

    return true;
}

/**
  Initialises file paths, creates FITS file etc
  */
bool init(Batch* this) {

    // create a config object
    this->config = ippToPspsConfig_Constructor(this->configsDir);
    if (this->config == NULL) {

        this->exitCode = PS_EXIT_CONFIG_ERROR;
        return false;
    }

    // get survey ID using config object
    if (strlen(this->surveyType) > 0 && !ippToPspsConfig_getSurveyId(this->config, this->surveyType, &this->surveyID)) {

        this->exitCode = PS_EXIT_CONFIG_ERROR;
        return false;
    }
    // create full FITS out path
    sprintf (this->fitsOutPath, "%s/%s", this->fitsOutPath, this->fitsOutFile);

    // create an output FITS file
    int status=0;
    if (fits_create_file(&this->fitsOut, this->fitsOutPath, &status)) {
        fits_report_error(stderr, status);
        psError(PS_ERR_IO, false, "Unable to create file at: '%s'", this->fitsOutPath);
        this->exitCode = PS_EXIT_SYS_ERROR;
        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 this->fitsOutFile);
    }

    return true;
}

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

    printf("\n");
    printf("* surveyType      : '%s'\n", this->surveyType);
    printf("* surveyID        : %d\n", this->surveyID);
    printf("* resultsPath     : '%s'\n", this->resultsPath);
    printf("* numOfInputFiles : %d\n", this->numOfInputFiles);
    printf("* fitsInPath      : '%s'\n", this->fitsInPath);
    printf("* fitsOutFile     : '%s'\n", this->fitsOutFile);
    printf("* fitsOutPath     : '%s'\n", this->fitsOutPath);
    printf("* configsDir      : '%s'\n", this->configsDir);
}



#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(Batch *this) {

    this->surveyID = -1;
    this->resultsXmlDoc = NULL;
    this->numOfInputFiles = 0;
    this->inputFiles = NULL;
    this->fitsOut = NULL;
    this->config = NULL;
    this->pmconfig = NULL;
    this->exitCode = PS_EXIT_SUCCESS;

    this->parseArguments = parseArguments;
    this->init = init;
    this->print = print;
    this->destroy = destroy;

    this->gotResultsPath = gotResultsPath;
    this->gotFitsInPath = gotFitsInPath;
    this->gotFitsOutPath = gotFitsOutPath;
    this->gotConfig = gotConfig;
    this->gotSurveyType = gotSurveyType;

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


