/*
 * This file is part of the KMOS Pipeline
 * Copyright (C) 2002,2003 European Southern Observatory
 *
 * This program 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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef KMOS_MOLECFIT_H
#define KMOS_MOLECFIT_H

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

#include <string.h>
#include <math.h>

#include <cpl.h>
#include <molecfit.h>

#include "kmo_constants.h"
#include "kmo_utils.h"
#include "kmo_dfs.h"
#include "kmos_pfits.h"


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

/* KMOS_MOLECFIT RECIPES */
#define KMOS_MOLECFIT_MODEL           "kmos_molecfit_model"
#define KMOS_MOLECFIT_CALCTRANS       "kmos_molecfit_calctrans"
#define KMOS_MOLECFIT_CORRECT         "kmos_molecfit_correct"

/* FRAME TAGS */

/* RAW: DATA */
//#define KMOS_TAG_STAR_SPEC             "STAR_SPEC"          /* CPL_VECTOR    (1D, ROWS=X):                1-HEADER +     48 -EXTENSIONS (DATA+NOISE) */
//#define KMOS_TAG_EXTRACT_SPEC          "EXTRACT_SPEC"       /* CPL_VECTOR    (1D, ROWS=X):                1-HEADER + (24/48)-EXTENSIONS (DATA/NOISE) */
//#define KMOS_TAG_SCIENCE               "SCIENCE"            /* CPL_IMAGELIST (3D, N_IMG=X, ROWS=COLS=14): 1-HEADER + (24/48)-EXTENSIONS (DATA/NOISE) */
//#define KMOS_TAG_SCI_RECONSTRUCTED     "SCI_RECONSTRUCTED"  /* CPL_IMAGELIST (3D, N_IMG=X, ROWS=COLS=14): 1-HEADER + (24/48)-EXTENSIONS (DATA/NOISE) */

/* CALIB: KERNEL */
//#define KMOS_TAG_KERNEL                "KERNEL_LIBRARY"     /* CPL_IMAGE     (2D, ROWS=Y,  COLS=15):      1-HEADER +  (24*6)-EXTENSIONS (DATA)       */

/* CALIB: kmos_molecfit_model */
//#define KMOS_TAG_ATMOS_PARM            "ATMOS_PARM"         /* CPL_TABLE     (2D, ROWS=50, COLS=4 ):      1-HEADER +     48 -EXTENSIONS (DATA+NOISE) */
//#define KMOS_TAG_BEST_FIT_PARM         "BEST_FIT_PARM"      /* CPL_TABLE     (2D, ROWS=36, COLS=3 ):      1-HEADER +     48 -EXTENSIONS (DATA+NOISE) */
//#define KMOS_TAG_BEST_FIT_MODEL        "BEST_FIT_MODEL"     /* CPL_TABLE     (2D, ROWS=X,  COLS=11):      1-HEADER +     48 -EXTENSIONS (DATA+NOISE) */

/* CALIB: kmos_molecfit_calctrans */
//#define KMOS_TAG_TELLURIC_DATA         "TELLURIC_DATA"      /* CPL_TABLE     (2D, ROWS=X,  COLS=9):       1-HEADER +     48 -EXTENSIONS (DATA+NOISE) */
//#define KMOS_TAG_TELLURIC              "TELLURIC"           /* CPL_VECTOR    (1D, ROWS=X):                1-HEADER +     48 -EXTENSIONS (DATA+NOISE) */

/* CALIB: RESPONSE */
//#define KMOS_TAG_RESPONSE              "RESPONSE"           /* CPL_VECTOR    (1D, ROWS=X):                1-HEADER +     48 -EXTENSIONS (DATA+NOISE) */

/* CALIB: kmos_molecfit_correct */
//#define KMOS_TAG_SPEC_SINGLE           "SINGLE_SPECTRA"     /* CPL_VECTOR    (1D, ROWS=X):                1-HEADER +     48 -EXTENSIONS (DATA+NOISE) */
//#define KMOS_TAG_CUBE_SINGLE           "SINGLE_CUBES"       /* CPL_IMAGELIST (3D, N_IMG=X, ROWS=COLS=14): 1-HEADER +     48 -EXTENSIONS (DATA+NOISE) */


/* Column names of the input spectrum table */
#define MF_SPECTRUM_COLUMN_WLEN        "lambda"
#define MF_SPECTRUM_COLUMN_FLUX        "flux"
#define MF_SPECTRUM_COLUMN_FLUX_ERROR  "fluxerr"
#define MF_SPECTRUM_COLUMN_MASK        "mask"

/* Column names of the input wavelength table */
#define MF_WAVELENGTH_COLUMN_LOWER     "llim"
#define MF_WAVELENGTH_COLUMN_UPPER     "ulim"

/* Column names of the input molecules table */
#define MF_MOLECULES_COLUMN_LIST       "list_molec"
#define MF_MOLECULES_COLUMN_FIT        "fit_molec"
#define MF_MOLECULES_COLUMN_RELCOL     "relcol"


/* Name of parameter in the output fits file */
#define KMOS_MF_PARAM_RECIPE           "ESO DRS MOLECFIT PARAM "

/* KMOS counters */
#define N_IFUS                         24
#define N_KERNEL_LIBRARY_EXTENSIONS    N_IFUS * 6

/* Wavelength detection limits in KMOS */
#define KMOS_WAVELENGTH_START          0.8
#define KMOS_WAVELENGTH_END            2.5

/* Initial  FWHM of the Gaussian in pixels have Grating dependency */
#define RES_GAUSS_IZ                   1.84
#define RES_GAUSS_YJ                   1.82
#define RES_GAUSS_H                    1.76
#define RES_GAUSS_K                    1.73
#define RES_GAUSS_HK                   2.06

#define MOLECFIT_MAX_POLY_FIT          8


/*----------------------------------------------------------------------------*/
/**
 *                 Typedefs: Structs and enum types
 */
/*----------------------------------------------------------------------------*/

typedef struct {
  const char         *name;                  /* Kernel extension name                                                          */
  int                num;                    /* IFU number                                                                     */
  int                ext;                    /* IFU.ANGLE num  extension in the kernel library                                 */
  cpl_propertylist   *header_ext;            /* Header property list in IFU extension of kernel library for the specific angle */
  double             header_CRVAL1;          /* RA at ref. pixel [deg]                                                         */
  double             header_CD2_2;           /* Pixel resolution in x [deg]                                                    */
  cpl_matrix         *data;                  /* Convolution kernel values specifics in the grating                             */
} kmos_kernel;

typedef struct {
  cpl_boolean        process;                /* Flag: Process this IFU or not                                                  */
  int                map;                    /* Mapping of ifu (in kmos_molecfit_calctrans relation between IFU_X and IFU_Y    */
  char               *name;                  /* IFU extension name   in the raw file                                           */
  int                num;                    /* IFU number           in the raw file                                           */
  int                ext;                    /* IFU extension number in the raw file                                           */
  cpl_propertylist   *header_ext_data;       /* Header property list in IFU data  extension of raw file                        */
  cpl_propertylist   *header_ext_noise;      /* Header property list in IFU noise extension of raw file                        */
  cpl_propertylist   *header_1D_data;        /* Header property list convert from 3D DataCube to 1D spectrum, if necessary     */
  double             header_CRVAL1;          /* RA at ref. pixel [deg]                                                         */
  double             header_CDELT1;          /* Pixel resolution in x [deg]                                                    */
  cpl_table          *data;                  /* Wavelength data in the input file. Contain only one column with wavelengths    */
  double             median;                 /* Median of data spectrum                                                        */
  kmos_kernel        kernel;                 /* Associate kernel to one IFU                                                    */
} kmos_spectrum;

typedef enum {
  GRATING_IZ = 0,                            /* KMOS IZ grating                                                                */
  GRATING_YJ,                                /* KMOS YJ grating                                                                */
  GRATING_H,                                 /* KMOS H  grating                                                                */
  GRATING_K,                                 /* KMOS K  grating                                                                */
  GRATING_HK                                 /* KMOS HK grating                                                                */
} kmos_grating_type;

typedef struct {
  const char         *name;                  /* Specific unique grating to these spectrums data: 'IZ', 'YJ', 'H', 'K', 'HK'    */
  kmos_grating_type  type;                   /* Type that match with grating_name                                              */
  const char         *wave_range;            /* wave_ranges{ini1,end1,...,iniN,endN} defined by the user to fit in the grating */
  const char         *list_molec;            /* list_molec{H2O,CO,CO2,CH4,O2} str    defined by the user to fit in the grating */
  const char         *fit_molec;             /* fit_molec {1,  0, 1,  1,  0 } flag   defined by the user to fit in the grating */
  const char         *relcol;                /* relDensity{1.,1.,1.06,1.,1. } double defined by the user to fit in the grating */
  cpl_table          *incl_wave_ranges;      /* Wavelength ranges     to fit in the grating. Contains 2 columns(llim, ulim)    */
  cpl_table          *molecules;             /* Molecules to fit. Contains 3 columns (list_molec, fit_molec, relcol)           */
} kmos_grating;

typedef struct {
  cpl_boolean        fit;                    /* Flag: Polynomial fit                                                           */
  cpl_size           n;                      /* Polynomial degree                                                              */
  double             const_val;              /* Initial value of the constant term of the polynomial fit                       */
} mf_fit;

typedef struct {
  cpl_propertylist   *parms;                 /* Input recipe parameters to be save in the output res_out fits file             */
  kmos_spectrum      ifus[N_IFUS];           /* Data ifu spectrums. Contains name, property list, data and scale of wavelength */
  cpl_propertylist   *header_spectrums;      /* Primary property list header in the raw STAR_SPEC file                         */
  float              rot_angle;              /* Rotator relative to nasmyth [deg]                                              */
  kmos_grating       grating;                /* Grating data by user or default values                                         */
  cpl_boolean        use_input_kernel;       /* The parameters below are ignored if use_input_kernel                           */
  cpl_propertylist   *header_kernels;        /* Primary property list header in the kernel library file                        */
  cpl_boolean        kernmode;               /* Voigt profile approx. or independent Gauss and Lorentz                         */
  double             kernfac;                /* Size of kernel in FWHM                                                         */
  cpl_boolean        varkern;                /* Does the kernel size increase linearly with wavelength?                        */
  mf_fit             fit_continuum;          /* Continuum fit                                                                  */
  mf_fit             fit_wavelenght;         /* Wavelength solution refinement/adjustment                                      */
} kmos_molecfit_parameter;


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

/* Insert parameter in the input parameterlist */
cpl_error_code kmos_molecfit_fill_parameter(
    const char* recipe, cpl_parameterlist *self, const char* param,
    cpl_boolean range, const void *rMin, const void *rMax,
    cpl_type type, const void *value, const char* description,
    cpl_boolean mf_conf);

/* Set the grating type that check with grating string */
cpl_error_code kmos_molecfit_grating_type(
    const char* name, kmos_grating_type *type);

/* Fill type grating with parameters by default */
cpl_error_code kmos_molecfit_grating_by_default(
    kmos_grating *grating, kmos_grating_type type);

/* Generate the wave_range and molecules cpl_table's */
cpl_error_code kmos_molecfit_generate_wave_molec_tables(
    kmos_molecfit_parameter *conf);

/* Fill the molecfit generic configuration file */
cpl_parameterlist * kmos_molecfit_conf_generic(
    const char* recipe, kmos_molecfit_parameter *conf);

/* Get frame with input data */
cpl_frame * kmos_molecfit_get_frame_spectrums(
    cpl_frameset *frameset);

/* Load input spectrum in the RAW file for a kmos_std_star execution */
cpl_error_code kmos_molecfit_load_spectrums(
    cpl_frameset *frameset, kmos_molecfit_parameter *conf, const char *recipe_name);

/* Extract a 1D spectrum from a extension datacube data in one FITS file */
cpl_vector * kmos_molecfit_extract_spec_from_datacube(
    const char *filename, int ext, cpl_propertylist *header);

/* Load and setup the convolution kernels provided by the user for one grating */
cpl_error_code kmos_molecfit_load_kernels(
    const cpl_frame *frm, kmos_molecfit_parameter *conf);

/* Load a convolution kernel for one specific extensions in the kernel library */
cpl_error_code kmos_molecfit_load_kernel(
    const char *filename, kmos_kernel *kernel);

/* Get a resampled kernel with the number of rows indicate in n_rows_resampled */
cpl_error_code kmos_molecfit_resample_kernel(
    kmos_spectrum *ifu, double spec_lambda_ini, double spec_lambda_delta, cpl_size spec_n_lambdas);

/* Read library kernel from FITS file and save to ASCII file */
cpl_error_code kmos_molecfit_kernelLibrary_FITS_2_ASCII(
    const char *refFitsFile, const char *target_dir);

/* Read library kernel from ASCII file and save to FITS file */
cpl_error_code kmos_molecfit_kernelLibrary_ASCII_2_FITS(
    const char *refFitsFile, const char *target_dir, const char *target_fits);

/* Save to disk a new *.fits output file */
cpl_error_code kmos_molecfit_save(
    cpl_frameset            *frameset,
    const cpl_parameterlist *parlist,
    const char              *recipe,
    cpl_propertylist        *list,
    const char              *tag,
    const char              *gratingname,
    const char              *filename);

/* Save to disk a cpl_table output for one IFU molecfit execution */
cpl_error_code kmos_molecfit_save_mf_results(
    cpl_propertylist *header_data,
    cpl_propertylist *header_noise,
    const char       *tag,
    const char       *gratingname,
    const char       *filename,
    cpl_table        *table,
    cpl_vector       *vec);

/* Clean configuration generic parameter */
void kmos_molecfit_clean(
    kmos_molecfit_parameter *conf);

/* Clean spectrum parameter */
void kmos_molecfit_clean_spectrum(
    kmos_spectrum *ifu);

/* Clean kernel parameter */
void kmos_molecfit_clean_kernel(
    kmos_kernel *kernel);

/* Clean grating parameter */
void kmos_molecfit_clean_graing(
    kmos_grating *grating);

/* Nullify configuration generic parameter */
void kmos_molecfit_nullify(
    kmos_molecfit_parameter *conf);

/* Get the directory of execution */
char * kmos_molecfit_cwd_get(void);

/* Set the temporary directory variable with path */
cpl_error_code kmos_molecfit_tmpdir_set(const char *path, char **oldpath);


#endif /* KMOS_MOLECFIT_H */
