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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include "pslib.h"
#include "stac.h"
#include "stacConfig.h"

stacConfig *stacConfigAlloc(void)
{
    // Allocate memory
    stacConfig *config = (stacConfig*) psAlloc(sizeof(stacConfig));
    // Set defaults
    config->verbose = 0;
    config->gain = 1.0;
    config->readnoise = 0.0;
    config->inputs = NULL;
    config->output = NULL;
    config->saveShifts = false;
    config->starFile = NULL;
    config->starMapFile = NULL;
    config->aper = 3.0;
    config->outnx = 0;
    config->outny = 0;
    config->saturated = 65535.0;        // Saturation level
    config->bad = 0.0;                  // Bad level
    config->reject = 2.5;
    config->frac = 0.45;
    config->grad = 0.7;
    config->nReject = 2;

    return config;
}


void stacConfigFree(stacConfig *config)
{
    // Free the vectors, if necessary
    if (config->inputs) {
        psFree(config->inputs);
    }
    // Free everything
    psFree(config);
}


stacConfig *stacParseConfig(int argc,   // Number of command-line arguments
                            char **argv // Command-line arguments
    )
{
    stacConfig *config = stacConfigAlloc(); // Configuration values
    const char *programName = argv[0];  // Program name

    /* Variables for getopt */
    int opt;   /* Option, from getopt */
    extern char *optarg;   /* Argument accompanying switch */
    extern int optind;   /* getopt variables */

    /* Parse command-line arguments using getopt */
    while ((opt = getopt(argc, argv, "hvSg:r:o:s:b:k:n:f:G:p:a:")) != -1) {
        switch (opt) {
          case 'h':
            help(programName);
            exit(EXIT_SUCCESS);
          case 'v':
            config->verbose++;
            break;
          case 'g':
            if (sscanf(optarg, "%f", &config->gain) != 1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 'r':
            if (sscanf(optarg, "%f", &config->readnoise) != 1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 'o':
            if ((sscanf(argv[optind-1], "%d", &config->outnx) != 1) ||
                (sscanf(argv[optind++], "%d", &config->outny) != 1)) {
                // Note: incrementing optind, so I can read more than one parameter.
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 's':
            if (sscanf(optarg, "%f", &config->saturated) != 1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 'b':
            if (sscanf(optarg, "%f", &config->bad) != 1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 'p':
            if (argc < optind+1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            config->starFile = argv[optind-1];
            config->starMapFile = argv[optind++];
            // Note: incrementing optind, so I can read more than one parameter.
            break;
          case 'a':
            if (sscanf(optarg, "%f", &config->aper) != 1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 'k':
            if (sscanf(optarg, "%f", &config->reject) != 1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 'n':
            if (sscanf(optarg, "%d", &config->nReject) != 1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 'f':
            if (sscanf(optarg, "%f", &config->frac) != 1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 'G':
            if (sscanf(optarg, "%f", &config->grad) != 1) {
                help(programName);
                exit(EXIT_FAILURE);
            }
            break;
          case 'S':
            config->saveShifts = true;
            break;
          default:
            help(programName);
        }
    }

    /* Get the remaining, mandatory values */
    argc -= optind;
    argv += optind;
    if (argc < 2) {
        help(programName);
        exit(EXIT_FAILURE);
    }

    config->output = argv[0];           // Output file
    // Get the input files
    config->inputs = psArrayAlloc(argc - 1);
    config->inputs->n = argc - 1;
    for (int i = 1; i < argc; i++) {
        config->inputs->data[i-1] = psAlloc(strlen(argv[i]));
        strncpy(config->inputs->data[i-1], argv[i], strlen(argv[i]));
    }

    // Debugging output
    psTrace("stac.config", 8, "Parsed command line for configuration\n");
    psTrace("stac.config", 9, "Verbosity level: %d\n",config->verbose);
    psTrace("stac.config", 9, "%ld inputs:\n",config->inputs->n);
    for (int i = 0; i < config->inputs->n; i++) {
        psTrace("stac.config", 9, "\t%s\n", (char *)config->inputs->data[i]);
    }
    psTrace("stac.config", 9, "Output file is %s\n",config->output);
    psTrace("stac.config", 9, "Output file size is %dx%d\n", config->outnx, config->outny);

    return config;
}

