# include "ppSimSequence.h"
# include <sys/stat.h>

// XXX Memory leaks in string variables

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

    bool status;
    int argNum;
    unsigned int nFail;

    psLibInit(NULL);

    char *dbname = NULL;
    if ((argNum = psArgumentGet (argc, argv, "-dbname"))) {
        psArgumentRemove(argNum, &argc, argv);
        dbname = psStringCopy (argv[argNum]);
        psArgumentRemove(argNum, &argc, argv);
    }

    char *path = NULL;
    if ((argNum = psArgumentGet (argc, argv, "-path"))) {
        psArgumentRemove(argNum, &argc, argv);
        path = psStringCopy (argv[argNum]);
        psArgumentRemove(argNum, &argc, argv);
        psString line = NULL;           // Line to execute
        psStringAppend(&line, "mkdir -p %s", path);
        if (!system(line)) {
            psWarning("Unable to create directory %s", path);
        }
        psFree(line);
    }

    char *camera = NULL;
    if ((argNum = psArgumentGet (argc, argv, "-camera"))) {
        psArgumentRemove(argNum, &argc, argv);
        camera = psStringCopy (argv[argNum]);
        psArgumentRemove(argNum, &argc, argv);
    }

    char *workdir = NULL;
    if ((argNum = psArgumentGet (argc, argv, "-workdir"))) {
        psArgumentRemove(argNum, &argc, argv);
        workdir = psStringCopy (argv[argNum]);
        psArgumentRemove(argNum, &argc, argv);
    }

    char *ppsim_recipe = NULL;
    if ((argNum = psArgumentGet (argc, argv, "-ppsim_recipe"))) {
        psArgumentRemove(argNum, &argc, argv);
        ppsim_recipe = psStringCopy (argv[argNum]);
        psArgumentRemove(argNum, &argc, argv);
    }

    char *dvodb = NULL;
    if ((argNum = psArgumentGet (argc, argv, "-dvodb"))) {
        psArgumentRemove(argNum, &argc, argv);
        dvodb = psStringCopy (argv[argNum]);
        psArgumentRemove(argNum, &argc, argv);
    }

    char *tess_id = NULL;
    if ((argNum = psArgumentGet (argc, argv, "-tess_id"))) {
        psArgumentRemove(argNum, &argc, argv);
        tess_id = psStringCopy (argv[argNum]);
        psArgumentRemove(argNum, &argc, argv);
    }

    char *basename = NULL;
    if ((argNum = psArgumentGet (argc, argv, "-basename"))) {
        psArgumentRemove(argNum, &argc, argv);
        basename = psStringCopy (argv[argNum]);
        psArgumentRemove(argNum, &argc, argv);
    } else {
        basename = psStringCopy ("simtest");
    }

    char *label = NULL;
    if ((argNum = psArgumentGet (argc, argv, "-label"))) {
        psArgumentRemove(argNum, &argc, argv);
        label = psStringCopy (argv[argNum]);
        psArgumentRemove(argNum, &argc, argv);
    }

    if (argc != 4) {
        fprintf (stderr, "USAGE: ppSimSequence (sequence) (simulate) (inject) [options]\n");
        fprintf (stderr, "generates a set of simulated data defined by the sequence file\n");
        fprintf (stderr, " (sequence) : a mdc-file describing the desired image sequences\n");
        fprintf (stderr, " (simulate) : an output file with commands to generate the images\n");
        fprintf (stderr, " (inject)   : an output file with commands to inject the images into the pipeline\n");
        fprintf (stderr, "options:\n");
        fprintf (stderr, " -camera (camera) [otherwise must be set in sequences file]\n");
        fprintf (stderr, " -ppsim_recipe (recipe)\n");
        fprintf (stderr, " -dbname (dbname)\n");
        fprintf (stderr, " -path (path)\n");
        fprintf (stderr, " -workdir (workdir)\n");
        fprintf (stderr, " -basename (basename)\n");
        fprintf (stderr, " -label (label)\n");
        fprintf (stderr, " -dvodb (dvodb)\n");
        fprintf (stderr, " -tess_id (tess_id)\n");
        exit (2);
    }

    // load the sequence description
    psMetadata *config = psMetadataConfigRead (NULL, &nFail, argv[1], false);
    if (!config) {
        psLogMsg ("ppSimSequence", PS_LOG_WARN, "unable to read sequence description from %s", argv[1]);
        exit (1);
    }

    FILE *simfile = fopen (argv[2], "w");
    if (!simfile) {
        psLogMsg ("ppSimSequence", PS_LOG_WARN, "unable to open %s for output", argv[2]);
        exit (1);
    }

    FILE *inject = fopen (argv[3], "w");
    if (!inject) {
        psLogMsg ("ppSimSequence", PS_LOG_WARN, "unable to open %s for output", argv[3]);
        exit (1);
    }

    // build the base injectCommand string
    psString injectCommand = psStringCopy ("ipp_inject_fileset.pl --telescope SIMTEST");
    if (dbname)  psStringAppend (&injectCommand, " --dbname %s",  dbname);
    if (workdir) psStringAppend (&injectCommand, " --workdir %s", workdir);
    if (label)   psStringAppend (&injectCommand, " --label %s", label);
    if (dvodb)   psStringAppend (&injectCommand, " --dvodb %s", dvodb);
    if (tess_id) psStringAppend (&injectCommand, " --tess_id %s", tess_id);

    // build the base ppSimCommand string
    psString ppSimCommand = psStringCopy ("ppSim");
    if (ppsim_recipe) psStringAppend (&ppSimCommand, " -recipe PPSIM %s", ppsim_recipe);

    unsigned long seed = psMetadataLookupS32 (&status, config, "RND_SEED");
    if (!status) seed = 0;
    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, seed);

    psMetadataItem *item = psMetadataLookup (config, "SEQUENCE");
    if (item == NULL) {
        psLogMsg ("ppSimSequence", PS_LOG_WARN, "missing SEQUENCE description");
        exit (1);
    }

    psArray *sequences = NULL;
    if (item->type == PS_DATA_METADATA) {
        sequences = psArrayAlloc(1);
        sequences->data[0] = psMemIncrRefCounter (item->data.V);
    } else {
        if (item->type != PS_DATA_METADATA_MULTI)  {
            psLogMsg ("ppSimSequence", PS_LOG_WARN, "SEQUENCE is not MULTI or METADATA");
            exit (1);
        }
        sequences = psListToArray (item->data.list);
    }

    for (int i = 0; i < sequences->n; i++) {

        // XXX this is not obvious: the entry on the list is a metadata item
        // containing the psMetadata :: why is this not just a metadata?
        psMetadataItem *item = sequences->data[i];
        psMetadata *sequence = item->data.V;
        // double check item type?

        // determine the sequence type
        char *type = psMetadataLookupStr (&status, sequence, "OBSTYPE");
        if (!status) {
            psLogMsg ("ppSimSequence", PS_LOG_WARN, "SEQUENCE %d is missing a type", i);
            exit (1);
        }

	// determine the camera for the sequence and define the ppSim command
	if (camera == NULL) {
	    camera = psMetadataLookupStr (&status, sequence, "CAMERA");
	}

	psString injectCommandReal = NULL;
	psString ppSimCommandReal = NULL;

	psStringAppend (&injectCommandReal, "%s --camera %s", injectCommand, camera);
	psStringAppend (&ppSimCommandReal, "%s -camera %s", ppSimCommand, camera);

        if (!strcasecmp (type, "BIAS")) {
            ppSimSequenceBias (simfile, inject, sequence, i, rng, path, basename, ppSimCommandReal, injectCommandReal);
            continue;
        }
        if (!strcasecmp (type, "DARK")) {
            ppSimSequenceDark (simfile, inject, sequence, i, rng, path, basename, ppSimCommandReal, injectCommandReal);
            continue;
        }
        if (!strcasecmp (type, "FLAT")) {
            ppSimSequenceFlat (simfile, inject, sequence, i, rng, path, basename, ppSimCommandReal, injectCommandReal);
            continue;
        }
        if (!strcasecmp (type, "OBJECT")) {
            ppSimSequenceObject (simfile, inject, sequence, i, rng, path, basename, ppSimCommandReal, injectCommandReal);
            continue;
        }

        psLogMsg ("ppSimSequence", PS_LOG_WARN, "SEQUENCE %d has an unknown type: %s", i, type);
        exit (1);
    }

    exit (0);
}
