/* Copyright Dassault Systemes, 1999, 2010 */

/*=========================================================================*\
 *
 * This example file demonstrates how to implement an external
 * executable approximation technique and integrate it into Isight.
 *
 * To integrate a different approximation technique using this example, 
 * search this file for all comments starting with "INSERT" and add your 
 * custom code where needed.
 *
 * At the top of the file there are several generic utility routines for 
 * allocating a memory array, reading a technique options file, reading a 
 * data matrix file.
 *
 * All communication between Isight and this executable program is done
 * via writing and reading files.
 *
\*=========================================================================*/

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

/* 
 * remove this #define statement if you are 
 * compiling on non-Win32 platform
 */
#define WIN32

#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif

/* INSERT local variables for your technique options here */
static int unusedTechniqueOption_1 = 1;
static int unusedTechniqueOption_2 = 0;
static int unusedTechniqueOption_3 = 0;

/* 
 * INSERT static variables for your internal data here
 * These data must contain everything that is required to re-construct
 * your approximation, i.e. coefficients, tuning parameters, etc.
 */
static double internalData[3];

/*=========================================================================*\
 * Utility function that calls calloc() and in case of memory
 * allocation failure prints a message to stderr and exits the program.
\*=========================================================================*/
static void * allocateArray(size_t nElem, size_t elemSize, char* arrayName) {
    void * memPointer = calloc(nElem, elemSize);
    if(memPointer == NULL) {
        fprintf(stderr, "Failed to allocate memory for %s\n", arrayName);
        exit(1);
    }
    return memPointer;
}

/*=========================================================================*\
 * This function reads technique options from a file.
 * The format of the technique options file is as follows:
 *
 *     -------------------
 *    |Val_1 OptionName_1 |
 *    |Val_2 OptionName_2 |
 *    |...                |
 *     -------------------
 *
 * NOTE! the order of technique options in the file is not guaranteed to
 * match the displayed order in Isight. When using the values of the
 * technique options, always check the name of the option.
 *
\*=========================================================================*/
static void readTechniqueOptionsFile(char* fName) {

    FILE* f;
    int i;
    double val;
    char optionName[256];

    /* open the file for reading */
    if((f = fopen(fName, "r")) == NULL) {
        fprintf(stderr,"Failed to open file \"%s\" for reading\n", fName);
        exit(1);
    }

    /* debug */
    fprintf(stdout,"Reading TECHNIQUE OPTIONS from \"%s\"\n",fName);

    /* 
     * NOTE: technique options in the file can be in any order
     * so we have to first read ALL of them and then match
     * their names to known option names;
     *
     * INSERT code to match technique option names and assign
     * option values below.
     */
    while(!feof(f)) {
        /* read one value and one string into temporary local variables */
        if(fscanf(f,"%lf %[^\n]\n", &val, optionName) == 2) {
            /* check the option name, if a match found assign the value */
            if(strcmp(optionName,"Unused Technique Option 1")==0) {
                unusedTechniqueOption_1 = (int)val;
            }
            else if(strcmp(optionName,"Unused Technique Option 2")==0) {
                unusedTechniqueOption_2 = (int)val;
            }
            else if(strcmp(optionName,"Unused Technique Option 3")==0) {
                unusedTechniqueOption_3 = (int)val;
            }
            /* debug */
            fprintf(stdout,"  %s = %lg\n", optionName, val);
        }
    }

    fclose(f);
}

/*=========================================================================*\
 * This function reads a matrix of values from a file.
 * The format of the matrix data file is as follows:
 *
 *     ------------------------
 *    | nRow number of rows    |
 *    | nCol number of columns |
 *    |Val_1 Val_2 Val_3 ...   |
 *    |Val_1 Val_2 Val_3 ...   |
 *    |Val_1 Val_2 Val_3 ...   |
 *    |...                     |
 *     ------------------------
\*=========================================================================*/
static double** readDataMatrixFile(char* fName, int* nRow, int* nCol) {

    FILE* f = NULL;
    int i,j;
    double** dataMatrix;

    /* open the file for reading */
    if((f = fopen(fName, "r")) == NULL) {
        fprintf(stderr,"Failed to open file \"%s\" for reading\n", fName);
        exit(1);
    }

    /* read nRow and nCol from the first 2 lines of the file */
    if(fscanf(f,"%d %*[^\n]\n", nRow) != 1) {
        fprintf(stderr,"Invalid file format, \"%s\" line 1 - expected number of rows\n",fName);
        exit(1);
    }
    if(fscanf(f,"%d %*[^\n]\n", nCol) != 1) {
        fprintf(stderr,"Invalid file format, \"%s\" line 2 - expected number of columns\n",fName);
        exit(1);
    }

    /* debugging */
    fprintf(stdout, "Reading data matrix file \"%s\", nRow=%d, nCol=%d\n",fName,*nRow,*nCol);

    /* allocate matrix for data values */
    dataMatrix = (double**)allocateArray(*nRow, sizeof(double*), "data matrix");
    for(i=0; i<*nRow; i++) {
        dataMatrix[i] = (double*)allocateArray(*nCol, sizeof(double), "data matrix");
    }

    /* read the values from the file */
    for(i=0; i<*nRow; i++) {
        fprintf(stdout,"data line #%d: ",i+1);
        for(j=0; j<*nCol; j++) {
            if(fscanf(f," %lg ", &dataMatrix[i][j]) != 1) {
                fprintf(stderr,"Invalid file format, \"%s\", data line=%d, column=%d - "
                        "failed to read a data value\n",fName,i+1,j+1);
                exit(1);
            }
            fprintf(stdout,"%g ",dataMatrix[i][j]);
        }
        fprintf(stdout,"\n");
    }

    fclose(f);
    return dataMatrix;
}

/*=========================================================================*\
 * This function writes a matrix of values to a file.
\*=========================================================================*/
static void writeInternalDataFile(char* internalDataFileName) {

    int i;
    FILE* f = NULL;

    /* open the file for writing */
    if((f = fopen(internalDataFileName,"w")) == NULL) {
        fprintf(stderr,"Failed to open file \"%s\" for writing\n", internalDataFileName);
        exit(1);
    }

    /* INSERT your code for writing internal data to a file here */
    for(i=0; i<3; i++) {
        fprintf(f,"%.15g\n", internalData[i]);
    }
    fclose(f);
}

/*=========================================================================*\
 * This function reads a matrix of values from a file.
\*=========================================================================*/
static void readInternalDataFile(char* internalDataFileName) {

    int i;
    FILE* f = NULL;

    /* open the file for reading */
    if((f = fopen(internalDataFileName,"r")) == NULL) {
        fprintf(stderr,"Failed to open file \"%s\" for reading\n", internalDataFileName);
        exit(1);
    }

    /* INSERT your code for reading internal data from a file here */
    for(i=0; i<3; i++) {
        if(fscanf(f," %lg ", &internalData[i]) != 1) {
            fprintf(stderr,"Failed to read internal data from file \"%s\", line %d\n", internalDataFileName,i+1);
            exit(1);
        }
    }
    fclose(f);
}

/*=========================================================================*\
 * This function performs initialization of approximation.
\*=========================================================================*/
static void initializeApproximation(char* techniqueOptionsFileName, 
        char* inputMatrixFileName, char* outputMatrixFileName,
        char* internalDataFileName) {

    int i;
    double** inputMatrix = NULL;
    double** outputMatrix = NULL;
    int nIn, nOut;
    int nPoint, nPoint2;
    FILE* f = NULL;


    /* 
     * read technique options from the file;
     * the values of technique options and their names will
     * be stored in the global variables (see them
     * at the top of this file)
     */
    readTechniqueOptionsFile(techniqueOptionsFileName);

    /* read sampling input and output matrices */
    inputMatrix = readDataMatrixFile(inputMatrixFileName, &nPoint, &nIn);
    outputMatrix = readDataMatrixFile(outputMatrixFileName, &nPoint2, &nOut);

    /* simple validation check */
    if(nPoint != nPoint2) {
        fprintf(stderr,"Number of points is different in the input and output "
                "matrix files: %d != %d\n",nPoint,nPoint2);
        exit(1);
    }

    /* 
     * Now we have read technique options, and sampling points (input and output matrices).
     * We are ready to initialize approximation using the available sampling data.
     *
     * INSERT a call to your initialization algorithm below this comment.
     */
    internalData[0] = 1.0;
    internalData[1] = 2.0;
    internalData[2] = 3.0;

    /* 
     * after initialization is done, we must save all internal coefficient data and 
     * everything else needed to re-create the approximation later into the specified file; 
     * Isight will read this file and preserve the internal data in memory until evaluation
     * of the approximation is needed;
     * for this template/example we will simply use all technique options as the 
     * internal approximation data; in real implementation this will be
     * a collection of internal data values.
     */
    writeInternalDataFile(internalDataFileName);
}

/*=========================================================================*\
 * This function performs export of approximation to a file.
 *
 * NOTE: only internal coefficient data needs to be exported by this
 * routine. Isight will take the contents of the file that this routine
 * creates and append it to the standard format coefficient data file which 
 * contains all parameter names, masic sizing parameters, and sampling data 
 * (inputs and outputs).
 * The data written by this routine must start with an easily recognizable token
 * word, such that you can find this word in a file later when you need
 * to load your approximation from a file (see next routine).
\*=========================================================================*/
static void exportApproximation(char* internalDataFileName, char* exportToFileName) {

    int i;
    FILE* f = NULL;

    /* debug */
    fprintf(stdout,"Exporting internal approximation data to file \"%s\"\n", exportToFileName);

    /* 
     * read internal data from the internal data file; this is the same file
     * that was created by the initialization routine (see above) previously
     * and preserved by Isight until now.
     */
    readInternalDataFile(internalDataFileName);

    /* open export file for writing */
    if((f = fopen(exportToFileName,"w")) == NULL) {
        fprintf(stderr,"Failed to open file \"%s\" for writing\n", exportToFileName);
        exit(1);
    }

    /*
     * now we are ready to export approximation to a coefficient data file in a human 
     * readable text format; NOTE: the data must start with an easily recognizable
     * token word such that such that you can find this word in a file later when you 
     * need to load your approximation from a file (see next routine).
     *
     * INSERT code to write internal data to the file here
     */
    fprintf(f,"\nINTERNAL COEFFICIENT DATA:\n\n");
    for(i=0; i<3; i++) {
        fprintf(f,"%g ", internalData[i]);
    }
    fprintf(f, "\n");
    fclose(f);
}

/*=========================================================================*\
 * This function loads of approximation from a standard format
 * coefficient data file. 
 *
 * NOTE: only internal coefficient data needs to be loaded by this
 * routine. Isight appends the internal data created by the previous
 * routine (exportApproximation) to the end of the standard coefficient
 * data file which also contains all parameter names, basic sizing parameters, 
 * and sampling data (inputs and outputs) at the beginning.
\*=========================================================================*/
static void loadApproximation(char* loadFromFileName, char* internalDataFileName) {

    int i;
    FILE* f = NULL;
    char stringBuffer[256];

    /* debug */
    fprintf(stdout,"Loading internal approximation data from file \"%s\"\n", loadFromFileName);

    /* open the file */
    if((f = fopen(loadFromFileName, "r")) == NULL) {
        fprintf(stderr,"Failed to open file \"%s\" for reading\n", loadFromFileName);
        exit(1);
    }

    /* 
     * we are only interested in the last part of the file, where our internal coefficient data
     * is written; we skip the beginning of the standard coefficient data file until we find the 
     * needed keyword. In this template/example we used "INTERNAL COEFFICIENT DATA" at the 
     * beginning of the section with internal data (see previous routine exportApproximation).
     *
     * INSERT your code for finding the needed keyword here
     */
    do {
        stringBuffer[0] = 0;
        fscanf(f,"%255s",stringBuffer);
    }
    while(strcmp(stringBuffer,"INTERNAL") != 0 && !feof(f));

    if(feof(f) != 0) {
        fprintf(stderr,"Invalid file format, could not find \"INTERNAL COEFFICIENT DATA\" "
                "string in \"%s\"\n",loadFromFileName);
        exit(1);
    }

    /* we found the string "INTERNAL"; skip the rest of the line */
    fscanf(f,"%*[^\n]\n");

    /* INSERT your code for reading the internal coefficient data values here */
    for(i=0; i<3; i++) {
        if(feof(f)) {
            fprintf(stderr,"Unexpected EOF while trying to read internal "
                    "data from file \"%s\"\n", loadFromFileName);
            exit(1);
        }
        if(fscanf(f,"%lg", &internalData[i]) != 1) {
            fprintf(stderr,"Failed to read internal data from file \"%s\"\n", 
                    loadFromFileName);
            exit(1);
        }
    }
    fclose(f);

    /* 
     * after loading internal data, we must save internal approximation data 
     * and everything needed to re-create the approximation into the specified file; 
     * Isight will read this file and preserve the internal data in memory until evaluation
     * of the approximation is needed.
     */
    writeInternalDataFile(internalDataFileName);
}

/*=========================================================================*\
 * This function performs evaluation of approximation for all points
 * contained in the file "inputValsFileName". Output values must be written
 * to file "outputValsFileName".
 *
 * NOTE: before calling this routine, Isight will create files with
 * sampling data points (input and output matrices) and a file with internal
 * data which was created by the initialization routine earlier.
\*=========================================================================*/
static void evaluateApproximation(char* inputMatrixFileName, char* outputMatrixFileName,
        char* internalDataFileName, char* inputValsFileName, char* outputValsFileName) {

    int i,j;
    double** inputMatrix = NULL;
    double** outputMatrix = NULL;
    double** inputVals = NULL;
    double** outputVals = NULL;
    int nIn, nOut, nIn2;
    int nPoint, nPoint2;
    FILE* f = NULL;


    /* read internal data from the file */
    readInternalDataFile(internalDataFileName);

    /* read input and output matrices */
    inputMatrix = readDataMatrixFile(inputMatrixFileName, &nPoint, &nIn);
    outputMatrix = readDataMatrixFile(outputMatrixFileName, &nPoint2, &nOut);

    /* simple validation check */
    if(nPoint != nPoint2) {
        fprintf(stderr,"Number of points is different in the input and output "
                "matrix files: %d != %d\n",nPoint,nPoint2);
        exit(1);
    }

    /* read input point values for evaluation */
    inputVals = readDataMatrixFile(inputValsFileName, &nPoint2, &nIn2);

    /* simple validation check */
    if(nIn2 != nIn) {
        fprintf(stderr,"Number of inputs is different in the input matrix and "
                "input values files: %d != %d\n",nIn,nIn2);
        exit(1);
    }

    /* allocate matrix for output values produced by approximation evaluations */
    outputVals = (double**)allocateArray(nPoint2, sizeof(double*), "output vals");
    for(i=0; i<nPoint2; i++) {
        outputVals[i] = (double*)allocateArray(nOut, sizeof(double), "output vals");
    }

    /* open the output values file for writing results */
    if((f = fopen(outputValsFileName,"w")) == NULL) {
        fprintf(stderr,"Failed to open file \"%s\" for writing\n", outputValsFileName);
        exit(1);
    }

    /* 
     * now we are ready to evaluate approximation for every point in the inputValsFile;
     * for this template/example we take an average value of inputs and set 
     * all outputs to that value.
     *
     * INSERT your approximation evaluation algorithm here
     */
    for(i=0; i<nPoint2; i++) {
        double average = 0;
        for(j=0; j<nIn; j++) {
            average += inputVals[i][j];
        }
        average /= nIn;
        for(j=0; j<nOut; j++) {
            outputVals[i][j] = average;
        }
    }

    /* write output values to file */
    fprintf(f,"%d number of rows\n", nPoint2);
    fprintf(f,"%d number of columns\n", nOut);
    for(i=0; i<nPoint2; i++) {
        for(j=0; j<nOut; j++) {
            fprintf(f,"%.15g ", outputVals[i][j]);
        }
        fprintf(f,"\n");
    }
    fclose(f);
}

/*=========================================================================*\
 *
 * Entry point for the program. Isight will create necessary files
 * and then call this program to execute your approximation algorithm.
 *
 * The command syntax is as follows:
 *    "this.exe subCommand optionsFile inputValMatrix outputValMatrix coefFileName"
 *
 * where:
 *    <0> this.exe - the name of this executable program;
 *    <1> subCommand - sub-command designating which action must be taken,
 *        one of "initialize", "evaluate", "export", "load";
 *    see code below for possible other cimmand line arguments.
 *
\*=========================================================================*/
int main(int argc, char** argv) {

    int i;
    char* subCommand               = NULL;
    char* techniqueOptionsFileName = NULL;
    char* inputMatrixFileName      = NULL;
    char* outputMatrixFileName     = NULL;
    char* internalDataFileName     = NULL;
    char* inputValsFileName        = NULL;
    char* outputValsFileName       = NULL;
    char* exportToFileName         = NULL;
    char* loadFromFileName         = NULL;

    /* 
     * debugging printout; 
     * all stdOut prints can be seen as DEBUG 
     * messages in Isight job log 
     */
    fprintf(stdout,"**** Easy Native Approximation Technique ****\n");
    fprintf(stdout,"**** args = \n");
    for(i=0; i<argc; i++) {
        fprintf(stdout,"    %s\n", argv[i]);
    }
    fprintf(stdout,"\n");

    /* set file names from the command line arguments */
    for(i=2; i<argc; i+=2) {
        if(strcmp(argv[i],"-techniqueOptionsFile")==0) {
            techniqueOptionsFileName = argv[i+1];
            fprintf(stdout,"techniqueOptionsFile = \"%s\"\n",techniqueOptionsFileName);
        }
        else if(strcmp(argv[i],"-inputMatrixFile")==0) {
            inputMatrixFileName = argv[i+1];
            fprintf(stdout,"inputMatrixFileName = \"%s\"\n",inputMatrixFileName);
        }
        else if(strcmp(argv[i],"-outputMatrixFile")==0) {
            outputMatrixFileName = argv[i+1];
            fprintf(stdout,"outputMatrixFileName = \"%s\"\n",outputMatrixFileName);
        }
        else if(strcmp(argv[i],"-internalDataFile")==0) {
            internalDataFileName  = argv[i+1];
            fprintf(stdout,"internalDataFileName = \"%s\"\n",internalDataFileName);
        }
        else if(strcmp(argv[i],"-inputValuesFile")==0) {
            inputValsFileName  = argv[i+1];
            fprintf(stdout,"inputValsFileName = \"%s\"\n",inputValsFileName);
        }
        else if(strcmp(argv[i],"-outputValuesFile")==0) {
            outputValsFileName  = argv[i+1];
            fprintf(stdout,"outputValsFileName = \"%s\"\n",outputValsFileName);
        }
        else if(strcmp(argv[i],"-exportToFile")==0) {
            exportToFileName  = argv[i+1];
            fprintf(stdout,"exportToFileName = \"%s\"\n",exportToFileName);
        }
        else if(strcmp(argv[i],"-loadFromFile")==0) {
            loadFromFileName  = argv[i+1];
            fprintf(stdout,"loadFromFileName = \"%s\"\n",loadFromFileName);
        }
        else {
            fprintf(stderr,"Invalid command line arg: \"%s\", must be one of "
                    "\"-techniqueOptionsFile\", \"-inputMatrixFile\", "
                    "\"-outputMatrixFile\", \"-internalDataFile\"\n" 
                    "\"-inputValuesFile\", \"-outputValuesFile\", "
                    "\"-exportToFile\"\n \"-loadFromFile\"", argv[i]);
            exit(1);
        }
    }

    /* save sub-command and determine what action is needed */
    subCommand = argv[1];
    if(strcmp(subCommand,"initialize")==0) {
        initializeApproximation(techniqueOptionsFileName, inputMatrixFileName, 
                outputMatrixFileName, internalDataFileName);
    }
    else if(strcmp(subCommand,"evaluate")==0) {
        evaluateApproximation(inputMatrixFileName, outputMatrixFileName,
                internalDataFileName, inputValsFileName, outputValsFileName);
    }
    else if(strcmp(subCommand,"export")==0) {
        exportApproximation(internalDataFileName, exportToFileName);
    }
    else if(strcmp(subCommand,"load")==0) {
        loadApproximation(loadFromFileName, internalDataFileName);
    }
    else {
        fprintf(stderr,"Invalid sub-command: \"%s\", must be one of "
            "\"initialize\", \"evaluate\", \"export\", \"load\"\n", subCommand);
        exit(1);
    }

    /* 
     * exit with return code = 0; 
     * any non-zero return code means an error
     */
    exit(0);
}

