/*                                                                            *
 *   This file is part of the ESO X-shooter Pipeline                          *
 *   Copyright (C) 2006 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-30 10:21:21 $
 * $Revision: 1.190 $
 * $Name: not supported by cvs2svn $
 */

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

/*--------------------------------------------------------------------------*/
/**
 * @defgroup xsh_mflat   xsh_mflat
 * @ingroup recipes
 *
 * This recipe calculates the master flat frame
 * See man-page for details.
 */
/*--------------------------------------------------------------------------*/
/**@{*/

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


/* DRL steps */

/* Error handling */
#include <xsh_error.h>
/* Utility fonctions */
#include <xsh_pfits.h>
#include <xsh_utils.h>
#include <xsh_utils_image.h>
#include <xsh_msg.h>
/* DFS functions */
#include <xsh_dfs.h>
/* DRL functions */
#include <xsh_data_instrument.h>
#include <xsh_data_order.h>
#include <xsh_drl.h>
#include <xsh_drl_check.h>
#include <xsh_model_arm_constants.h>

/* Library */
#include <cpl.h>
#include <xsh_drl_check.h>

/*---------------------------------------------------------------------------
                                Defines
 ---------------------------------------------------------------------------*/

#define RECIPE_ID "xsh_mflat"
#define RECIPE_AUTHOR "P.Goldoni, L.Guglielmi, R. Haigron, F. Royer, D. Bramich, A. Modigliani"
#define RECIPE_CONTACT "amodigli@eso.org"

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

/*
 *   Plugin initalization, execute and cleanup handlers
 */

static int xsh_mflat_create(cpl_plugin *);
static int xsh_mflat_exec(cpl_plugin *);
static int xsh_mflat_destroy(cpl_plugin *);

/* The actual executor function */
static void xsh_mflat(cpl_parameterlist *, cpl_frameset *);

/*---------------------------------------------------------------------------
                            Static variables
 ---------------------------------------------------------------------------*/
static char xsh_mflat_description_short[] =
"Create the master flat and the orders edges traces table frames";

static char xsh_mflat_description[] =
"This recipe creates the master flat and the orders edges traces table frames.\n\
  Input Frames :\n\
    - [UVB] A set of n RAW frames (Format = RAW, n>=3,\
 Tag = FLAT_D2_mode_UVB, mode=SLIT/IFU)\n\
    - [UVB] A set of n RAW frames (Format = RAW, n>=3,\
 Tag = FLAT_QTH_mode_UVB)\n\
    - [VIS] A set of n RAW frames (Format = RAW, n>=3,\
 Tag = FLAT_mode_VIS)\n\
    - [NIR] A set of n x n RAW frames ((Format = RAW, n>=3,\
 Tag = FLAT_mode_NIR_ON, FLAT_mode_NIR_OFF)\n\
    - A spectral format table (Format = PRE, Tag = SPECTRAL_FORMAT_TAB_arm)\n\
    - An order table (Format = TABLE, Tag = ORDER_TAB_CENTR_arm)\n\
    - [UVB,VIS] A master bias (Format = PRE, Tag = MASTER_BIAS_arm)\n\
    - [OPTIONAL] A map of reference bad pixel (Format = QUP,RAW, Tag = BP_MAP_RP_arm)\n\
    - [OPTIONAL] A map of non linear pixel (Format = QUP,RAW, Tag = BP_MAP_NL_arm)\n\
    - [OPTIONAL,UVB,VIS] A master dark (Format = PRE, Tag = MASTER_DARK_arm)\n\
  Products : \n\
    - An updated order table with edge UP and edge LOW\
 (Format = TABLE, TAG = ORDER_TAB_EDGES_mode_arm)\n\
    - A master flat (Format = PRE, PRO.CATG = MASTER_FLAT_mode_arm)\n\
    - The inter-order background frame (Format = PRE, PRO.CATG = MFLAT_BACK_mode_arm)\n\
    - The inter-order background sampling points grid table\n\
      (Format = PRE, PRO.CATG = MFLAT_GRID_BACK_mode_arm)\n\
  Prepare the flat frames.\n\
  Stack and sigma clip all the flat frames.\n\
  Subtract master bias.\n\
  Subtract master dark.\n\
  Detect order edge.\n\
  Subtract background.\n\
  Create the Master Flat.\n";

/*---------------------------------------------------------------------------
                              Functions code
 ---------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/**
  @brief    Build the list of available plugins, for this module.
  @param    list    the plugin list
  @return   0 if everything is ok, -1 otherwise

  Create the recipe instance and make it available to the application using 
  the interface. This function is exported.
 */
/*--------------------------------------------------------------------------*/

int cpl_plugin_get_info(cpl_pluginlist *list) {
  cpl_recipe *recipe = NULL;
  cpl_plugin *plugin = NULL;

  recipe = cpl_calloc(1, sizeof(*recipe));
  if ( recipe == NULL ){
    return -1;
  }

  plugin = &recipe->interface ;

  cpl_plugin_init(plugin,
                  CPL_PLUGIN_API,                   /* Plugin API */
                  XSH_BINARY_VERSION,            /* Plugin version */
                  CPL_PLUGIN_TYPE_RECIPE,           /* Plugin type */
                  RECIPE_ID,                        /* Plugin name */
                  xsh_mflat_description_short,      /* Short help */
                  xsh_mflat_description,            /* Detailed help */
                  RECIPE_AUTHOR,                    /* Author name */
                  RECIPE_CONTACT,                   /* Contact address */
                  xsh_get_license(),                /* Copyright */
                  xsh_mflat_create,
                  xsh_mflat_exec,
                  xsh_mflat_destroy);

  cpl_pluginlist_append(list, plugin);

  return (cpl_error_get_code() != CPL_ERROR_NONE);
 }

/*--------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options
  @param    plugin  the plugin
  @return   0 if everything is ok

  Create the recipe instance and make it available to the application using 
  the interface.

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

static int xsh_mflat_create(cpl_plugin *plugin){
  cpl_recipe *recipe = NULL;
  xsh_stack_param stack_param = {"median",5.,5.};

  /* Reset library state */
  xsh_init();

  /* Check input */
  assure( plugin != NULL, CPL_ERROR_NULL_INPUT, "Null plugin");

  /* Get the recipe out of the plugin */
  assure( cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE,
          CPL_ERROR_TYPE_MISMATCH,
          "Plugin is not a recipe");

  recipe = (cpl_recipe *)plugin;

  /* Create the parameter list in the cpl_recipe object */
  recipe->parameters = cpl_parameterlist_new();
  assure( recipe->parameters != NULL,
          CPL_ERROR_ILLEGAL_OUTPUT,
          "Memory allocation failed!");

  /* Fill the parameter list */
  /* Set generic parameters (common to all recipes) */
  check( xsh_parameters_generic( RECIPE_ID, recipe->parameters ) ) ;
  xsh_parameters_decode_bp(RECIPE_ID,recipe->parameters,-1);
  check( xsh_parameters_pre_overscan( RECIPE_ID, recipe->parameters ) ) ;
  check(xsh_parameters_stack_create(RECIPE_ID,recipe->parameters,stack_param));

  /* detect order params */
  check( xsh_parameters_detect_order_create( RECIPE_ID,
    recipe->parameters));

  /* d2 detect order params */
  check( xsh_parameters_d2_detect_order_create( RECIPE_ID,
    recipe->parameters));

  /* subtract_background_params */
  check( xsh_parameters_background_create( RECIPE_ID,
    recipe->parameters));

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE ){
      xsh_error_dump(CPL_MSG_ERROR);
      return 1;
    }
    else {
      return 0;
    }
}

/*--------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*--------------------------------------------------------------------------*/

static int xsh_mflat_exec(cpl_plugin *plugin) {
  cpl_recipe *recipe = NULL;

  /* Check parameter */
  assure( plugin != NULL, CPL_ERROR_NULL_INPUT, "Null plugin" );

  /* Get the recipe out of the plugin */
  assure( cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE,
          CPL_ERROR_TYPE_MISMATCH, "Plugin is not a recipe");

  recipe = (cpl_recipe *)plugin;

  /* Check recipe */
  xsh_mflat(recipe->parameters, recipe->frames);

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
      xsh_error_dump(CPL_MSG_ERROR);
      /* RESTORE the error context */
      cpl_error_reset();
      return 1;
    }
    else {
      return 0;
    }
}

/*--------------------------------------------------------------------------*/
/**
  @brief    Destroy what has been created by the 'create' function
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*--------------------------------------------------------------------------*/
static int xsh_mflat_destroy(cpl_plugin *plugin)
{
    cpl_recipe *recipe = NULL;

    /* Check parameter */
    assure( plugin != NULL, CPL_ERROR_NULL_INPUT, "Null plugin" );

    /* Get the recipe out of the plugin */
    assure( cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE,
            CPL_ERROR_TYPE_MISMATCH, "Plugin is not a recipe");

    recipe = (cpl_recipe *)plugin;
 
    xsh_free_parameterlist(&recipe->parameters);

  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE)
        {
            return 1;
        }
    else
        {
            return 0;
        }
}

static cpl_frame*
xsh_mflat_combine_flats(cpl_frameset *raw_set, cpl_frameset *on_set,
    cpl_frameset *off_set, cpl_frame *bpmap_frame, cpl_frame *master_bias_frame,
    cpl_frame *master_dark_frame, cpl_frame *cen_order_tab_frame,
    xsh_instrument* instrument, xsh_stack_param* stack_par,
    const int pre_overscan_corr) {

  cpl_frame *rmdark = NULL;
  cpl_frameset *onoff_set = NULL;
  if (xsh_instrument_get_arm(instrument) != XSH_ARM_NIR) {
    /* prepare RAW frames in XSH format */
    check(
        xsh_prepare( raw_set, bpmap_frame, master_bias_frame, XSH_FLAT, instrument,pre_overscan_corr,CPL_TRUE));

    cpl_frameset *sub_bias_set = NULL;
    cpl_frameset *sub_dark_set = NULL;
    cpl_frame* frm = NULL;
    cpl_frame* sub = NULL;
    char prefix[256];
    int i = 0;
    int nraws = 0;
    if (master_bias_frame != NULL) {

      xsh_msg( "Subtract bias");
      sub_bias_set = cpl_frameset_new();
      nraws = cpl_frameset_get_size(raw_set);

      for (i = 0; i < nraws; i++) {
        sprintf(prefix, "FLAT_SUB_%d_", i);
        frm = cpl_frameset_get_frame(raw_set, i);
        //xsh_free_frame(&sub);
        sub = xsh_subtract_bias(frm, master_bias_frame, instrument, prefix,
            pre_overscan_corr, 1);
        cpl_frameset_insert(sub_bias_set, sub);
      }
    } else {
      sub_bias_set = cpl_frameset_duplicate(raw_set);
    }

    if (master_dark_frame != NULL) {
      xsh_msg( "Subtract dark");

      sub_dark_set = cpl_frameset_new();
      nraws = cpl_frameset_get_size(sub_bias_set);

      for (i = 0; i < nraws; i++) {
        sprintf(prefix, "FLAT_SUBTRACT_DARK_%d.fits", i);
        frm = cpl_frameset_get_frame(sub_bias_set, i);
        //xsh_free_frame(&sub);
        sub = xsh_subtract_dark(frm, master_dark_frame, prefix, instrument);
        cpl_frameset_insert(sub_dark_set, sub);
      }

    } else {
      sub_dark_set = cpl_frameset_duplicate(sub_bias_set);
    }
    xsh_free_frameset(&sub_bias_set);

    check(
        rmdark=xsh_create_master_flat2(sub_dark_set,cen_order_tab_frame,stack_par,instrument));
    xsh_free_frameset(&sub_dark_set);

  }
  /* in NIR mode */
  else {
    check(
        xsh_prepare( on_set, bpmap_frame, NULL, "ON", instrument,pre_overscan_corr,CPL_TRUE));
    check(
        xsh_prepare( off_set, bpmap_frame, NULL, "OFF", instrument,pre_overscan_corr,CPL_TRUE));
    /* do ON - OFF */
    check( onoff_set = xsh_subtract_nir_on_off( on_set, off_set, instrument));
    check(
        rmdark=xsh_create_master_flat2(onoff_set,cen_order_tab_frame,stack_par,instrument));

  }


  cleanup:
  xsh_free_frameset(&onoff_set);
  return rmdark;
}


static void 
xsh_combine_flats_and_detect_edges( cpl_frameset *raw_set,
                       cpl_frameset *on_set,
		       cpl_frameset *off_set, 
		       cpl_frame *bpmap_frame,
		       cpl_frame *master_bias_frame, 
		       cpl_frame *master_dark_frame,  
                       cpl_frame *cen_order_tab_frame,
                       cpl_frame *qc_cen_order_tab_frame,
		       xsh_stack_param* stack_par,
		       xsh_detect_order_param *detectorder_par, 
		       const int pre_overscan_corr,
		       cpl_frame **edges_order_tab_frame,  
		       cpl_frame **rmdark,
		       xsh_instrument* instrument)
{


  check(*rmdark=xsh_mflat_combine_flats(raw_set,on_set,off_set,bpmap_frame,master_bias_frame,
      master_dark_frame,cen_order_tab_frame,instrument,stack_par,pre_overscan_corr));

  /* for QC monitor the lamp level after (bias) and dark subtraction */
  check(xsh_monitor_flux(*rmdark,qc_cen_order_tab_frame, instrument));

  /* detect order edge */
  xsh_msg("Detect order edges");
  check( *edges_order_tab_frame = xsh_detect_order_edge( *rmdark,
    cen_order_tab_frame, detectorder_par, instrument));

  cleanup:

    return;
}

/*--------------------------------------------------------------------------*/
/**
  @brief    Rescale parameters for binning
  @param    raws   raw frames set
  @param    backg parameters to control inter-order background subtraction
  @param    det_order parameters to control order edges detection
  In case of failure the cpl_error_code is set.
 */
/*--------------------------------------------------------------------------*/

static cpl_error_code 
xsh_params_bin_scale(cpl_frameset* raws,
                     xsh_background_param* backg,
                     xsh_detect_order_param* det_order)
{

  cpl_frame* frame=NULL;
  const char* name=NULL;
  cpl_propertylist* plist=NULL;
  int binx=0;
  int biny=0;

  check(frame=cpl_frameset_get_frame(raws,0));
  check(name=cpl_frame_get_filename(frame));
  check(plist=cpl_propertylist_load(name,0));
  check(binx=xsh_pfits_get_binx(plist));
  check(biny=xsh_pfits_get_biny(plist));
  xsh_free_propertylist(&plist);

  if(biny>1) {

    /* backg->sampley=backg->sampley/biny; 
       No of sampling points, not to be sampling bin dependent */
    backg->radius_y=backg->radius_y/biny;

  }


  if(binx>1) {

    backg->radius_x=backg->radius_x/binx;
    det_order->search_window_hsize=det_order->search_window_hsize/biny;
    det_order->min_order_size_x=det_order->min_order_size_x/binx;

  }
 
 cleanup:
  xsh_free_propertylist(&plist);
  return cpl_error_get_code();

}

static cpl_error_code 
xsh_params_set_defaults(cpl_parameterlist* pars,
                        xsh_instrument* inst,
                        xsh_detect_order_param* det_order,
                        xsh_background_param* backg)
{
   cpl_parameter* p=NULL;

   check(p=xsh_parameters_find(pars,RECIPE_ID,"detectorder-min-order-size-x"));
   if(cpl_parameter_get_int(p) <= 0) {
      if (xsh_instrument_get_arm(inst) == XSH_ARM_NIR){
         det_order->min_order_size_x=40;
      } else {
         det_order->min_order_size_x=60;
      } 
   }

   check(p=xsh_parameters_find(pars,RECIPE_ID,"detectorder-min-sn"));
   if(cpl_parameter_get_double(p) <= 0) {
      if (xsh_instrument_get_arm(inst) == XSH_ARM_NIR){
         if(xsh_instrument_get_mode(inst) == XSH_MODE_IFU) {
            det_order->min_sn=4;
         } else {
            det_order->min_sn=60;
         }
      } else {
         if(xsh_instrument_get_mode(inst) == XSH_MODE_IFU) {
            det_order->min_sn=20;
         } else {
           if (xsh_instrument_get_arm(inst) == XSH_ARM_UVB){
              det_order->min_sn=20;
           } else {
              det_order->min_sn=40;
           }
         }
      }
   }

  check(p=xsh_parameters_find(pars,RECIPE_ID,"detectorder-slice-trace-method"));
  if(strcmp(cpl_parameter_get_string(p),"auto") == 0) {
     if (xsh_instrument_get_mode(inst) == XSH_MODE_IFU){
        cpl_parameter_set_default_string(p,"sobel");
     } else {
        cpl_parameter_set_default_string(p,"fixed");
     }
  }

 cleanup:
 
  return cpl_error_get_code();

}

static cpl_error_code
xsh_qc_ifu_flat(cpl_frame* mflat,xsh_instrument* instrument)
{
    int do_it = 0;
    if ( (xsh_instrument_get_arm(instrument) == XSH_ARM_UVB) ||
                    (xsh_instrument_get_arm(instrument) == XSH_ARM_VIS) ) {
        int binx=instrument->binx;
        int biny=instrument->biny;
        if( binx == 1 && biny == 1 ) {
            do_it = 1;
        }
    } else {
        if ( xsh_instrument_get_arm(instrument) == XSH_ARM_NIR ) {
            do_it = 1;
        }
    }

    if(do_it) {
        xsh_pre* pre = xsh_pre_load(mflat,instrument);
        double s1r=0;
        double s2r=0;
        double s3r=0;

        double s1b=0;
        double s2b=0;
        double s3b=0;

        if (xsh_instrument_get_arm(instrument) == XSH_ARM_UVB){


            s1r=cpl_image_get_median_window(pre->data,1454,1500,1469,1600);
            s2r=cpl_image_get_median_window(pre->data,1479,1500,1491,1600);
            s3r=cpl_image_get_median_window(pre->data,1503,1500,1516,1600);

            s1b=cpl_image_get_median_window(pre->data,515,2380,530,2380);
            s2b=cpl_image_get_median_window(pre->data,541,2330,555,2380);
            s3b=cpl_image_get_median_window(pre->data,566,2330,579,2380);

        } else if (xsh_instrument_get_arm(instrument) == XSH_ARM_VIS){

            s1r=cpl_image_get_median_window(pre->data,1652,2050,1666,2100);
            s2r=cpl_image_get_median_window(pre->data,1674,2050,1684,2100);
            s3r=cpl_image_get_median_window(pre->data,1697,2050,1711,2100);

            s1b=cpl_image_get_median_window(pre->data,426,2650,444,2757);
            s2b=cpl_image_get_median_window(pre->data,452,2650,470,2757);
            s3b=cpl_image_get_median_window(pre->data,477,2650,496,2757);

        } else if (xsh_instrument_get_arm(instrument) == XSH_ARM_NIR){

            s1r=cpl_image_get_median_window(pre->data,620,1133,630,1175);
            s2r=cpl_image_get_median_window(pre->data,636,1133,646,1175);
            s3r=cpl_image_get_median_window(pre->data,653,1133,663,1175);

            s1b=cpl_image_get_median_window(pre->data,128,1080,133,1111);
            s2b=cpl_image_get_median_window(pre->data,141,1080,150,1111);
            s3b=cpl_image_get_median_window(pre->data,158,1080,167,1111);

        }
        xsh_msg("s1b %g",s1b);
        xsh_msg("s2b %g",s2b);
        xsh_msg("s3b %g",s3b);

        xsh_msg("s1r %g",s1r);
        xsh_msg("s2r %g",s2r);
        xsh_msg("s3r %g",s3r);

        double A=(s2r-s2b)/s2r;
        double m=(s1r+s2r+s3r)/3.0;
        double a=(m-s1r)*(m-s1r);
        double b=(m-s2r)*(m-s2r);
        double c=(m-s3r)*(m-s3r);
        double R=sqrt((a+b+c)/2.0);

        double RD=(s1r-s3r)/(s1r+s2r)*2.0;

        m=(s1b+s2b+s3b)/3.0;
        a=(m-s1b)*(m-s1b);
        b=(m-s2b)*(m-s2b);
        c=(m-s3b)*(m-s3b);
        double B=sqrt((a+b+c)/2.0);
        double BD=(s1b-s3b)/(s1b+s2b)*2.0;

        xsh_msg("FLUX_ORDER_RATIO %g",A);
        xsh_msg("SLICE_RMS        %g",R);
        xsh_msg("SLICE2_RMS       %g",B);
        xsh_msg("SLICE_S_DIFF     %g",RD);
        xsh_msg("SLICE2_S_DIFF    %g",BD);

        cpl_propertylist_append_double(pre->data_header,"QC.FRATIO",A);
        cpl_propertylist_append_double(pre->data_header,"QC.SLICER.RMS",R);
        cpl_propertylist_append_double(pre->data_header,"QC.SLICEB.RMS",B);
        cpl_propertylist_append_double(pre->data_header,"QC.SLICER.SDIFF",RD);
        cpl_propertylist_append_double(pre->data_header,"QC.SLICEB.SDIFF",BD);
        xsh_pre_save(pre,cpl_frame_get_filename(mflat),cpl_frame_get_tag(mflat),0);
        xsh_pre_free(&pre);
    }

    return cpl_error_get_code();
}



/*--------------------------------------------------------------------------*/
/**
  @brief    Interpret the command line options and execute the data processing
  @param    parameters     the parameters list
  @param    frameset   the frames list

  In case of failure the cpl_error_code is set.
 */
/*--------------------------------------------------------------------------*/
static void xsh_mflat(cpl_parameterlist* parameters, cpl_frameset* frameset)
{
  const char* recipe_tags[1] = {XSH_FLAT};
  int recipe_tags_size = 1;

  /* ALLOCATED locally */
  cpl_frameset* raws = NULL;
  cpl_frameset* calib = NULL;
  cpl_frameset* on = NULL;
  cpl_frameset* off = NULL;
  cpl_frameset* qth = NULL;
  cpl_frameset* d2 = NULL;
  xsh_instrument* instrument = NULL;
  /* Input frames */
  cpl_frame* spectralformat_frame = NULL;
  cpl_frame* bpmap = NULL;
  cpl_frame* master_bias = NULL;
  cpl_frame* master_dark = NULL;
  cpl_frame* cen_order_tab_frame = NULL;
  /* Products */
  cpl_frame* edges_order_tab_frame = NULL;
  cpl_frame* master_flat_frame = NULL;
  cpl_frame* master_bkg_frame = NULL;

  cpl_frame *qth_edges_order_tab_frame = NULL;
  cpl_frame *qth_master_flat_frame = NULL;
  cpl_frame *d2_edges_order_tab_frame = NULL;
  cpl_frame* d2_master_flat_frame = NULL;
  /* Parameters */
  xsh_detect_order_param* det_order = NULL;
  xsh_d2_detect_order_param * d2_det_order = NULL;
  xsh_background_param* backg = NULL;
  /* Others */
  cpl_frame *qth_cen_order_tab_frame = NULL;
  cpl_frame *d2_cen_order_tab_frame = NULL;
  //char fname[256];
  char fname_d2[256];
  char fname_qth[256];
  //char pcatg[256];
  cpl_table* grid_tab1=NULL;
  cpl_table* grid_tab2=NULL;
  cpl_propertylist* plist=NULL;
  cpl_frame* rmbackground=NULL;
  cpl_frame* qth_rmbackground=NULL;
  cpl_frame* d2_rmbackground=NULL;
  cpl_frame* grid_backg=NULL;
  cpl_frame* d2_grid_backg=NULL;
  cpl_frame* qth_grid_backg=NULL;
  cpl_frame* frame_backg=NULL;
  cpl_frame* d2_frame_backg=NULL;
  cpl_frame* qth_frame_backg=NULL;
  int pre_overscan_corr=0;
  xsh_stack_param* stack_par=NULL;
  //const int mode_or=1;
  cpl_frame *rmdark = NULL;
  cpl_frame *qth_rmdark = NULL;
  cpl_frame *d2_rmdark = NULL;

  /**************************************************************************/
  /* DFS management */
  /**************************************************************************/
  check( xsh_begin( frameset, parameters, &instrument, &raws, &calib,
		    recipe_tags, recipe_tags_size,
		    RECIPE_ID, XSH_BINARY_VERSION,
		    xsh_mflat_description_short ) ) ;

  xsh_recipe_params_check(parameters,instrument,RECIPE_ID);
  if(instrument->arm == XSH_ARM_NIR) {
    xsh_instrument_nir_corr_if_JH(raws,instrument);
  }

  /**************************************************************************/
  /* Recipe frames */
  /**************************************************************************/

  check(bpmap=xsh_check_load_master_bpmap(calib,instrument,RECIPE_ID));
  check( cen_order_tab_frame = xsh_find_order_tab_centr( calib, instrument));
  // TODO: my or DRS function?
  //check(order_tab_centr = xsh_find_frame_with_tag(calib,XSH_ORDER_TAB_CENTR,
  //                                                 instrument));

  check( spectralformat_frame = xsh_find_frame_with_tag( calib,
    XSH_SPECTRAL_FORMAT, instrument));


  if (xsh_instrument_get_arm(instrument) == XSH_ARM_UVB){
    XSH_ASSURE_NOT_ILLEGAL_MSG(cpl_frameset_get_size(raws) >= 2,"UVB arm requires D2 and QTH input");
    XSH_ASSURE_NOT_ILLEGAL_MSG( instrument->lamp == XSH_LAMP_QTH_D2,"Something wrong in instrument setting");
  }
  else{
    XSH_ASSURE_NOT_ILLEGAL_MSG(cpl_frameset_get_size(raws) >= 1,"Provide at least one input raw flat frame");
  }


  /* In UVB and VIS mode */
  if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
    if ((master_bias = xsh_find_frame_with_tag(calib, XSH_MASTER_BIAS,
        instrument)) == NULL) {

      xsh_msg_warning("Frame %s not provided", XSH_MASTER_BIAS);
      xsh_error_reset();
    }

    if((master_dark = xsh_find_frame_with_tag(calib,XSH_MASTER_DARK,
                 instrument)) == NULL){
       xsh_msg_warning("Frame %s not provided",XSH_MASTER_DARK);
       xsh_error_reset();
     }

    if ( xsh_instrument_get_arm( instrument) == XSH_ARM_UVB){
      check( xsh_dfs_split_qth_d2( raws, &qth, &d2));
    }
  }
  /* IN NIR mode */
  else {
    /* split on and off files */
    check(xsh_dfs_split_nir(raws,&on,&off));
  }


  check( xsh_instrument_update_from_spectralformat( instrument,
    spectralformat_frame));

  /**************************************************************************/
  /* Recipe parameters */
  /**************************************************************************/
  check( pre_overscan_corr = xsh_parameters_get_int( parameters, RECIPE_ID,
						     "pre-overscan-corr"));
  check( stack_par = xsh_stack_frames_get( RECIPE_ID, parameters));
  check( det_order = xsh_parameters_detect_order_get( RECIPE_ID,
    parameters,parameters));
  check( d2_det_order = xsh_parameters_d2_detect_order_get( RECIPE_ID,
    parameters));
  check( backg =  xsh_parameters_background_get( RECIPE_ID,
    parameters));

  xsh_params_set_defaults(parameters,instrument,det_order,backg);
  /* adjust relevant parameter to binning */
  if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
    check(xsh_params_bin_scale(raws,backg,det_order));
  }


  /**************************************************************************/
  /* Recipe code */
  /**************************************************************************/
  if ( xsh_instrument_get_arm(instrument) != XSH_ARM_UVB){
     /* VIS or NIR */
     check( xsh_combine_flats_and_detect_edges(raws, on, off,bpmap,master_bias,
				  master_dark, cen_order_tab_frame,
				  cen_order_tab_frame,stack_par, 
				  det_order,
				  pre_overscan_corr,
				  &edges_order_tab_frame, &rmdark,
                                  instrument));

     // subtract inter-order background
     xsh_msg("Subtract inter-order background");
     check( rmbackground = xsh_subtract_background(rmdark,
                 edges_order_tab_frame,
                 backg,
                 instrument, "MFLAT",
                 &grid_backg,
                 &frame_backg,1,1,0));

     // Create the master flat: normalises, set FITS header, QC
     check(master_flat_frame = xsh_create_master_flat_with_mask(rmbackground,edges_order_tab_frame,instrument));


     xsh_msg("Detect dead-saturated pixels");
     check(xsh_image_mflat_detect_blemishes(master_flat_frame, instrument));
     /* Temporarily disactivated */
     if(xsh_instrument_get_mode(instrument) == XSH_MODE_IFU) {
         xsh_qc_ifu_flat(master_flat_frame,instrument);
     }

     /***********************************************************************/
     /* Products */
     /***********************************************************************/
     xsh_msg("Saving products");
     check( xsh_add_product_table( edges_order_tab_frame, frameset,
                                         parameters,RECIPE_ID,instrument,NULL));

     check( xsh_add_product_pre( master_flat_frame, frameset, 
                                 parameters, RECIPE_ID, instrument));
     /*
     check( xsh_add_product_image( rmbackground, frameset, 
                                   parameters, RECIPE_ID, instrument,NULL));
     */
     check( xsh_add_product_image( frame_backg, frameset, parameters, RECIPE_ID,
                                   instrument,NULL));

     check(xsh_add_product_table( grid_backg, frameset,parameters, 
                                        RECIPE_ID, instrument,NULL));

     /* END case VIS/NIR arm */
  } else {  
     /* BEGIN case UVB arm */
 
     check( xsh_order_split_qth_d2( cen_order_tab_frame, spectralformat_frame, 
                                    &qth_cen_order_tab_frame, &d2_cen_order_tab_frame, instrument));

     check( xsh_instrument_update_lamp( instrument, XSH_LAMP_QTH));
     check( xsh_combine_flats_and_detect_edges( qth, on, off,bpmap, master_bias,
                                   master_dark, qth_cen_order_tab_frame,
                                   cen_order_tab_frame,stack_par, 
                                   det_order,
 				  pre_overscan_corr,
                                  &qth_edges_order_tab_frame, 
                                   &qth_rmdark,
                                   instrument));



     det_order->min_sn = d2_det_order->min_sn;
     check( xsh_instrument_update_lamp( instrument, XSH_LAMP_D2));
     check( xsh_combine_flats_and_detect_edges( d2, on, off,bpmap, master_bias,
                                   master_dark, d2_cen_order_tab_frame,
                                   cen_order_tab_frame,stack_par,
                                   det_order,
          pre_overscan_corr,
                                  &d2_edges_order_tab_frame,
                                   &d2_rmdark,
                                   instrument));


     check(edges_order_tab_frame=xsh_flat_merge_qth_d2_tabs(qth_edges_order_tab_frame,
                                  d2_edges_order_tab_frame,instrument));


     check( xsh_instrument_update_lamp( instrument, XSH_LAMP_UNDEFINED));
     // subtract inter-order background
     xsh_msg("Subtract inter-order background");
     check( qth_rmbackground = xsh_subtract_background( qth_rmdark,
                 edges_order_tab_frame,
                 backg,
                 instrument, "MFLAT_QTH",
                 &qth_grid_backg,
                 &qth_frame_backg,1,1,0));

     // Create the master flat: normalises, set FITS header, QC
     check( xsh_instrument_update_lamp( instrument, XSH_LAMP_QTH));
     check(qth_master_flat_frame = xsh_create_master_flat_with_mask(qth_rmbackground,edges_order_tab_frame,instrument));
 
 
     check( xsh_add_product_table( qth_edges_order_tab_frame, frameset,
                                         parameters,RECIPE_ID,instrument,NULL));
     check( xsh_add_product_pre( qth_master_flat_frame, frameset,
                                 parameters, RECIPE_ID, instrument));
     /*
     check( xsh_add_product_image( qth_rmbackground, frameset,
                                   parameters, RECIPE_ID, instrument,NULL));
                                   */
     check( xsh_add_product_image( qth_frame_backg, frameset,
                                   parameters, RECIPE_ID, instrument,NULL));

     check(xsh_add_product_table(qth_grid_backg,frameset,parameters,
                                        RECIPE_ID, instrument,NULL));
     xsh_msg("name=%s tag=%s",cpl_frame_get_filename(qth_grid_backg),cpl_frame_get_tag(qth_grid_backg));

     /*
     det_order->min_sn = d2_det_order->min_sn;
     check( xsh_instrument_update_lamp( instrument, XSH_LAMP_D2));
     check( xsh_combine_flats_and_detect_edges( d2, on, off,bpmap, master_bias,
                                   master_dark, d2_cen_order_tab_frame, 
                                   cen_order_tab_frame,stack_par, 
                                   det_order,
 				  pre_overscan_corr,
                                  &d2_edges_order_tab_frame, 
                                   &d2_rmdark,
                                   instrument));

    */

     // subtract inter-order background
     check( xsh_instrument_update_lamp( instrument, XSH_LAMP_UNDEFINED));
     xsh_msg("Subtract inter-order background");
     check( d2_rmbackground = xsh_subtract_background( d2_rmdark,
                 edges_order_tab_frame,
                 backg,
                 instrument, "MFLAT_D2",
                 &d2_grid_backg,
                 &d2_frame_backg,1,1,0));

     // Create the master flat: normalises, set FITS header, QC
     check( xsh_instrument_update_lamp( instrument, XSH_LAMP_D2));
     check(d2_master_flat_frame = xsh_create_master_flat_with_mask(d2_rmbackground,d2_edges_order_tab_frame,instrument));

     /* merge grid bkg tables for d2 and qth */
     sprintf(fname_d2,"MFLAT_D2_GRID_BACK_%s_%s.fits",
             xsh_instrument_mode_tostring( instrument),
             xsh_instrument_arm_tostring( instrument));

     sprintf(fname_qth,"MFLAT_QTH_GRID_BACK_%s_%s.fits",
             xsh_instrument_mode_tostring( instrument),
             xsh_instrument_arm_tostring( instrument));
     /*
     check(edges_order_tab_frame=xsh_flat_merge_qth_d2_tabs(qth_edges_order_tab_frame,
                                 d2_edges_order_tab_frame,instrument));
                                 */

     /*
     check(grid_tab1=cpl_table_load(fname_d2,1,0));
     check(grid_tab2=cpl_table_load(fname_qth,1,0));


     check(cpl_table_insert(grid_tab1,grid_tab2,cpl_table_get_nrow(grid_tab1)));
     check(plist=cpl_propertylist_load(fname_d2,0));
     sprintf(pcatg,"%s_%s_%s","MFLAT_GRID_BACK", 
             xsh_instrument_mode_tostring( instrument),
             xsh_instrument_arm_tostring( instrument));
     check( xsh_pfits_set_pcatg(plist,pcatg));


     sprintf(fname,"%s.fits",pcatg);
     check(cpl_table_save( grid_tab1, plist, NULL, fname, CPL_IO_DEFAULT));


     grid_backg=xsh_frame_product(fname,pcatg,CPL_FRAME_TYPE_TABLE, 
                                  CPL_FRAME_GROUP_CALIB,CPL_FRAME_LEVEL_FINAL);

     xsh_free_propertylist(&plist);
     xsh_free_table(&grid_tab1);
     xsh_free_table(&grid_tab2);
     */

     check( xsh_add_product_table( d2_edges_order_tab_frame, frameset,
                                         parameters,RECIPE_ID,instrument,NULL));
     check( xsh_add_product_pre( d2_master_flat_frame, frameset,
                                 parameters, RECIPE_ID, instrument));
     /*
     check( xsh_add_product_image( d2_rmbackground, frameset,
                                   parameters, RECIPE_ID, instrument,NULL));
     */
     check( xsh_add_product_image( d2_frame_backg, frameset,
                                   parameters, RECIPE_ID, instrument,NULL));

     check(xsh_add_product_table(d2_grid_backg,frameset,parameters,
                                           RECIPE_ID, instrument,NULL));
     xsh_msg("name=%s tag=%s",cpl_frame_get_filename(d2_grid_backg),cpl_frame_get_tag(d2_grid_backg));
    /*
     check(xsh_add_product_table(grid_backg,frameset,parameters, 
                                       RECIPE_ID, instrument,NULL));

   */

     /* merge the two master flat */
     check( xsh_instrument_update_lamp( instrument, XSH_LAMP_UNDEFINED));
     /* merge the two master flat */
     xsh_free_frame(&edges_order_tab_frame);
     check( xsh_flat_merge_qth_d2( qth_master_flat_frame,
                                   qth_edges_order_tab_frame,
                                   d2_master_flat_frame, 
                                   d2_edges_order_tab_frame,
                                   qth_frame_backg,d2_frame_backg,
                                   &master_flat_frame, &master_bkg_frame, 
                                   &edges_order_tab_frame, instrument));

 
     check( xsh_add_product_table( edges_order_tab_frame, frameset,
                                         parameters,RECIPE_ID,instrument,NULL));

 
     xsh_msg("Detect dead-saturated pixels");
     check(xsh_image_mflat_detect_blemishes(master_flat_frame,instrument));
     /* Temporarily disactivated */
     if(xsh_instrument_get_mode(instrument) == XSH_MODE_IFU) {
         xsh_qc_ifu_flat(master_flat_frame,instrument);
     }


     check( xsh_add_product_pre( master_flat_frame, frameset,
                                 parameters, RECIPE_ID, instrument));
   
  
     check( xsh_add_product_image( master_bkg_frame, frameset, 
                                   parameters, RECIPE_ID, instrument,NULL));

     /* END case UVB arm */
  } 
  xsh_msg("xsh_mflat success!!");

  cleanup:
    xsh_end( RECIPE_ID, frameset, parameters);
    XSH_FREE( det_order);
    XSH_FREE( d2_det_order);
    XSH_FREE( backg);
    XSH_FREE( stack_par);

    xsh_free_frameset( &raws);
    xsh_free_frameset( &calib);
    xsh_free_frameset( &on);
    xsh_free_frameset( &off);
    xsh_free_frameset( &qth);
    xsh_free_frameset( &d2);
 
    xsh_free_frame( &qth_cen_order_tab_frame);
    xsh_free_frame( &qth_edges_order_tab_frame);
    xsh_free_frame( &qth_master_flat_frame);

 
    xsh_free_frame( &d2_cen_order_tab_frame);
    xsh_free_frame( &d2_edges_order_tab_frame);
    xsh_free_frame( &d2_master_flat_frame);
 

    xsh_free_frame( &edges_order_tab_frame);
    xsh_free_frame( &master_flat_frame);
    xsh_free_frame( &master_bkg_frame);
 
    xsh_free_frame( &rmbackground);
    xsh_free_frame( &d2_rmbackground);
    xsh_free_frame( &qth_rmbackground);
    xsh_free_frame( &grid_backg);
    xsh_free_frame( &d2_grid_backg);
    xsh_free_frame( &qth_grid_backg);
 
    xsh_free_frame( &frame_backg);
    xsh_free_frame( &bpmap);
    xsh_free_frame( &rmdark);
    xsh_free_frame( &d2_rmdark);
    xsh_free_frame( &qth_rmdark);
   //To free the following generates seg fault
    xsh_free_frame( &d2_frame_backg);
    xsh_free_frame( &qth_frame_backg);
 
    xsh_instrument_free( &instrument);

    xsh_free_propertylist(&plist);
 
    xsh_free_table(&grid_tab1);
    xsh_free_table(&grid_tab2);


    return;
}


/**@}*/

