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

#include <stdio.h>
#include <string.h>

#include <pslib.h>
#include <psmodules.h>

#include "ppStac.h"

static void stacOptionsFree(ppStacOptions *options)
{
    psFree(options->config);
    psFree(options->inFiles);
    if (options->outFile) {
        psFitsClose(options->outFile);
    }
}


ppStacOptions *ppStacOptionsAlloc(void)
{
    ppStacOptions *options = psAlloc(sizeof(ppStacOptions));
    psMemSetDeallocator(options, (psFreeFunc)stacOptionsFree);

    options->config = NULL;
    options->inFiles = NULL;
    options->outFile = NULL;
    options->combine = 0;
    options->iter = 1;
    options->rej = 3.0;
    options->maskVal = 0;

    return options;
}



// Parse a recipe option according to its type
#define OPTION_PARSE(OPTION,MD,NAME,TYPE) \
{ \
    psMetadataItem *item = psMetadataLookup(MD, NAME); \
    if (item) { \
        OPTION = psMetadataItemParse##TYPE(item); \
    } else { \
        psWarning("Recipe option %s isn't specified; using default.\n", NAME); \
    } \
}

ppStacOptions *ppStacOptionsRead(int *argc, char *argv[])
{
    PS_ASSERT_INT_POSITIVE(*argc, NULL);
    PS_ASSERT_PTR_NON_NULL(argv, NULL);

    if (*argc <= 1 || psArgumentGet(*argc, argv, "-help") || psArgumentGet(*argc, argv, "-h")) {
        fprintf(stderr,
                "ppStac: Combine multiple registered images.\n\n"
                "Usage: %s OUT.fits IN1.fits IN2.fits ... [-recipe NAME FILE]\n\n",
                argv[0]);
        exit(PS_EXIT_SUCCESS);
    }

    ppStacOptions *options = ppStacOptionsAlloc(); // Options, to be returned

    // Parse the configuration and arguments
    pmConfigReadParamsSet(false);
    options->config = pmConfigRead(argc, argv, PPSTAC_RECIPE);
    if (!options->config) {
        psError(PS_ERR_UNKNOWN, false, "Unable to read configuration.\n");
        psFree(options);
        return NULL;
    }

    bool mdok;                          // Status of MD lookup
    psMetadata *recipe = psMetadataLookupMetadata(&mdok, options->config->recipes,
                                                  PPSTAC_RECIPE); // Recipe to use
    if (!recipe) {
        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find recipe %s.\n", PPSTAC_RECIPE);
        psFree(options);
        return NULL;
    }

    psString outName = pmConfigConvertFilename(argv[1], options->config, true); // Output filename
    options->outFile = psFitsOpen(outName, "w");
    if (!options->outFile) {
        psError(PS_ERR_IO, false, "Unable to open output file %s.\n", outName);
        psFree(options);
        psFree(outName);
        return NULL;
    }
    psFree(outName);

    int numInputs = *argc - 2; // Number of input files
    if (numInputs <= 1) {
        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Not enough input images supplied to combine.\n");
        return NULL;
    }

    options->inFiles = psArrayAlloc(numInputs);
    for (int i = 0; i < numInputs; i++) {
        psString inName = pmConfigConvertFilename(argv[i + 2], options->config, false);
        psFits *inFile = psFitsOpen(inName, "r");
        if (!inFile) {
            psError(PS_ERR_IO, false, "Unable to open input file %s.\n", inName);
            psFree(options);
            psFree(inName);
            return NULL;
        }
        psFree(inName);
        options->inFiles->data[i] = inFile;
    }

    OPTION_PARSE(options->rej,     recipe, "REJ",     F32);
    OPTION_PARSE(options->iter,    recipe, "ITER",    S32);
    OPTION_PARSE(options->maskVal, recipe, "MASKVAL", U8);

    const char *stat = psMetadataLookupStr(&mdok, recipe, "COMBINE");  // The combination statistic
    if (!mdok || !stat || strlen(stat) == 0) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't recognise statistic COMBINE.\n");
        psFree(options);
        return NULL;
    }
    options->combine = psStatsOptionFromString(stat);

    return options;
}
