/*                                                                              *
 *   This file is part of the ESO UVES Pipeline                                 *
 *   Copyright (C) 2004,2005 European Southern Observatory                      *
 *                                                                              *
 *   This library is free software; you can redistribute it and/or modify       *
 *   it under the terms of the GNU General Public License as published by       *
 *   the Free Software Foundation; either version 2 of the License, or          *
 *   (at your option) any later version.                                        *
 *                                                                              *
 *   This program is distributed in the hope that it will be useful,            *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
 *   GNU General Public License for more details.                               *
 *                                                                              *
 *   You should have received a copy of the GNU General Public License          *
 *   along with this program; if not, write to the Free Software                *
 *   Foundation, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA       *
 *                                                                              */

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup flames_preppa  Recipe: Prepare a frame
 * TO BE WRITTEN
 */
/*----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 Includes
 -----------------------------------------------------------------------------*/

#include <flames_preppa_impl.h>

#include <flames_prepframe.h>
#include <flames_freetemplate.h>
#include <flames_writesigma.h>
#include <flames_initframe.h>
#include <flames_medianfilterframe.h>

#include <flames.h>
#include <flames_midas_def.h>
#include <flames_def_drs_par.h>
#include <flames_initemplate.h>
#include <flames_freeframe.h>
#include <flames_initbadpixel.h>

#include <uves_utils.h>
#include <uves_utils_wrappers.h>
#include <uves_parameters.h>
#include <uves_pfits.h>
#include <uves_recipe.h>
#include <uves.h>
#include <uves_dfs.h>
#include <uves_dump.h>
#include <uves_error.h>
#include <uves_msg.h>

#include <cpl.h>

/*-----------------------------------------------------------------------------
 Typedefs
 -----------------------------------------------------------------------------*/

typedef enum {
    MEDIAN, NONE
} filtertype;

/*-----------------------------------------------------------------------------
 Functions prototypes
 -----------------------------------------------------------------------------*/

#if 0
static cpl_error_code flames_process_chip(cpl_image** raw_image,
                                          uves_propertylist* raw_header,
                                          uves_propertylist* rotated_header,
                                          enum uves_chip chip,
                                          cpl_parameterlist* parameters,
                                          cpl_image* bp_map);

static cpl_error_code
flames_preppa_engine(cpl_image** raw_image,
                     uves_propertylist* raw_header,
                     uves_propertylist* rotated_header,
                     cpl_parameterlist* parameters,
                     enum uves_chip chip,
                     cpl_image* bp_map,
                     const char* filterswitch,
                     const double halfxwindow,
                     const double halfywindow,
                     const int maxiters,
                     const double kappa,
                     const char* satfilter,
                     const double p_thres_min,
                     const double p_thres_max,
                     const double ron,
                     const double gain);
#endif
static int
flames_preppa_define_parameters(cpl_parameterlist *parameters);

/*-----------------------------------------------------------------------------
 Static variables
 -----------------------------------------------------------------------------*/

static const char *flames_preppa_description_short =
                "Prepares a FLAMES-UVES frame";

static const char *flames_preppa_description =
                "Frame preparation procedure: a mask & a sigma is associated to the frame"
                "You should feed the procedure with: "
                "an input image frame"
                "fixme: describe i/o tags"
                "To be written\n";

/*-----------------------------------------------------------------------------
 Recipe standard code
 -----------------------------------------------------------------------------*/

#define cpl_plugin_get_info flames_preppa_get_info
UVES_RECIPE_DEFINE( FLAMES_PREPPA_ID, FLAMES_PREPPA_DOM,
                    flames_preppa_define_parameters, "Jonas M. Larsen", "cpl@eso.org",
                    flames_preppa_description_short, flames_preppa_description);

/**@{*/
/*----------------------------------------------------------------------------*/
/**
 @brief    Setup the recipe options
 @param    parameters        the parameterlist to fill
 @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int flames_preppa_define_parameters(cpl_parameterlist *parameters) {
    cpl_parameter *p;
    /*****************
     *    General    *
     *****************/

    if (uves_define_global_parameters(parameters) != CPL_ERROR_NONE) {
        return -1;
    }

    /*****************
     * Preprocessing *
     *****************/

    uves_parameter_new_enum(p, "flames_preppa.filt_mask", CPL_TYPE_STRING,
                            "Switch for filter-generated bad pixel mask", "flames_preppa",
                            DRS_FILT_MASK, 2, "Y", "N");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "filt_mask");
    cpl_parameterlist_append(parameters, p);

    uves_parameter_new_value(p, "flames_preppa.hwx", CPL_TYPE_INT,
                             "Filter X-half window", "flames_preppa", DRS_FILT_HW_X);

    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "hwx");
    cpl_parameterlist_append(parameters, p);

    uves_parameter_new_value(p, "flames_preppa.hwy", CPL_TYPE_INT,
                             "Filter Y-half window", "flames_preppa", DRS_FILT_HW_Y);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "hwy");
    cpl_parameterlist_append(parameters, p);

    uves_parameter_new_value(p, "flames_preppa.imax", CPL_TYPE_INT,
                             "Maximum filtering iterations", "flames_preppa", DRS_FILT_IMAX);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "imax");
    cpl_parameterlist_append(parameters, p);

    uves_parameter_new_value(p, "flames_preppa.ks", CPL_TYPE_DOUBLE,
                             "Kappa sigma parameter", "flames_preppa", DRS_FILT_KS);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ks");
    cpl_parameterlist_append(parameters, p);

    uves_parameter_new_enum(p, "flames_preppa.sat_sw", CPL_TYPE_STRING,
                            "Do you want mask saturated pixels?", "flames_preppa", DRS_FILT_SAT_SW,
                            2, "Y", "N");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sat_sw");
    cpl_parameterlist_append(parameters, p);

    uves_parameter_new_value(p, "flames_preppa.p_thres_min", CPL_TYPE_DOUBLE,
                             "Enter saturation threshold min", "flames_preppa", DRS_PTHRE_MIN);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "p_thres_min");
    cpl_parameterlist_append(parameters, p);

    uves_parameter_new_value(p, "flames_preppa.p_thres_max", CPL_TYPE_DOUBLE,
                             "Enter saturation threshold max", "flames_preppa", DRS_PTHRE_MAX);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "p_thres_max");
    cpl_parameterlist_append(parameters, p);

    return (cpl_error_get_code() != CPL_ERROR_NONE);
}

/*----------------------------------------------------------------------------*/
/**
 @brief    Prepare any FLAMES-UVES frame for data reduction
 @param    parameters recipe parameters
 @param    frames recipe input frames

 @return   0 if everything is ok
 */

/*----------------------------------------------------------------------------*/
/* The actual executor function */
static void UVES_CONCAT2X(FLAMES_PREPPA_ID,exe)(cpl_frameset *frames,
                const cpl_parameterlist *parameters, const char *starttime) {
    /* Input image */
    cpl_image *raw_img[2] = { NULL, NULL };
    uves_propertylist *raw_header[2] = { NULL, NULL };
    uves_propertylist *rot_header[2] = { NULL, NULL };
    cpl_table *ext[2] = { NULL, NULL };

#if 0
    cpl_image *mask[2] = {NULL, NULL};
    cpl_image* bp_map=NULL;
    /* Input guess table */

    /* Local variables */
    const char *raw_filename = "";
    int nraw=0;
    int i=0;

    bool blue= false; /* only RED arm data */
    enum uves_chip chip;
    cpl_frameset* raw=NULL;

#endif

    check(
                    uves_initialize(frames, parameters, make_str(FLAMES_PREPPA_ID), flames_preppa_description_short),
                    "Initialization failed");

#if 0   /* disable for now */

    /* Read recipe parameters */
    {

    }

    /* Load raw image and header, and identify input frame as red or blue */

    /*  read raw input frame

     */
    check(uves_extract_frames_group_type(frames,&raw,CPL_FRAME_GROUP_RAW),"Extract raw frames");

    check_nomsg(nraw=cpl_frameset_get_size(raw));

    for(i=0;i<nraw;i++) {

        check( flames_load_frame_index(raw, &raw_filename, raw_img,
                        raw_header, rot_header, ext,
                        i),
                        "Error loading raw frame %d",i);

    }

    /* Loop over one or two chips */
    for (chip = uves_chip_get_first(blue);
                    chip != UVES_CHIP_INVALID;
                    chip = uves_chip_get_next(chip))
    {
        const char *chip_name = "";

        int raw_index = uves_chip_get_index(chip);

        check_nomsg( chip_name = uves_pfits_get_chipid(raw_header[raw_index], chip));

        flames_process_chip(&raw_img[raw_index],
                            raw_header[raw_index],
                            rot_header[raw_index],
                            chip,
                            parameters,
                            bp_map);

    }

#endif  

    cleanup:
    /* Raw */
    uves_free_image(&(raw_img[0]));
    uves_free_image(&(raw_img[1]));

    uves_free_propertylist(&(raw_header[0]));
    uves_free_propertylist(&(raw_header[1]));

    uves_free_propertylist(&(rot_header[0]));
    uves_free_propertylist(&(rot_header[1]));

    uves_free_table(&ext[0]);
    uves_free_table(&ext[1]);

    /* Calibration */

    /* Product */

    return;

}
#if 0
/*----------------------------------------------------------------------------*/
/**
 @brief    Process each frame chip
 @param    raw_image  image
 @param    raw_header  header
 @param    rotated_header  rotated header
 @param    chip       chip ID
 @param    parameters recipe parameters
 @param    bp_map     bad pixel map
 @return   0 if everything is ok
 */

/*----------------------------------------------------------------------------*/

static cpl_error_code flames_process_chip(cpl_image** raw_image,
                                          uves_propertylist* raw_header,
                                          uves_propertylist* rotated_header,
                                          enum uves_chip chip,
                                          cpl_parameterlist* parameters,
                                          cpl_image* bp_map)

{

    const char* filt_sw=NULL;
    int max_iters=0;
    double kappa=0;
    const char* sat_filter=NULL;
    double p_thres_min=0;
    double p_thres_max=0;
    double half_width_window_x=0;
    double half_width_window_y=0;
    double ron=0;
    double gain=0;

    check( uves_get_parameter(parameters, NULL, FLAMES_PREPPA_ID "",
                    "filt_mask", CPL_TYPE_STRING, &filt_sw ),
           "Could not read parameter");

    check( uves_get_parameter(parameters, NULL, FLAMES_PREPPA_ID "",
                    "hwx", CPL_TYPE_INT, &half_width_window_x ),
           "Could not read parameter");

    check( uves_get_parameter(parameters, NULL, FLAMES_PREPPA_ID "",
                    "hwy", CPL_TYPE_INT, &half_width_window_y ),
           "Could not read parameter");

    check( uves_get_parameter(parameters, NULL, FLAMES_PREPPA_ID "",
                    "imax", CPL_TYPE_INT, &max_iters ),
           "Could not read parameter");

    check( uves_get_parameter(parameters, NULL, FLAMES_PREPPA_ID "",
                    "ks", CPL_TYPE_DOUBLE, &kappa ),
           "Could not read parameter");

    check( uves_get_parameter(parameters, NULL, FLAMES_PREPPA_ID "",
                    "sat_sw", CPL_TYPE_STRING, &sat_filter ),
           "Could not read parameter");

    check( uves_get_parameter(parameters, NULL, FLAMES_PREPPA_ID "",
                    "p_thres_min", CPL_TYPE_DOUBLE, &p_thres_min ),
           "Could not read parameter");

    check( uves_get_parameter(parameters, NULL, FLAMES_PREPPA_ID "",
                    "p_thres_max", CPL_TYPE_DOUBLE, &p_thres_max ),
           "Could not read parameter");

    flames_preppa_engine(raw_image,
                         raw_header,
                         rotated_header,
                         parameters,
                         chip,
                         bp_map,
                         filt_sw,
                         half_width_window_x,
                         half_width_window_y,
                         max_iters,
                         kappa,
                         sat_filter,
                         p_thres_min,
                         p_thres_max,
                         ron,
                         gain);

    cleanup:

    return (cpl_error_get_code() != CPL_ERROR_NONE);

}
#endif
#if 0
/*----------------------------------------------------------------------------*/
/**
 @brief    Process each frame chip: the real job is done here
 @param    raw_im  input raw image
 @param    raw_head  header
 @param    rot_head  rotated header
 @param    parameters recipe parameters
 @param    chip       chip ID
 @param    bp_map     bad pixel map
 @param    filterswitch filter/generated badpixel mask
 @param    halfxwindow  window X half size of box to perform median filter
 @param    halfywindow  window X half size of box to perform median filter
 @param    maxiters     maximum number of iterations to perform median filter
 @param    kappa        kappa values used to clip points in median filter
 @param    satfilter    string value to switch on/off the reading of additional parameters to define saturation threshold
 @param    p_thres_min  pixel (intensity) threshold minimum value
 @param    p_thres_max  pixel (intensity) threshold maximum value
 @param    ron          detector read out noise
 @param    gain         detector gain

 @return   0 if everything is ok
 */

/*----------------------------------------------------------------------------*/
static cpl_error_code
flames_preppa_engine(cpl_image** raw_im,
                     uves_propertylist* raw_head,
                     uves_propertylist* rot_head,
                     cpl_parameterlist* parameters,
                     enum uves_chip chip,
                     cpl_image* bp_map,
                     const char* filterswitch,
                     const double halfxwindow,
                     const double halfywindow,
                     const int maxiters,
                     const double kappa,
                     const char* satfilter,
                     const double p_thres_min,
                     const double p_thres_max,
                     const double ron,
                     const double gain)

{
    frame *myframe=0;
    frame *tpl=0;
    char framename[CATREC_LEN+5];
    char badpxframe[CATREC_LEN+5];
    filtertype filter=NONE;
    char filter2=FALSE;

    double satthres[2]= {0,0};
    frame_data fdsatthres[2]= {0,0};
    int stat=0;
    int actvals=0;

    memset(framename, 0, CATREC_LEN+5);
    memset(badpxframe, 0, CATREC_LEN+5);

    /* allocate memory for the structures */
    myframe = (frame *) calloc(1, sizeof(frame));
    tpl = (frame *) calloc(1, sizeof(frame));

    if (strncmp("YES", satfilter, (size_t) actvals) == 0) {
        /* ok, satfilter requested, read the other parameters */
        fdsatthres[0] = (frame_data) satthres[0];
        fdsatthres[1] = (frame_data) satthres[1];
        filter2 = TRUE;
    }
    else if (strncmp("NO", satfilter, (size_t) actvals) == 0) {
        filter2 = FALSE;
    }
    else {
        uves_msg_warning("The only supported satfilter options are YES and NO,");
        uves_msg_warning("falling back to NO");
        filter2 = FALSE;
    }

    /* compare as many letters as we have with the expected values */
    if (strcmp("MEDIAN", filterswitch) == 0) {
        /* ok, median filtering requested, read the other parameters */
        if ((maxiters<=0) || (kappa<=0)) {
            uves_msg_warning("MAXITERS and KAPPA must be positive, falling back to no \
filter");
            filter = NONE;
        }
        else if ((halfxwindow<0) || (halfywindow<0)) {
            uves_msg_warning("HALFWINDOWS must be non-negative, falling back to no \
filter");
            filter = NONE;
        }
        else {
            filter = MEDIAN;
        }
    }
    else if (strcmp("NONE", filterswitch) == 0) {
        filter = NONE;
    }
    else {
        uves_msg_warning("The only supported filter options are MEDIAN and NONE,");
        uves_msg_warning("falling back to NONE");
        filter = NONE;
    }

    /* have we got a general badpixel mask? Inquire...*/
    /* AMo here we should check if the input has a bad pix frame */

    /* it is defined and it is a character keyword, try to read in the
   badpixel mask */
    if (bp_map!=NULL) {
        /* read the badpixel frame into the template */
        uves_msg_debug("init bad pixel map");
        if ((stat = flames_initbadpixel(tpl, raw_head,chip,bp_map)) != NOERR) {
            /* I could not set up the template from the specified
       general badpixel frame */
            return(MAREMMA);
        }
    }
    else {
        /* the keyword is empty, no general bad pixel mask */
        /* the image is its own template */
        if ((stat = flames_initemplate(tpl, raw_head,chip)) != NOERR) {
            /* I could not set up the template from the first frame */
            return(MAREMMA);
        }
    }

    /* Ok, I have a template, let's read in the frame */
    if ((stat = flames_initframe(raw_im,raw_head,chip,myframe,framename,tpl,filter2,fdsatthres))
                    != NOERR) {
        /* problems in initframe */
        return(MAREMMA);
    }

    if (filter==MEDIAN) {
        /* let's add a median filter, to detect some obvious bad pixels */
        if ((stat = flames_medianfilterframe(myframe, halfxwindow, halfywindow,
                        maxiters, kappa*kappa)) != NOERR) {
            /* problems in medianfilterframe */
            return(MAREMMA);
        }
    }

    /* Let's write to disk the variance frame and the badpixel mask */
    if ((stat = flames_writesigma(myframe, framename)) != NOERR) {
        /* problems in writesigma */
        return(MAREMMA);
    }

    /* free dynamically allocated memory */
    if ((stat = flames_freeframe(myframe)) != NOERR) {
        /* error freeing myframe internal arrays */
        return(MAREMMA);
    }

    free(myframe);
    if ((stat = flames_freetemplate(tpl)) != NOERR) {
        /* error freeing template internal arrays */
        return(MAREMMA);
    }

    free(tpl);

    cleanup:
    return (cpl_error_get_code() != CPL_ERROR_NONE);
}
#endif

/*----------------------------------------------------------------------------*/
/**
 @brief    Process each frame chip: the real job is done here
 @param    image         to process
 @param    BADPXFRAME     bad pixel frame filename
 @param    filterswitch  filter/generated badpixel mask
 @param    drs_pthre_min saturation threshold
 @param    drs_pthre_max saturation threshold

 @return   0 if everything is ok
 */

/*----------------------------------------------------------------------------*/
void flames_preppa_process(cpl_frame *image, const char * BADPXFRAME,
                           const char *filterswitch, int drs_pthre_min, int drs_pthre_max) {

    double HALFWINDOWS[2] = { DRS_FILT_HW_X, DRS_FILT_HW_Y };

    int MAXITERS = DRS_FILT_IMAX;
    double KAPPA = DRS_FILT_KS;
    const char *SATFILTER = DRS_FILT_SAT_SW;
    double SATTHRES[2];

    const char *h_ron_l;
    const char *h_ron_u;
    const char *h_gain_l;
    const char *h_gain_u;
    const char *FRAMENAME = NULL;

    bool new_format;

    uves_propertylist *header = NULL;

    check( header = uves_propertylist_load(cpl_frame_get_filename(image), 0),
           "Could not load header from %s", cpl_frame_get_filename(image));

    check( new_format = uves_format_is_new(header),
           "Error determining FITS header format");

    uves_msg_debug("new format=%d", new_format);
    h_ron_l = UVES_RON(new_format, UVES_CHIP_REDL);
    h_ron_u = UVES_RON(new_format, UVES_CHIP_REDU);
    h_gain_l = UVES_GAIN(new_format, UVES_CHIP_REDL);
    h_gain_u = UVES_GAIN(new_format, UVES_CHIP_REDU);

    SATTHRES[0] = drs_pthre_min;
    SATTHRES[1] = drs_pthre_max;

    FRAMENAME = cpl_frame_get_filename(image);

    uves_msg_debug("Preparing frame %s", FRAMENAME);

    uves_msg_debug("RON_L=%s GAIN_L=%s", h_ron_l, h_gain_l);
    uves_msg_debug("RON_U=%s GAIN_U=%s", h_ron_u, h_gain_u);
    uves_msg_debug("BADPXFRAME=%s", BADPXFRAME);
    uves_msg_debug("FILTERSWITCH=%s", filterswitch);
    uves_msg_debug("HALWINDOWS=%f,%f", HALFWINDOWS[0], HALFWINDOWS[1]);
    uves_msg_debug("MAXITERS=%d", MAXITERS);
    uves_msg_debug("KAPPA=%f", KAPPA);
    uves_msg_debug("SATFILTER=%s", SATFILTER);
    uves_msg_debug("SATTHRES=%f,%f", SATTHRES[0], SATTHRES[1]);
    assure(
                    flames_prepframe(FRAMENAME, SATFILTER, filterswitch, BADPXFRAME, SATTHRES, &MAXITERS, &KAPPA, HALFWINDOWS, h_ron_l, h_ron_u, h_gain_l, h_gain_u) == 0,
                    CPL_ERROR_ILLEGAL_OUTPUT, "Error while preparing frame %s", FRAMENAME);

    cleanup: uves_free_propertylist(&header);
    return;
}
/**@}*/
