/*                                                                              *
 *   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       *
 *                                                                              */
/*
 * $Author: amodigli $
 * $Date: 2013-01-09 10:25:32 $
 * $Revision: 1.21 $
 * $Name: not supported by cvs2svn $
 * $Log: not supported by cvs2svn $
 * Revision 1.20  2010/09/24 09:31:29  amodigli
 * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
 *
 * Revision 1.18  2008/03/28 08:52:50  amodigli
 * IRPLIB_CONCAT2X-->UVES_CONCAT2X
 *
 * Revision 1.17  2007/08/27 07:12:18  amodigli
 * cleaned output
 *
 * Revision 1.16  2007/07/30 06:50:54  amodigli
 * defined several new h files (removed from flames_uves.h)
 *
 * Revision 1.15  2007/07/28 13:27:46  amodigli
 * Moved several function declarations from flames_uves.h to other new .h files
 *
 * Revision 1.14  2007/07/03 12:42:11  amodigli
 * removed hard coded RON setting
 *
 * Revision 1.13  2007/06/14 12:57:49  jmlarsen
 * Cleaned up some static memory usage
 *
 * Revision 1.12  2007/06/11 13:29:28  jmlarsen
 * Changed recipe contact address to cpl at eso.org
 *
 * Revision 1.11  2007/06/11 05:44:55  amodigli
 * changed uves_msg to uves_msg_debug
 *
 * Revision 1.10  2007/06/08 13:06:46  jmlarsen
 * Send bug reports to Andrea
 *
 * Revision 1.9  2007/06/06 08:17:33  amodigli
 * replace tab with 4 spaces
 *
 * Revision 1.8  2007/05/30 14:39:02  amodigli
 * remove uves_error_dump calls
 *
 * Revision 1.7  2007/05/18 15:45:35  amodigli
 * changed interface: pass BADPXFRAME
 *
 * Revision 1.6  2007/05/07 16:24:19  jmlarsen
 * Enabled flames_preppa
 *
 * Revision 1.5  2007/05/07 07:00:44  amodigli
 * added to repository
 *
 * Revision 1.4  2007/04/24 12:49:34  jmlarsen
 * Replaced cpl_propertylist -> uves_propertylist which is much faster
 *
 * Revision 1.3  2007/01/29 13:09:55  jmlarsen
 * Work on conversion to CPL
 *
 * Revision 1.2  2007/01/10 11:08:34  jmlarsen
 * Don't define DRS_USE_ORDEF
 *
 * Revision 1.1  2006/10/17 12:33:42  jmlarsen
 * Moved FLAMES source to flames directory
 *
 * Revision 1.5  2006/08/23 15:41:06  amodigli
 * removed warning from checks on line length
 *
 * Revision 1.4  2006/08/11 14:56:05  amodigli
 * removed Doxygen warnings
 *
 * Revision 1.3  2006/08/07 11:35:35  jmlarsen
 * Disabled parameter environment variable mode
 *
 * Revision 1.2  2006/07/31 06:28:11  amodigli
 * porting to CPL
 *
 * Revision 1.1  2006/06/26 08:05:08  amodigli
 * added to repository
 *
 */
#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;
}
/**@}*/
