/*===========================================================================
 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[CATREC_LEN];
    char satfilter[CATREC_LEN];
    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, CATREC_LEN);
    memset(satfilter, 0, CATREC_LEN);
    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... */
        free(myframe);
        free(template);
        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... */
            	free(myframe);
            	free(template);
                return (flames_midas_fail());
            }
            if ((status = SCKRDD(KAPPA, 1, 1, &actvals, &kappa, &unit, &null)) != 0) {
                /* the keyword seems undefined, protest... */
            	free(myframe);
            	free(template);
                return (flames_midas_fail());
            }
            if ((status = SCKRDD(HALFWINDOWS, 1, 1, &actvals, &halfxwindow,
                            &unit, &null)) != 0) {
                /* the keyword seems undefined, protest... */
            	free(myframe);
            	free(template);
                return (flames_midas_fail());
            }
            if ((status = SCKRDD(HALFWINDOWS, 2, 1, &actvals, &halfywindow,
                            &unit, &null)) != 0) {
                /* the keyword seems undefined, protest... */
            	free(myframe);
            	free(template);
                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 */
    	free(myframe);
    	free(template);
        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 */
        	free(myframe);
        	free(template);
            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 */
            	free(myframe);
            	free(template);
                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 */
            	free(myframe);
            	free(template);
                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 */
        	free(myframe);
        	free(template);
            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 */
        	free(myframe);
        	free(template);
            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 */
    	free(myframe);
    	free(template);
        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 */
        	free(myframe);
        	free(template);
            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 */
    	free(myframe);
    	free(template);
        return (flames_midas_fail());
    }

    /* free dynamically allocated memory */
    if ((status = freeframe(myframe)) != NOERR) {
        /* error freeing myframe internal arrays */
    	free(myframe);
    	free(template);
        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);
}
