/*===========================================================================
 Copyright (C) 2001 European Southern Observatory (ESO)

 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., 675 Massachusetss Ave, Cambridge,
 MA 02139, USA.

 Corresponding concerning ESO-MIDAS should be addressed as follows:
 Internet e-mail: midas@eso.org
 Postal address: European Southern Observatory
 Data Management Division
 Karl-Schwarzschild-Strasse 2
 D 85748 Garching bei Muenchen
 GERMANY
 ===========================================================================*/
/* Program  : prepframe.c                                                  */
/* Author   : G. Mulas  -  ITAL_FLAMES Consortium                          */
/* Date     :                                                              */
/*                                                                         */
/* Purpose  : Missing                                                      */
/*                                                                         */
/*                                                                         */
/* Input:  see interface                                                   */
/*                                                                      */
/* Output:                                                              */
/*                                                                         */
/* DRS Functions called:                                                   */
/* none                                                                    */
/*                                                                         */
/* Pseudocode:                                                             */
/* Missing                                                                 */
/*                                                                         */
/* Version  :                                                              */
/* Last modification date: 2002/08/05                                      */
/* Who     When        Why                Where                            */
/* AMo     02-08-05   Add header         header                            */
/*-------------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

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

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <flames_midas_def.h>
#include <flames_uves.h>
#include <flames_newmatrix.h>
#include <flames_initemplate.h>
#include <flames_initbadpixel.h>
#include <uves_msg.h>

typedef enum {
  MEDIAN, NONE
} filtertype;

flames_err
flames_frame_save(flames_frame *frame, char * inp_name, const char * out_name);

/*---------------------------------------------------------------------------*/
/**
 @brief Prepare a frame

 @param FRAMENAME frame input frame to be saved as fits file
 @param SATFILTER string holding saturation filter switch
 @param FILTERSWITCH string holding filter switch
 @param BADPXFRAME  bad pixel filename
 @param SATTHRES    saturation threshold value
 @param MAXITERS    maximum number of iterations
 @param KAPPA       kappa value used in kappa-sigma clip
 @param HALFWINDOWS array to hold half (X,Y) window values for median filter
 @param H_RON_L     detector RON chip REDL
 @param H_RON_U     detector RON chip REDU
 @param H_GAIN_L    detector GAIN chip REDL
 @param H_GAIN_U    detector GAIN chip REDU

 @doc -read input parameters
 -initialises frame (prepare structure with data, errors, bad pix map)
 -if filer is set to median, perform median filer on input frame
 -compute error frame to be associated to input frame

 @return   0 if everything is ok
 */

int flames_prepframe(const char *FRAMENAME, const char *SATFILTER,
    const char *FILTERSWITCH, const char *BADPXFRAME, double *SATTHRES,
    int *MAXITERS, double *KAPPA, double *HALFWINDOWS, const char *H_RON_L,
    const char *H_RON_U, const char *H_GAIN_L, const char *H_GAIN_U) {
  flames_frame *myframe = 0;
  flames_frame *template = 0;
  char keytype = 0;
  char framename[CATREC_LEN + 5];char
  badpxframe[CATREC_LEN + 5];char
  filterswitch[161];
  char satfilter[161];
  filtertype filter = NONE;
  char filter2 = FALSE;
  int32_t i = 0;
  int unit = 0;
  int null = 0;
  int maxiters = 0;
  double halfxwindow = 0;
  double halfywindow = 0;
  double kappa = 0;
  double satthres[2] = { 0, 0 };
  frame_data fdsatthres[2] = { 0, 0 };
  int status = 0;
  int actvals = 0;
  int noelem = 0;
  int bytelem = 0;

  memset(framename, 0, CATREC_LEN + 5);
  memset(badpxframe, 0, CATREC_LEN + 5);
  memset(filterswitch, 0, 161);
  memset(satfilter, 0, 161);
  uves_msg_debug("FRAMENAME: %s", FRAMENAME);
  uves_msg_debug("SATFILTER: %s", SATFILTER);
  uves_msg_debug("FILTERSWITCH: %s", FILTERSWITCH);
  uves_msg_debug("BADPXFRAME: %s", BADPXFRAME);

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

  /* enter the MIDAS environment */
  SCSPRO("prepframe");

  /* read the FRAMENAME keyword containing the name of the raw frame to 
   prepare for further processing */

  if ((status = SCKGETC(FRAMENAME, 1, CATREC_LEN, &actvals, framename)) != 0) {
    /* the keyword seems undefined, protest... */
    free(myframe);
    free(template);

    return (flames_midas_fail());
  }

  if ((status = SCKGETC(SATFILTER, 1, 160, &actvals, satfilter)) != 0) {
    /* the keyword seems undefined, protest */
    free(myframe);
    free(template);
    return flames_midas_fail();
  }

  if (actvals >= 1) {
    /* convert satfilter to upper case, to ease subsequent checks */
    for (i = 0; i <= (actvals - 1); i++)
      satfilter[i] = toupper(satfilter[i]);
    /* compare as many letters as we have with the expected values */
    if (strncmp("YES", satfilter, (size_t) actvals) == 0) {
      /* ok, satfilter requested, read the other parameters */
      if (SCKRDD(SATTHRES, 1, 2, &actvals, satthres, &unit, &null) != 0) {
        /* the keyword seems undefined, protest */
        free(myframe);
        free(template);
        return flames_midas_fail();
      }
      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 {
      SCTPUT("The only supported satfilter options are YES and NO,");
      SCTPUT("falling back to NO");
      filter2 = FALSE;
    }
  } else {
    filter2 = FALSE;
  }

  if ((status = SCKGETC(FILTERSWITCH, 1, 160, &actvals, filterswitch)) != 0) {
    /* the keyword seems undefined, protest... */
    return (flames_midas_fail());
  }

  if (actvals >= 1) {
    /* convert filterswitch to upper case, to ease the subsequent check */
    for (i = 0; i <= (actvals - 1); i++)
      filterswitch[i] = toupper(filterswitch[i]);
    /* compare as many letters as we have with the expected values */
    if (strncmp("MEDIAN", filterswitch, (size_t) actvals) == 0) {
      /* ok, median filtering requested, read the other parameters */
      if ((status = SCKRDI(MAXITERS, 1, 1, &actvals, &maxiters, &unit, &null))
          != 0) {
        /* the keyword seems undefined, protest... */
        return (flames_midas_fail());
      }
      if ((status = SCKRDD(KAPPA, 1, 1, &actvals, &kappa, &unit, &null)) != 0) {
        /* the keyword seems undefined, protest... */
        return (flames_midas_fail());
      }
      if ((status = SCKRDD(HALFWINDOWS, 1, 1, &actvals, &halfxwindow,
          &unit, &null)) != 0) {
        /* the keyword seems undefined, protest... */
        return (flames_midas_fail());
      }
      if ((status = SCKRDD(HALFWINDOWS, 2, 1, &actvals, &halfywindow,
          &unit, &null)) != 0) {
        /* the keyword seems undefined, protest... */
        return (flames_midas_fail());
      }
      if ((maxiters <= 0) || (kappa <= 0)) {
        SCTPUT(
            "MAXITERS and KAPPA must be positive, falling back to no \
filter");
        filter = NONE;
      } else if ((halfxwindow < 0) || (halfywindow < 0)) {
        SCTPUT("HALFWINDOWS must be non-negative, falling back to no \
filter");
        filter = NONE;
      } else {
        filter = MEDIAN;
      }
    } else if (strncmp("NONE", filterswitch, (size_t) actvals) == 0) {
      filter = NONE;
    } else {
      SCTPUT("The only supported filter options are MEDIAN and NONE,");
      SCTPUT("falling back to NONE");
      filter = NONE;
    }
  } else {
    filter = NONE;
  }

  /* have we got a general badpixel mask? Inquire...*/
  if ((status = SCKFND_string(BADPXFRAME, &keytype, &noelem, &bytelem)) != 0) {
    /* SCKFND failed */
    return (flames_midas_fail());
  }

  switch (keytype) {
  case 'C':
    /* it is defined and it is a character keyword, try to read in the 
     badpixel mask */

    if ((status = SCKGETC(BADPXFRAME, 1, CATREC_LEN, &actvals, badpxframe))
        != 0) {
      /* problems reading the keyword */
      return (flames_midas_fail());
    }

    if ((actvals > 0) && ((actvals != 1) || (badpxframe[0] != ' '))) {

      /* read the badpixel frame into the template */
      if ((status = initbadpixel(template, badpxframe, H_RON_L, H_RON_U,
          H_GAIN_L, H_GAIN_U)) != NOERR) {
        /* I could not set up the template from the specified
         general badpixel frame */
        return (flames_midas_fail());
      }
    } else {

      /* the keyword is empty, no general bad pixel mask */
      /* the image is its own template */
      if ((status = initemplate(template, framename, H_RON_L, H_RON_U, H_GAIN_L,
          H_GAIN_U)) != NOERR) {
        /* I could not set up the template from the first frame */
        return (flames_midas_fail());
      }
    }
    break;

  case ' ':

    /* the keyword is undefined, no general bad pixel mask */
    /* the image is its own template */
    if ((status = initemplate(template, framename, H_RON_L, H_RON_U, H_GAIN_L,
        H_GAIN_U)) != NOERR) {
      /* I could not set up the template from the first frame */
      return (flames_midas_fail());
    }
    break;

  default:

    /* this keyword cannot contain a filename */
    SCTPUT("Warning: The BADPXFRAME keyword is of the wrong type");
    /* the image is its own template */
    if ((status = initemplate(template, framename, H_RON_L, H_RON_U, H_GAIN_L,
        H_GAIN_U)) != NOERR) {
      /* I could not set up the template from the first frame */
      return (flames_midas_fail());
    }
    break;
  }

  /* Ok, I have a template, let's read in the frame */
  //flames_frame_save(myframe,framename,"papero.fits");
  if ((status = initframe(myframe, framename, template, filter2, fdsatthres))
      != NOERR) {
    /* problems in initframe */
    return (flames_midas_fail());
  }

  //flames_frame_save(myframe,framename,"pippo.fits");

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

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

  /* free dynamically allocated memory */
  if ((status = freeframe(myframe)) != NOERR) {
    /* error freeing myframe internal arrays */
    return (flames_midas_fail());
  }
  free(myframe);
  if ((status = freetemplate(template)) != NOERR) {
    /* error freeing template internal arrays */
    return (flames_midas_fail());
  }
  free(template);

  return (SCSEPI());

}

/*---------------------------------------------------------------------------*/
/**
 @brief Save a frame on a give filename

 @param frame input frame to be saved as fits file
 @param inp_name input filename
 @param out_name output filename
 @doc save a frame on a given filename

 @return   0 if everything is ok
 */

flames_err flames_frame_save(flames_frame *frame, char * inp_name,
    const char * out_name) {

  int status = 0;
  int out_id = 0;
  int inp_id = 0;
  int msk_id = 0;

  int32_t nx = 0;
  int sx = 0;
  int sy = 0;

  int i = 0;
  int j = 0;

  char base_name[CATREC_LEN + 1];char
  file_name[CATREC_LEN + 1];char
  cdummy[CATREC_LEN + 1];frame_data
  *pd = 0;
  frame_mask *pm = 0;
  memset(base_name, '\0', CATREC_LEN + 1);
  memset(file_name, '\0', CATREC_LEN + 1);
  memset(cdummy, '\0', CATREC_LEN + 1);

  /* find out whether the framename includes the .fits extension or not */
  if ((status = stripfitsext(inp_name, base_name)) != NOERR) {
    /* problems stripping extension */
    return flames_midas_error(MAREMMA);
  }

  /* open the input file */
  if ((status = SCFOPN(inp_name, FLAMESDATATYPE, 0, F_IMA_TYPE, &inp_id))
      != 0) {
    /* could not open the data file */
    return flames_midas_error(MAREMMA);
  }

  sy = frame->subrows;
  sx = frame->subcols;

  nx = (frame->subrows * frame->subcols) - 1;
  pd = frame->frame_array[0];
  pm = frame->badpixel[0];

  for (i = 0; i < sx / 2; i++) {
    for (j = 0; j < sy / 2; j++) {
      pd[j * sx + i] = 100.;
    }
  }

  for (i = 0; i < sx / 2; i++) {
    for (j = 0; j < sy / 2; j++) {
      pm[j * sx + i] = 1;
    }
  }

  for (i = sx / 2; i < sx; i++) {
    for (j = sy / 2; j < sy; j++) {
      pm[j * sx + i] = 0;
    }
  }

  /* create the sigma file name */
  sprintf(file_name, "pippo_data.fits");

  /* create the output frame on disk */
  if ((status = SCFCRE(file_name, FLAMESDATATYPE, F_O_MODE, F_IMA_TYPE,
      frame->subrows*frame->subcols, &out_id)) != 0) {
    /* could not create the data file */
    return flames_midas_error(MAREMMA);
  }

  /* copy the descriptors from the data file */
  if ((status = SCDCOP(inp_id, out_id, 1)) != 0) {
    /* I could not copy the descriptors */
    return flames_midas_error(MAREMMA);
  }

  /* write the output frame itself */
  if ((status = SCFPUT(out_id, 1, frame->subrows*frame->subcols,
      (char *) frame->frame_array[0])) != NOERR) {
    /* could not write the frame */
    return flames_midas_error(MAREMMA);
  }

  /* create the sigma file name */
  sprintf(file_name, "pippo_mask.fits");

  /* create the output frame on disk */
  if ((status = SCFCRE(file_name, FLAMESMASKTYPE, F_O_MODE, F_IMA_TYPE,
      frame->subrows*frame->subcols, &msk_id)) != 0) {
    /* could not create the data file */
    return flames_midas_error(MAREMMA);
  }

  /* copy the descriptors from the data file */
  if ((status = SCDCOP(inp_id, msk_id, 1)) != 0) {
    /* I could not copy the descriptors */
    return flames_midas_error(MAREMMA);
  }

  /* write the output frame itself */
  if ((status = SCFPUT(msk_id, 1, frame->subrows*frame->subcols,
      (char *) frame->badpixel[0])) != NOERR) {
    /* could not write the frame */
    return flames_midas_error(MAREMMA);
  }

  /* close the output file */
  if ((status = SCFCLO(out_id)) != 0) {
    /* error closing file */
    return flames_midas_error(MAREMMA);
  }

  /* close the output file */
  if ((status = SCFCLO(msk_id)) != 0) {
    /* error closing file */
    return flames_midas_error(MAREMMA);
  }

  /* close the input file */
  if ((status = SCFCLO(inp_id)) != 0) {
    /* error closing file */
    return flames_midas_error(MAREMMA);
  }

  /* the end */
  return (NOERR);
}
