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

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

#include "ppStack.h"

// Print usage information and die
static void usage(const char *program,  // Name of the program
                  psMetadata *arguments, // Command-line arguments
                  pmConfig *config      // Configuration
    )
{
    fprintf(stderr, "\nPan-STARRS Image combination\n\n");
    fprintf(stderr, "Usage: %s INPUTS.mdc OUTPUT_ROOT\n"
            "where INPUTS.mdc contains various METADATAs, each with:\n"
            "\tIMAGE(STR):  Image filename\n"
            "\tMASK(STR):   Mask filename\n"
            "\tSEEING(F32): Seeing FWHM (pixels)\n"
            "\tWEIGHT(F32): Relative weighting to be applied\n"
            "\tSCALE(F32):  Relative scaling to be applied\n",
            program);
    fprintf(stderr, "\n");
    psArgumentHelp(arguments);
    psFree(arguments);
    psFree(config);
    pmConfigDone();
    psLibFinalize();
    exit(PS_EXIT_CONFIG_ERROR);
}

// Get a float-point value from the command-line or recipe, and add it to the arguments
#define VALUE_ARG_RECIPE_FLOAT(ARGNAME, RECIPENAME, TYPE) { \
    ps##TYPE value = psMetadataLookup##TYPE(NULL, arguments, ARGNAME); \
    if (isnan(value)) { \
        bool mdok; \
        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
        if (!mdok) { \
            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
                RECIPENAME, PPSTACK_RECIPE); \
            goto ERROR; \
        } \
    } \
    psMetadataAdd##TYPE(config->arguments, PS_LIST_TAIL, RECIPENAME, 0, NULL, value); \
}

// Get an integer value from the command-line or recipe, and add it to the arguments
#define VALUE_ARG_RECIPE_INT(ARGNAME, RECIPENAME, TYPE, UNSET) { \
    ps##TYPE value = psMetadataLookup##TYPE(NULL, arguments, ARGNAME); \
    if (value == UNSET) { \
        bool mdok; \
        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
        if (!mdok) { \
            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
                RECIPENAME, PPSTACK_RECIPE); \
            goto ERROR; \
        } \
    } \
    psMetadataAdd##TYPE(config->arguments, PS_LIST_TAIL, RECIPENAME, 0, NULL, value); \
}

// Get a mask value from the command-line or recipe, and add it to the arguments
#define VALUE_ARG_RECIPE_MASK(ARGNAME, RECIPENAME) { \
    bool mdok; \
    const char *name = psMetadataLookupStr(&mdok, arguments, ARGNAME); \
    if (!mdok || !name || strlen(name) == 0) { \
        name = psMetadataLookupStr(NULL, recipe, RECIPENAME); \
        if (!name) { \
            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
                RECIPENAME, PPSTACK_RECIPE); \
            goto ERROR; \
        } \
    } \
    psMaskType value = pmConfigMask(name, config); \
    psMetadataAddU8(config->arguments, PS_LIST_TAIL, RECIPENAME, 0, NULL, value); \
}

// Get a string value from the command-line and add it to the target
static bool valueArgStr(pmConfig *config,      // Configuration
                        psMetadata *arguments, // Command-line arguments
                        const char *argName, // Argument name in the command-line arguments
                        const char *mdName, // Name for value in the metadata
                        psMetadata *target // Target metadata to which to add value
                        )
{
    psString value = psMetadataLookupStr(NULL, arguments, argName); // Value of interest
    if (value && strlen(value) > 0) {
        return psMetadataAddStr(target, PS_LIST_TAIL, mdName, 0, NULL, value);
    }
    return false;
}

bool ppStackArguments(int argc, char *argv[], pmConfig *config)
{
    assert(config);

    psMetadata *arguments = psMetadataAlloc(); // Command-line arguments
    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stats", 0, "Statistics file", NULL);
    psMetadataAddS32(arguments, PS_LIST_TAIL, "-iter", 0, "Number of rejection iterations", 0);
    psMetadataAddF32(arguments, PS_LIST_TAIL, "-combine-rej", 0, "Combination rejection thresold (sigma)", NAN);
    psMetadataAddF32(arguments, PS_LIST_TAIL, "-convolve-rej", 0, "Convolution rejection thresold (sigma)", NAN);
    psMetadataAddF32(arguments, PS_LIST_TAIL, "-extent", 0, "Extent of convolution (sigma)", NAN);
    psMetadataAddU8(arguments,  PS_LIST_TAIL, "-mask-bad", 0, "Mask value for bad pixels", 0);
    psMetadataAddU8(arguments,  PS_LIST_TAIL, "-mask-blank", 0, "Mask value for blank region", 0);

    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 3) {
        usage(argv[0], arguments, config);
    }

    unsigned int numBad = 0;                     // Number of bad lines
    psMetadata *inputs = psMetadataConfigRead(NULL, &numBad, argv[1], false); // Information about inputs
    if (!inputs || numBad > 0) {
        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to cleanly read MDC file with inputs.");
        goto ERROR;
    }
    psMetadataAddMetadata(config->arguments, PS_LIST_TAIL, "INPUTS", 0,
                          "Metadata with input details", inputs);
    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0,
                     "Root name of the output image list", argv[2]);

    valueArgStr(config, arguments, "-stats", "STATS", config->arguments);

    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // Recipe for ppSim
    if (!recipe) {
        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSTACK_RECIPE);
        goto ERROR;
    }

    VALUE_ARG_RECIPE_INT("-iter",           "ITER",         S32, 0);
    VALUE_ARG_RECIPE_FLOAT("-combine-rej",  "COMBINE.REJ",  F32);
    VALUE_ARG_RECIPE_FLOAT("-convolve-rej", "CONVOLVE.REJ", F32);
    VALUE_ARG_RECIPE_FLOAT("-extent",       "EXTENT",       F32);
    VALUE_ARG_RECIPE_MASK("-mask-bad",      "MASK.BAD");
    VALUE_ARG_RECIPE_MASK("-mask-blank",    "MASK.BLANK");

    psTrace("ppStack", 1, "Done reading command-line arguments\n");
    psFree(arguments);
    return true;

ERROR:
    psFree(arguments);
    return false;
}


