/** @file pswarpArguments.c
 *
 *  @brief
 *
 *  @ingroup pswarp
 *
 *  @author IfA
 *  @version $Revision: 1.24 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2009-02-06 03:10:36 $
 *  Copyright 2009 Institute for Astronomy, University of Hawaii
 */

# include "pswarp.h"
# include <glob.h>


static void usage (void) {
    fprintf(stderr, "USAGE: pswarp [-file image(s)] [-list imagelist] [options] (output) (skycell)\n");
    fprintf(stderr, "  options:\n");
    fprintf(stderr, "    [-astrom astrom.cmp] : provide an alternative astrometry calibration\n");
    fprintf(stderr, "    [-mask mask.fits] : provide a corresponding mask image\n");
    fprintf(stderr, "    [-variance variance.fits] : provide a corresponding variance image\n");
    psErrorStackPrint(stderr, "\n");
    exit(PS_EXIT_CONFIG_ERROR);
}


pmConfig *pswarpArguments (int argc, char **argv) {

    bool status;
    int N;

    if (argc == 1) {
        usage();
    }

    // load config data from default locations
    pmConfig *config = pmConfigRead(&argc, argv, PSWARP_RECIPE);
    if (!config) {
        psError(psErrorCodeLast(), false, "Can't read configuration");
        return NULL;
    }

    // save the following additional recipe values based on command-line options
    // these options override the PSWARP recipe values loaded from recipe files
    if (!pmConfigRecipeOptions(config, PSWARP_RECIPE)) {
        psError(psErrorCodeLast(), false, "Can't do something with recipes");
        psFree(config);
        return NULL;
    }

    pmConfigFileSetsMD(config->arguments, &argc, argv, "ASTROM",   "-astrom", "-astromlist");

    {
        int arg;                        ///< Argument Number
        if ((arg = psArgumentGet(argc, argv, "-psphot-visual"))) {
            psArgumentRemove(arg, &argc, argv);
            pmVisualSetVisual(true);
        }
    }

    // chip selection is used to limit chips to be processed
    if ((N = psArgumentGet(argc, argv, "-chip"))) {
        psArgumentRemove(N, &argc, argv);
        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING,
                         "Only process these chips", argv[N]);
        psArgumentRemove(N, &argc, argv);
    }

    // Statistics file
    if ((N = psArgumentGet(argc, argv, "-stats"))) {
        psArgumentRemove(N, &argc, argv);
        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "STATS", PS_DATA_STRING,
                         "Filename for statistics of output image", argv[N]);
        psArgumentRemove(N, &argc, argv);
    }

    // Number of threads
    if ((N = psArgumentGet(argc, argv, "-threads"))) {
        psArgumentRemove(N, &argc, argv);
        int nThreads = atoi(argv[N]);
        psMetadataAddS32(config->arguments, PS_LIST_TAIL, "NTHREADS", 0, "number of warp threads", nThreads);
        psArgumentRemove(N, &argc, argv);

        // create the thread pool with number of desired threads, supplying our thread launcher function
        // XXX need to determine the number of threads from the config data
        psThreadPoolInit (nThreads);
    }
    pswarpSetThreads();

    if ((N = psArgumentGet(argc, argv, "-dumpconfig"))) {
        psArgumentRemove(N, &argc, argv);
        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "DUMP_CONFIG", PS_META_REPLACE,
                         "Filename for configuration dump", argv[N]);
        psArgumentRemove(N, &argc, argv);
    }


    pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
    pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK", "-mask", "-masklist");
    pmConfigFileSetsMD (config->arguments, &argc, argv, "VARIANCE", "-variance", "-variancelist");

    if (argc != 3) {
        usage();
    }


    psArray *array;

    // output position is fixed
    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);

    // skycell position is fixed
    array = psArrayAlloc(1);
    array->data[0] = psStringCopy(argv[2]);
    status = psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "SKYCELL", PS_DATA_ARRAY, "", array);
    psFree(array);

    psTrace("pswarp", 1, "Done with pswarpArguments...\n");
    return config;
}

/**
 * Parse the recipe and format into the arguments
 */
bool pswarpOptions(pmConfig *config)
{
    // Select the appropriate recipe
    psMetadata *recipe  = psMetadataLookupPtr(NULL, config->recipes, PSWARP_RECIPE);
    if (!recipe) {
        psError(PSWARP_ERR_CONFIG, true, "Can't find %s recipe!\n", PSWARP_RECIPE);
        return false;
    }

    // Get grid size
    bool status;                        ///< Status of MD lookup
    int nGridX = psMetadataLookupS32(&status, recipe, "GRID.NX");
    if (!status || nGridX <= 0) {
        nGridX = 128;
        psWarning("GRID.NX is not set in the recipe --- defaulting to %d", nGridX);
    }
    int nGridY = psMetadataLookupS32(&status, recipe, "GRID.NY");
    if (!status) {
        nGridY = 128;
        psWarning("GRID.NY is not set in the recipe --- defaulting to %d", nGridY);
    }

    // Get interpolation mode
    const char *name = psMetadataLookupStr (&status, recipe, "INTERPOLATION.MODE"); ///< Name of interp mode
    if (!name) {
        name = "BILINEAR";
        psLogMsg("pswarp", 3, "defaulting to %s interpolation", name);
    }
    psImageInterpolateMode interpolationMode = psImageInterpolateModeFromString(name); ///< Mode for interp.
    if (interpolationMode == PS_INTERPOLATE_NONE) {
        interpolationMode = PS_INTERPOLATE_BILINEAR;
        psLogMsg ("pswarp", 3,
                  "Unknown interpolation mode %s, defaulting to bilinear interpolation\n", name);
        name = "BILINEAR";
    }

    int numKernels = psMetadataLookupS32(&status, recipe, "INTERPOLATION.NUM");
    if (!status) {
        numKernels = 0;
        psWarning("INTERPOLATION.NUM is not set in the recipe --- defaulting to %d", numKernels);
    }

    float poorFrac = psMetadataLookupF32(&status, recipe, "POOR.FRAC"); ///< Frac of bad flux for a "poor"
    if (!status) {
        poorFrac = 0.0;
        psWarning("POOR.FRAC is not set in the %s recipe --- defaulting to %f.", PSWARP_RECIPE, poorFrac);
    }

    bool PSF = psMetadataLookupBool(&status, recipe, "PSF"); ///< Generate a PSF model?
    if (!status) {
        PSF = true;
        psWarning("PSF is not set in the %s recipe --- defaulting to TRUE.", PSWARP_RECIPE);
    }

    // Set recipe values in the recipe (since we've possibly altered some)
    psMetadataAddS32(recipe, PS_LIST_TAIL, "GRID.NX", PS_META_REPLACE,
                     "Iso-astrom grid spacing in x", nGridX);
    psMetadataAddS32(recipe, PS_LIST_TAIL, "GRID.NY", PS_META_REPLACE,
                     "Iso-astrom grid spacing in y", nGridY);
    psMetadataAddStr(recipe, PS_LIST_TAIL, "INTERPOLATION.MODE", PS_META_REPLACE,
                     "Interpolation mode", name);
    psMetadataAddS32(recipe, PS_LIST_TAIL, "INTERPOLATION.NUM", PS_META_REPLACE,
                     "Interpolation pre-calculated kernels", numKernels);
    psMetadataAddF32(recipe, PS_LIST_TAIL, "POOR.FRAC", PS_META_REPLACE,
                     "Fraction of bad flux for a pixel to be marked as poor", poorFrac);
    psMetadataAddBool(recipe, PS_LIST_TAIL, "PSF", PS_META_REPLACE, "Generate a PSF Model?", PSF);

    // Set recipe values in the arguments
    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "GRID.NX", 0,
                     "Iso-astrom grid spacing in x", nGridX);
    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "GRID.NY", 0,
                     "Iso-astrom grid spacing in y", nGridY);
    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "INTERPOLATION.MODE", 0,
                     "Interpolation mode", interpolationMode);
    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "INTERPOLATION.NUM", 0,
                     "Interpolation pre-calculated kernels", numKernels);
    psMetadataAddF32(config->arguments, PS_LIST_TAIL, "POOR.FRAC", 0,
                     "Fraction of bad flux for a pixel to be marked as poor", poorFrac);
    psMetadataAddBool(config->arguments, PS_LIST_TAIL, "PSF", PS_META_REPLACE, "Generate a PSF Model?", PSF);

    psTrace("pswarp", 1, "Done with pswarpArguments...\n");

    return (config);
}
