/** @file Dvo.c
 *
 *  @ingroup ippToPsps
 *
 *  @author IfA
 *  Copyright 2011 Institute for Astronomy, University of Hawaii
 */

#include "Dvo.h"

/**
  Destructor.
  */
static void destroy(Dvo* this) {

    if (this == NULL) return;

    this->logger->print(this->logger, MSG_DEBUG, "Dvo", "Destructor\n");

    if (this->dvoConfig) dvoConfigFree(this->dvoConfig);
    if (this->mysql) mysql_close(this->mysql);

    // libohana redirects free and gets confused by thigns allocated by calloc
    // so call the real free funtion through this warpper
    real_free(this);
}

/**
  Gets detections for this imageid
  */
static bool getDetections(Dvo* this) {

    SkyList* skyList = NULL;
    Image* image = NULL;

    this->logger->print(this->logger, MSG_INFO, "Dvo", "Deleting everything from the dvoDetection table\n"); 
    mysql_query(this->mysql, "DELETE FROM dvoDetection");

    mysql_query(this->mysql, "SELECT SOURCE_ID, EXTERN_ID FROM dvoImages");
    MYSQL_RES* result = mysql_store_result(this->mysql);

    // loop through all souceID/imageIDs
    MYSQL_ROW row;
    char sql[500];
    int sourceID, externID;
    while ((row = mysql_fetch_row(result))) {

        sourceID = atoi(row[0]);
        externID = atoi(row[1]);

        //this->logger->print(this->logger, MSG_INFO, "Dvo", "---------------------------------------------------------------------\n");
        //this->logger->print(this->logger, MSG_INFO, "Dvo", "Getting skylist from DVO database for source ID = %d and extern ID = %d\n",
        //        sourceID, externID); 
        skyList = dvoSkyListByExternID(this->dvoConfig, sourceID, externID, &image);
        if (!skyList) {

            this->logger->print(this->logger, MSG_ERROR, 
                    "Dvo", "Could not find skylist for sourceId=%d and extern ID = %d\n", 
                    sourceID, externID);

            continue;
        }

        sprintf(sql, 
                "UPDATE dvoImages SET IMAGE_ID = %d, FLAGS = %d, PHOTCODE = %d WHERE SOURCE_ID = %d AND EXTERN_ID = %d",
                image->imageID,
                image->flags,
                image->photcode,
                sourceID,
                externID);
        mysql_query(this->mysql, sql); 

        // now get detections
        //this->logger->print(this->logger, MSG_INFO, "Dvo", "Getting detections...\n"); 
        dvoDetection* dvoDetections = NULL;
        int32_t maxDetectionId = -1;
        //int32_t numDetections = 
            dvoGetDetections(skyList, image->imageID, &dvoDetections, &maxDetectionId);

        //this->logger->print(this->logger, MSG_INFO, "Dvo", 
        //        "Found %d detections with a max detection ID of %d\n", 
        //        numDetections, maxDetectionId); 


        //this->logger->print(this->logger, MSG_INFO, "Dvo", "Inserting IDs into dvoDetection table...\n"); 

        uint32_t zeroDvoCount = 0;
        for (int i=0; i<maxDetectionId+1; i++) {

            if (dvoDetections[i].ave.extID == 0) {
                zeroDvoCount++;
            }
            else {
                Average *ave  = &dvoDetections[i].ave;
                Measure *meas = &dvoDetections[i].meas;
                PhotCode *code = GetPhotcodebyCode(meas->photcode);
                sprintf(sql, 
                        "INSERT INTO dvoDetection (imageID, ippDetectID, detectID, ippObjID, objID, flags, zp, zpErr, airMass, expTime, ra, dec_, raErr, decErr) VALUES (%d, %u, %lu, %lu, %lu, %u, %f, %f, %f, %f, %lf, %lf, %f, %f)",
                        image->imageID,    // imageID
                        meas->detID,   // ippDetectID
                        meas->extID,   // detectID
                        (uint64_t)ave->catID*1000000000 + (uint64_t)ave->objID, // ippObjID
                        ave->extID,   // objID
                        meas->dbFlags,  // flags
                        code->C * 0.001 + code->K * (meas->airmass - 1) - meas->Mcal,   // zp
                        meas->dMcal,    // zpErr
                        meas->airmass,
                        pow(10.0, 0.4 * meas->dt),    // expTime
                        ave->R - meas->dR / 3600.,    // ra
                        ave->D - meas->dR / 3600.,    // dec
                        meas->dXccd * 0.01 * fabs(meas->pltscale), // estimate of raErr
                        meas->dYccd * 0.01 * fabs(meas->pltscale) // estimate of decErr
                        );

                mysql_query(this->mysql, sql); 
            }
        }

        if (zeroDvoCount) this->logger->print(this->logger, MSG_ERROR, "Dvo", "Found %d 0 object IDs\n", zeroDvoCount);
        if (dvoDetections) dvoFree(dvoDetections);
        SkyListFree(skyList);
    }

    mysql_free_result(result);

    return true;
}

/**
  Constructor.
  */
Dvo* new_Dvo(Logger* logger, const char* configFile, const char* dbname, const char* dvoDatabase) {

    Dvo* this = (Dvo*)calloc(1, sizeof(Dvo));
    this->logger = logger;
    this->logger->print(this->logger, MSG_DEBUG, "Dvo", "Constructor\n");

    this->logger->print(this->logger, MSG_INFO, "Dvo", "Using config file here '%s'\n", configFile);
    xmlDoc* doc = xmlReadFile(configFile, NULL, 0);

    // database parameters to be retrieved from config file
    char host[20];
    char user[20];
    char passwd[20];


    if (doc == NULL) {

        this->logger->print(this->logger, MSG_ERROR, "Dvo", "Could not load config file\n");
    }
    else {

        xmlNode* root_element = xmlDocGetRootElement(doc);


        /* We don't care what the top level element name is */
        xmlNodePtr cur = root_element->xmlChildrenNode;
        char name[50];
        unsigned char* value;
        while (cur != NULL) {

            // get database stuff
            if (strcmp((char*)cur->name, "localdatabase") == 0) {

                xmlNodePtr dbParams = cur->xmlChildrenNode;

                while (dbParams != NULL) {

                    sprintf(name, "%s", dbParams->name);
                    value = xmlNodeListGetString(doc, dbParams->xmlChildrenNode, 1);
                    if (strcmp(name, "user") == 0) sprintf(user, "%s", value);
                    else if (strcmp(name, "host") == 0) sprintf(host, "%s", value);
                    else if (strcmp(name, "password") == 0) sprintf(passwd, "%s", value);

                    dbParams = dbParams->next;
                }
            }

            cur = cur->next;
        }

        xmlFreeDoc(doc);
    }

    this->logger->print(this->logger, MSG_INFO, "Dvo", "Using DVO database = '%s'\n", dvoDatabase);
    this->logger->print(this->logger, MSG_INFO, "Dvo", "Using db name      = '%s'\n", dbname);
    this->logger->print(this->logger, MSG_INFO, "Dvo", "Using db host      = '%s'\n", host);
    this->logger->print(this->logger, MSG_INFO, "Dvo", "Using db user      = '%s'\n", user);
    this->logger->print(this->logger, MSG_INFO, "Dvo", "Using db password  = '%s'\n", passwd);

    int argc = 4;
    char** argv;
    argv = (char**)calloc(argc, sizeof(char *));
    for (int i=0; i<argc; i++)
        argv[i] = (char*)calloc(200, sizeof(char));

    snprintf(argv[0], 200, "nothing");
    snprintf(argv[1], 200, "-D");
    snprintf(argv[2], 200, "CATDIR");
    snprintf(argv[3], 200, "%s", dvoDatabase);

    this->dvoConfig = dvoConfigRead(&argc, argv);

    //for (int i=0; i<argc; i++) free(argv[i]);
    //free(argv);

    // method pointers
    this->destroy = destroy;
    this->getDetections = getDetections;

    // set up MySQL connection
    this->mysql = mysql_init(NULL);

    if (!this->mysql)
        this->logger->print(this->logger, MSG_ERROR, "Dvo", "mysql_init(), out of memory\n");
    else {

        if (!mysql_real_connect(this->mysql, host, user, passwd, dbname, 0, NULL, 0)) 
            this->logger->print(this->logger, MSG_ERROR, "Dvo", "Failed to connect to database: %s\n", 
                    mysql_error(this->mysql));

        else
            this->logger->print(this->logger, MSG_INFO, "Dvo", "Connected to %s@%s as user '%s'\n",
                    dbname, host, user);

    }

    return this;
}

/**
  Main
  */
int main(int argc, char **argv) {

    Logger* logger = new_Logger(NULL, true);

    if (argc < 4) {

        logger->print(logger, MSG_ERROR, "Dvo", "Incorrect arguments\n");
        logger->print(logger, MSG_INFO, "Dvo", "Usage: ./dvo <pathToConfig> <dbName> <pathToDvo>\n");
    }
    else {

        Dvo * dvo = new_Dvo(logger, argv[1], argv[2], argv[3]);
        dvo->getDetections(dvo);
        dvo->destroy(dvo);
    }

    logger->destroy(logger);

    return 0;
}

