/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004,2005 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */
/*----------------------------------------------------------------------------
   
   File name    :   stack_ini_by_cpl.c
   Author       :   Andrea Modigliani
   Created on   :   May 23, 2004
   Description  :   prepare stacked frames cpl input handling for SPIFFI

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

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

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

#include <string.h>
#include "sinfo_stack_ini_by_cpl.h"
#include "sinfo_error.h"
#include "sinfo_pro_types.h"
#include "sinfo_raw_types.h"
#include "sinfo_ref_types.h"
#include "sinfo_functions.h"

#include "sinfo_file_handling.h"

/*---------------------------------------------------------------------------
                    Functions private to this module
 ---------------------------------------------------------------------------*/
static void sinfo_stack_free_alloc(stack_config_n * cfg);  


static void     
parse_section_frames(stack_config_n *, 
                     cpl_frameset* sof, 
                     cpl_frameset** raw,
                     int* status,
                     fake* fk);

static void     
parse_section_cleanmean(stack_config_n *, cpl_parameterlist* cpl_cfg);
static void     
parse_section_flatfield(stack_config_n *, cpl_parameterlist* cpl_cfg);
static void     
parse_section_badpixel(stack_config_n *, cpl_parameterlist* cpl_cfg);
static void     
parse_section_interleaving(stack_config_n *);
static void     
parse_section_gaussconvolution(stack_config_n *, cpl_parameterlist* cpl_cfg);
static void     
parse_section_shiftframes(stack_config_n *);
static void     
parse_section_warpfix(stack_config_n *,  cpl_parameterlist* cpl_cfg);
static void     
parse_section_qclog(stack_config_n *,  cpl_parameterlist* cpl_cfg);
/**@{*/
/**
 * @addtogroup sinfo_rec_jitter Initialize stack structure configuration
 *
 * TBD
 */

/* generateStack_ini_file */
/*-------------------------------------------------------------------------*/
/**
  @name     parse_stack_ini_file
  @memo     Parse a ini_name.ini file and create a blackboard.
  @param    ini_name    Name of the ASCII file to parse.
  @return   1 newly allocated stack_config blackboard structure.
  @doc

  The requested ini file is parsed and a blackboard object is created, then
  updated accordingly. Returns NULL in case of error.
 */
/*--------------------------------------------------------------------------*/

stack_config_n * sinfo_parse_cpl_input_stack(cpl_parameterlist* cpl_cfg, 
                                       cpl_frameset* sof, 
                                       cpl_frameset** raw,
                                       fake* fk)
{
    stack_config_n   *       cfg =sinfo_stack_cfg_create_n();
    int status=0;
   


    /*
     * Perform sanity checks, fill up the structure with what was
     * found in the ini file
     */

    parse_section_cleanmean        (cfg, cpl_cfg);
    parse_section_flatfield        (cfg, cpl_cfg);
    parse_section_badpixel         (cfg, cpl_cfg); 
    parse_section_interleaving     (cfg); 
    parse_section_gaussconvolution (cfg, cpl_cfg); 
    parse_section_shiftframes      (cfg); 
    parse_section_warpfix          (cfg, cpl_cfg);
    parse_section_qclog            (cfg, cpl_cfg);
    parse_section_frames           (cfg, sof, raw, &status, fk);
          if (status > 0) {
                sinfo_msg_error("parsing cpl input");
                sinfo_stack_cfg_destroy_n(cfg);
                cfg = NULL ;
                return NULL ;
        }
    return cfg ;
}
/**
@name parse_section_frames
@memo Parse input frames
@param stack_config_n input stack configuration structer
@param sof input set of frames
@param raw output raw stack frames list
@param status result operation
@param fk fake frame structure
*/
static void     
parse_section_frames(stack_config_n * cfg, 
                     cpl_frameset* sof, cpl_frameset** raw_set,int* status, 
                     fake* fk)
{

   int                     i;
   const char            *       name;
   char                        file[FILE_NAME_SZ];
   int                     nobj, noff, ndark ;
   int                     nditherobj, nditheroff ;
   int                     found_sky ;
   int                     found_ref ;
   int                     found_dither ;
   int                     found_dark   ;
    int nraw=0;
   //int nsof=0;

   cpl_frame* frame   = NULL;

    char spat_res[FILE_NAME_SZ];
   char lamp_status[FILE_NAME_SZ];
   char band[FILE_NAME_SZ];
   int ins_set=0;
   char* tag=NULL;
   char* do_class=NULL;
  
   //nsof    = cpl_frameset_get_size(sof);


   do_class=fk->pro_class;
   /* sinfo_msg("do_class=%s",do_class); */ 
   if(strcmp(do_class,"DEFAULT") == 0) {
      sinfo_extract_raw_stack_frames(sof,raw_set);
   } else if (strcmp(do_class,PRO_FIBRE_NS_STACKED_DIST) == 0) {
      sinfo_is_fibres_on_off(sof,*raw_set);
   } else if (strcmp(do_class,PRO_FIBRE_NS_STACKED) == 0) {
      sinfo_contains_frames_kind(sof,*raw_set,(char*)PRO_FIBRE_NS_STACKED);
   } else if (strcmp(do_class,RAW_STACKED_SLITPOS) == 0) {
      sinfo_extract_raw_stack_frames(sof,raw_set);
   } else {
      sinfo_extract_raw_frames_type(sof,raw_set,do_class);
   }
   nraw    = cpl_frameset_get_size(*raw_set);
   if (nraw < 1) {
      sinfo_msg_error("Too few raw frames present in frameset!");
      (*status)++;
      return;
   }

 
   /* Allocate structures to go into the blackboard */
   cfg->framelist     = cpl_malloc(nraw * sizeof(char*));
   cfg->frametype     = cpl_malloc(nraw * sizeof(int));
   cfg->frameposition = cpl_malloc(nraw * sizeof(int));

   for (i=0;i<nraw;i++) {
     cfg->framelist[i]=NULL;
     cfg->frametype[i]=-1;
     cfg->frameposition[i]=-1;
   }

   found_sky     = 0 ;
   found_ref     = 0 ;
   found_dither  = 0 ;
   found_dark    = 0 ;
   nobj          = 0 ;
   noff          = 0 ;
   ndark         = 0 ;
   nditherobj    = 0 ;
   nditheroff    = 0 ;



   /* Browse through the charmatrix to get names and file types */
   /*   for (i=0 ; i<nraw ; i++) { */
   for (i=0 ; i<nraw ; i++) {
      frame = cpl_frameset_get_frame(*raw_set,i);
      name=cpl_frame_get_filename(frame);
       if(sinfo_file_exists((char*)name)==1) {
    /* to go on the file must exist */
    if(cpl_frame_get_tag(frame) != NULL) {
      /* If the frame has a tag we process it. Else it is an object */ 
      tag= (char*) cpl_frame_get_tag(frame);
          if((sinfo_frame_is_on(frame)  == 0) || 
             (sinfo_frame_is_sky(frame)  == 1)) 
      {
             cfg->framelist[i]=cpl_strdup(cpl_frame_get_filename(frame));
             cfg->frametype[i] = FRAME_OFF ;
             found_sky = 1;
             if (sinfo_frame_is_dither(frame)) 
           {
                cfg->frameposition[i] = FRAME_POS2 ;
                nditheroff++ ;
           }
             else 
           {
                cfg->frameposition[i] = FRAME_POS1 ;
                noff++ ;
           }
      }
          else if(strstr(tag,RAW_REF)  != NULL) 
      {
          cfg->framelist[i]=cpl_strdup(cpl_frame_get_filename(frame));
          cfg->frametype[i] = FRAME_REF ;
              found_ref=1;
          if (sinfo_frame_is_dither(frame))
        {
          cfg->frameposition[i] = FRAME_POS2 ;
        }
              else 
        {
          cfg->frameposition[i] = FRAME_POS1 ;
        }
      }
          else if(sinfo_is_dark(tag)) 
        {
          cfg->framelist[i]=cpl_strdup(cpl_frame_get_filename(frame));
          cfg->frametype[i] = FRAME_DRK ;
          cfg->frameposition[i] = FRAME_POS1 ;
              found_dark=1;
              ndark++;
          sinfo_msg("Frame is sinfo_dark on\n");
        }
          else 
        {

          cfg->framelist[i]=cpl_strdup(cpl_frame_get_filename(frame));
          cfg->frametype[i] = FRAME_ON ;
              found_ref=1;
          if (sinfo_frame_is_dither(frame))
        {
          cfg->frameposition[i] = FRAME_POS2 ;
                  found_dither=1;
                  nditherobj++;
        }
              else 
        {
          cfg->frameposition[i] = FRAME_POS1 ;
                  nobj++;
        }
        }
    }
        else 
      {
            /* No type means an object */
        cfg->frametype[i] = FRAME_ON ;
            /* No type means position 1 */
        cfg->frameposition[i] = FRAME_POS1 ;
            
        nobj ++ ;
      }
      }
      
      /* Store file name into framelist */
    }
   
   sinfo_msg("Noff= %d Nobj= %d Nditheroff= %d Nditherobj= %d",
           noff,nobj,nditheroff,nditherobj);
   


   /* Copy relevant information into the blackboard */
   cfg->nframes         = nraw ;
   cfg->nobj            = nobj ;
   cfg->noff            = noff ;
   cfg->ndark           = ndark ;
   cfg->nditherobj      = nditherobj ;
   cfg->nditheroff      = nditheroff ;
   cfg->contains_sky    = found_sky ;
   cfg->contains_ref    = found_ref ;
   cfg->contains_dither = found_dither ;
   cfg->contains_dark   = found_dark ;


   frame = cpl_frameset_get_frame(*raw_set,0);

   ck0_nomsg(sinfo_get_spatial_res(frame,spat_res));



   if(sinfo_frame_is_on(frame) == 1) {
     strcpy(lamp_status,"on");
   } else {
     strcpy(lamp_status,"off");
   }
   switch(sinfo_frame_is_on(frame)) 
     {
   case 0: 
      strcpy(lamp_status,"on");
      break;
    case 1: 
      strcpy(lamp_status,"off");
      break;
    case -1:
      strcpy(lamp_status,"undefined");
      break;
    default: 
      strcpy(lamp_status,"undefined");
      break;


     }
   sinfo_get_band(frame,band);


   sinfo_msg("Spatial resolution: %s lamp status: %s band: %s",
                     spat_res,              lamp_status,    band);

   sinfo_get_ins_set(band,&ins_set);
   frame = cpl_frameset_get_frame(*raw_set,0);
   tag=(char*)cpl_frame_get_tag(frame);


   /* Update flatInd setting if we had changed the mflat switch */
    /* take care of NS test special case */
    if(fk->frm_switch==1) {
     /* 
        In this case we force certain values indipendently from the 
        setting occurring during CPL parameters parsing 
      */

    cfg->maskInd = fk->mask_index;
    cfg->indind = fk->ind_index;
    cfg->flatInd = fk->flat_index;
        cfg -> loReject = fk->low_rej;
        cfg -> hiReject = fk->hig_rej;
        cfg -> warpfixInd =  fk->wfix_index;
    /*
        sinfo_msg("Fake frame: reset parameter values: ");
        sinfo_msg("maskInd:  %d", cfg->maskInd);
        sinfo_msg("indind:   %d", cfg->indind);
        sinfo_msg("flatInd:  %d", cfg->flatInd);
        sinfo_msg("loReject: %f", cfg->loReject);
        sinfo_msg("hiReject: %f", cfg->hiReject);
    */
   }
  
  


 
   if(cfg -> flatInd) { 
      if(NULL != cpl_frameset_find(sof,PRO_MASTER_FLAT_LAMP)) {
         frame = cpl_frameset_find(sof,PRO_MASTER_FLAT_LAMP);
         strcpy(file,cpl_frame_get_filename(frame));
         strcpy(cfg -> flatfield1, file);
      } else if(NULL != cpl_frameset_find(sof,PRO_MASTER_FLAT_LAMP1)) {
         frame = cpl_frameset_find(sof,PRO_MASTER_FLAT_LAMP1);
         strcpy(file,cpl_frame_get_filename(frame));
         strcpy(cfg -> flatfield1, file);
      } else {
         sinfo_msg_error("Frame %s not found!", PRO_MASTER_FLAT_LAMP);
         sinfo_msg_error("Frame %s not found!", PRO_MASTER_FLAT_LAMP1);
         sinfo_stack_free_alloc(cfg);  
         (*status)++;
         return;
      }

      if(found_dither) {
         if(NULL != cpl_frameset_find(sof,PRO_MASTER_FLAT_LAMP2)) {
            frame = cpl_frameset_find(sof,PRO_MASTER_FLAT_LAMP2);
            strcpy(file,cpl_frame_get_filename(frame));
            strcpy(cfg -> flatfield2, file);
         } else {
            sinfo_msg("Frame %s not found!", PRO_MASTER_FLAT_LAMP2);
         }
      }
   }

   /* bad pixel section */
   if(cfg->maskInd != 0) {
     if(strstr(do_class,"FIBRE_NS") != NULL) {
        if(NULL != cpl_frameset_find(sof,PRO_BP_MAP_DI)) {
           frame = cpl_frameset_find(sof,PRO_BP_MAP_DI);
           strcpy(file,cpl_frame_get_filename(frame));
           strcpy(cfg -> mask, file);
        } else {
           sinfo_msg_error("Frame %s not found!", PRO_BP_MAP_DI);
           sinfo_stack_free_alloc(cfg);  
           (*status)++;
           return;
        }

     } else {

        if(NULL != cpl_frameset_find(sof,PRO_MASTER_BP_MAP)) {
           frame = cpl_frameset_find(sof,PRO_MASTER_BP_MAP);
           strcpy(file,cpl_frame_get_filename(frame));
           strcpy(cfg -> mask, file);
        } else {
           sinfo_msg_error("Frame %s not found!", PRO_MASTER_BP_MAP);
           sinfo_stack_free_alloc(cfg);  
           (*status)++;
           return;
        }

     }

     if (strcmp(do_class,RAW_STACKED_SLITPOS) == 0) {
       cfg -> indind = 1;
     }

      if(cfg -> indind == 0) { 
          if(NULL != cpl_frameset_find(sof,PRO_SLIT_POS)) {
            frame = cpl_frameset_find(sof,PRO_SLIT_POS);
            strcpy(file,cpl_frame_get_filename(frame));
            strcpy(cfg -> slitposList, file);
            sinfo_msg("Using %s to interpolate bad pixels",
                             PRO_SLIT_POS);
            cpl_error_reset();
          } else if(NULL != cpl_frameset_find(sof,PRO_SLIT_POS_GUESS)) {
            frame = cpl_frameset_find(sof,PRO_SLIT_POS_GUESS);
            strcpy(file,cpl_frame_get_filename(frame));
            strcpy(cfg -> slitposList, file);
            sinfo_msg("Using %s to interpolated bad pixels",
                             PRO_SLIT_POS_GUESS);
            cpl_error_reset();
          } else {
            sinfo_msg_error("Frame %s nor %s found!", 
                              PRO_SLIT_POS,PRO_SLIT_POS_GUESS);
            sinfo_stack_free_alloc(cfg);  
            (*status)++;
            return;
          }

     
      }
      if(cfg -> maskInd == 2) {
         if(NULL != cpl_frameset_find(sof,PRO_INDEX_LIST)) {
            frame = cpl_frameset_find(sof,PRO_INDEX_LIST);
            strcpy(file,cpl_frame_get_filename(frame));
            strcpy(cfg ->indexlist, file);
         } else {
            sinfo_msg_error("Frame %s not found!", PRO_INDEX_LIST);
            sinfo_stack_free_alloc(cfg);  
            (*status)++;
            return;
         }

      }
   }

   cfg -> warpfixInd =  fk->wfix_index;
   if(cfg->warpfixInd != 0) {
        if(NULL != cpl_frameset_find(sof,PRO_DISTORTION)) {
            frame = cpl_frameset_find(sof,PRO_DISTORTION);
            strcpy(file,cpl_frame_get_filename(frame));
            strcpy(cfg -> polyFile, file);
         } else {
            sinfo_msg_error("Frame %s not found!", PRO_DISTORTION);
            sinfo_stack_free_alloc(cfg);  
            (*status)++;
            return;
         }
   }

   if(NULL != cpl_frameset_find(sof,PRO_MASTER_DARK)) {
     frame = cpl_frameset_find(sof,PRO_MASTER_DARK);
     strcpy(file,cpl_frame_get_filename(frame));
     strcpy(cfg -> mdark, file);
     cfg->mdark_ind=1;

   } else {
     sinfo_msg("Frame %s not found", PRO_MASTER_DARK);
     cfg->mdark_ind=0;
   }

  cleanup:
   return ;
}

/**
@brief reads parameters for clean mean
@name parse_section_cleanmean
@param stack_config_n configuration structure
@param cpl_config parameter list

*/
static void     
parse_section_cleanmean(stack_config_n * cfg,cpl_parameterlist* cpl_cfg)
{

   cpl_parameter* p;
   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.low_rejection");
   cfg -> loReject = cpl_parameter_get_double(p);

   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.high_rejection");
   cfg -> hiReject = cpl_parameter_get_double(p);

        return ;
}
/**
@brief reads parameters for flatfield
@name parse_section_flatfield
@param stack_config_n configuration structure
@param cpl_config parameter list
*/
static void     
parse_section_flatfield(stack_config_n * cfg,cpl_parameterlist* cpl_cfg)
{

   cpl_parameter* p;
   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.flat_index");
   cfg -> flatInd = cpl_parameter_get_bool(p);

}
/**
@brief reads parameters for bad pixel search
@name parse_section_badpixel
@param stack_config_n configuration structure
@param cpl_config parameter list
*/
static void     
parse_section_badpixel(stack_config_n * cfg,cpl_parameterlist* cpl_cfg)
{
  cpl_parameter* p;


   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.ind_index");
   cfg -> indind = cpl_parameter_get_bool(p);

   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.mask_index");
   cfg -> maskInd = cpl_parameter_get_int(p);

   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.mask_rad");
   cfg -> maxRad = cpl_parameter_get_int(p);
   /*
   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.sigma_factor");
   */
   cfg -> sigmaFactor = 3.;

}
/**
@brief reads parameters for frame interleaving
@name parse_section_interleaving
@param stack_config_n configuration structure
*/
static void     
parse_section_interleaving(stack_config_n * cfg)
{
   cfg -> interInd = 0;
   cfg -> noRows = 400;

}
/**
@brief reads parameters for Gaussian convolution
@name parse_section_gaussconvolution
@param stack_config_n configuration structure
@param cpl_config parameter list
*/
static void     
parse_section_gaussconvolution(stack_config_n * cfg,cpl_parameterlist* cpl_cfg)
{

   cpl_parameter* p;
   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.gauss_index");
   cfg -> gaussInd = cpl_parameter_get_bool(p);

   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.kernel_half_width");
   cfg -> hw = cpl_parameter_get_int(p);

}
/**
@brief reads parameters for frame shift
@name parse_section_shiftframes
@param stack_config_n configuration structure
*/
static void     
parse_section_shiftframes(stack_config_n * cfg)
{
  /*
   cpl_parameter* p;
   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.shift_frame_index");
   cfg -> sfInd  = cpl_parameter_get_bool(p);

   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.shift_frame_type");
   cfg -> sfType = cpl_parameter_get_int(p);

   p = cpl_parameterlist_find(cpl_cfg,"sinfoni.stacked.shift_frame_order");
   cfg -> sfOrder = cpl_parameter_get_int(p);
  */

   cfg -> sfInd  = 0;
   cfg -> sfType = 1;
   cfg -> sfOrder = 2;

}
/**
@brief reads parameters for warping
@name parse_section_warpfix
@param stack_config_n configuration structure
@param cpl_config parameter list
*/
static void     
parse_section_warpfix(stack_config_n * cfg,cpl_parameterlist * cpl_cfg)
{
       
   cpl_parameter* p;

   p = cpl_parameterlist_find(cpl_cfg, "sinfoni.stacked.warpfix_ind");
   cfg -> warpfixInd =  cpl_parameter_get_bool(p);


   p = cpl_parameterlist_find(cpl_cfg, "sinfoni.stacked.warpfix_kernel");
   strcpy(cfg -> kernel, cpl_parameter_get_string(p));

   return ;
}

/**
@brief reads parameters for qclog
@name parse_section_cleanmean
@param stack_config_n configuration structure
@param cpl_config parameter list
*/
static void     
parse_section_qclog(stack_config_n * cfg,cpl_parameterlist * cpl_cfg)
{
       
   cpl_parameter* p;

   p = cpl_parameterlist_find(cpl_cfg, "sinfoni.stacked.qc_thresh_min");
   cfg -> qc_thresh_min =  cpl_parameter_get_int(p);

   p = cpl_parameterlist_find(cpl_cfg, "sinfoni.stacked.qc_thresh_max");
   cfg -> qc_thresh_max =  cpl_parameter_get_int(p);

   return ;
}
/**
@brief free memory
@name sinfo_stack_free
@param stack_config_n configuration structure
*/
void
sinfo_stack_free(stack_config_n ** cfg)
{  
  if(*cfg != NULL) {
    sinfo_stack_free_alloc(*cfg);
    sinfo_stack_cfg_destroy_n(*cfg);
    *cfg = NULL;
  }
  return;

}

/**
@brief free memory
@name sinfo_stack_free_alloc
@param stack_config_n configuration structure
*/
static void
sinfo_stack_free_alloc(stack_config_n * cfg)
{  
  int i=0;
  for (i=0; i< cfg->nframes; i++) {
    if(cfg->framelist[i]  != NULL) {
        cpl_free(cfg->framelist[i]);
             cfg->framelist[i]=NULL;
    }
  }
  if(cfg->frametype != NULL) {
    cpl_free(cfg->frametype); 
    cfg->frametype=NULL;
  }
  if(cfg->framelist != NULL)   {
    cpl_free(cfg->framelist); 
    cfg->framelist=NULL;
  }
  if(cfg->frameposition != NULL) {
    cpl_free(cfg->frameposition); 
    cfg->frameposition=NULL;
  }
}
/**@}*/
