/*===========================================================================
  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  : sigma_clip.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


/* C functions include files */ 
#include <math.h>
/* MIDAS include files */
#include <flames_midas_def.h>
/* FLAMES-UVES include files */ 
#include <flames_sigma_clip.h>
#include <flames_uves.h>
/**
 @name  sigma_clip() clip outliers

  @param ScienceFrame  input science frame
  @param SingleFF      input slit FF frame
  @param Order         input order table
  @param kappa2        square of kappa to xclipo data points
  @param fibrestosolve fibre to be checked
  @param orderstosolve order to be checked
  @param numslices     number of good slices
  @param j             position
  @param nreject       output number of rejected data points
  @param mask          input mask frame
  @param newmask       output mask frame
  @param backframe     background frame   
  @param xkillsize     half X size of box to search for bad (CRH) pixels 
  @param ykillsize     half Y size of box to search for bad (CRH) pixels 

  @return success or error code

  @doc
      -find the portion of frame to be checked for kappa-sigma-clipping 
       this function will never be called if numslices is not greater than zero
      -we only want to check whether I should reject any pixels that were
       previously considered to be good; if they are marked as bad already, 
       it is pointless to check them: once a pixel has been marked as bad
       it will never be used again in this extraction 

      -recompute the variance for this pixel, using the fitted value 
      -comparing chi squared with the maximum allowed kappa value
       do reject the worst pixel 
 */

flames_err sigma_clip(flames_frame *ScienceFrame, allflats *SingleFF, 
                      orderpos *Order, double kappa2, int32_t *fibrestosolve,
                      int32_t *orderstosolve, int32_t numslices, int32_t j,
                      int32_t *nreject, frame_mask **mask,
                      frame_mask **newmask, frame_data **backframe,
                      int32_t xkillsize, int32_t ykillsize)
{
    int32_t i,m,n,imax=0;
    int32_t ilow, ihigh, ifibre, iorder, iframe, ix, iy;
    int32_t xmin, xmax, ymin, ymax;
    double chi2max=0, chi2=0;
    frame_data total=0;
    frame_data origvalue=0, ffsigma=0, pixelvalue=0, pixeldiff=0;

    singleflat *myflat=0;
    int32_t *lvecbuf1=0;
    int32_t *lvecbuf2=0;
    frame_data *fdvecbuf1=0;
    frame_data *fdvecbuf2=0;
    frame_data *fdvecbuf3=0;
    frame_data *fdvecbuf4=0;
    frame_data *fdvecbuf5=0;
    frame_data *fdvecbuf6=0;
    frame_mask *fmvecbuf1=0;
    frame_mask *fmvecbuf2=0;
    int32_t iorderifibreindex=0;
    int32_t iorderifibrejindex=0;
    int32_t ijindex=0;
    int32_t iyixoffset=0;
    int32_t c_singleff_subcols=SingleFF->subcols;
    int32_t c_singleff_maxfibres=SingleFF->maxfibres;


    /* find the portion of frame to be checked for kappa-sigma-clipping */
    /* this function will never be called if numslices is not greater than zero */
    iorder = orderstosolve[1];
    ifibre = fibrestosolve[1];
    iorderifibreindex = (iorder*c_singleff_maxfibres)+ifibre;
    iorderifibrejindex = (iorderifibreindex*c_singleff_subcols)+j;
    lvecbuf1 = SingleFF->lowfibrebounds[0][0];
    lvecbuf2 = SingleFF->highfibrebounds[0][0];
    fdvecbuf1 = ScienceFrame->spectrum[j][0];
    fdvecbuf2 = backframe[0];
    fdvecbuf3 = ScienceFrame->frame_array[0];
    fdvecbuf4 = ScienceFrame->frame_sigma[0];
    fmvecbuf1 = mask[0];
    fmvecbuf2 = newmask[0];
    ilow = lvecbuf1[iorderifibrejindex];
    ihigh = lvecbuf2[iorderifibrejindex];
    for (m=2; m<=numslices; m++) {
        iorder = orderstosolve[m];
        ifibre = fibrestosolve[m];
        iorderifibreindex = (iorder*c_singleff_maxfibres)+ifibre;
        iorderifibrejindex = (iorderifibreindex*c_singleff_subcols)+j;
        if (ilow>lvecbuf1[iorderifibrejindex])
            ilow = lvecbuf1[iorderifibrejindex];
        if (ihigh<lvecbuf2[iorderifibrejindex])
            ihigh = lvecbuf2[iorderifibrejindex];
    }
    for (i=ilow; i<=ihigh; i++) {
        /* I only want to check whether I should reject any pixels that were
      previously considered to be good; if they are marked as bad already, 
      it is pointless to check them: once a pixel has been marked as bad
      it will never be used again in this extraction */
        ijindex = (i*SingleFF->subcols)+j;
        if (fmvecbuf1[ijindex]==0) {
            total=0;
            ffsigma=0;
            for (n=1; n<=numslices; n++) {
                ifibre = fibrestosolve[n];
                iorder = orderstosolve[n];
                iorderifibreindex = (iorder*c_singleff_maxfibres)+ifibre;
                iorderifibrejindex = (iorderifibreindex*c_singleff_subcols)+j;
                iframe = SingleFF->fibre2frame[ifibre];
                myflat = SingleFF->flatdata+iframe;
                fdvecbuf5 = myflat->data[0];
                fdvecbuf6 = myflat->sigma[0];
                /* are we inside this fibre's boundaries? */
                if (i>=lvecbuf1[iorderifibrejindex] && i<=lvecbuf2[iorderifibrejindex]){
                    pixelvalue = fdvecbuf1[iorderifibreindex];
                    total += pixelvalue*fdvecbuf5[ijindex];
                    ffsigma += pixelvalue*pixelvalue*fdvecbuf6[ijindex];
                }
            }
            /* recompute the variance for this pixel, using the fitted value */
            /* actually, since what we need to use is the variance of the difference
    between the Science frame and the fitted value, include also the 
    propagated variance of the latter, although this is not formally
    part of the variance of the Science Frame. There is only one part
    of the variance which is discarded altogether for simplicity and 
    speed, i.e. the covariance between different single fibre FF frames 
    which is due to the previous multiplication by the slit FF. However, 
    a good argument in favor of dropping this term is that the single 
    fibre FF frames were at an earlier stage _divided_ by the slit FF, 
    then shifted and after that multiplied again by the slit FF; for 
    smallish shifts, these two steps mostly cancel out, giving therefore 
    no contribution to the variance either. Since tracking this 
    contribution would be an incredible mess anyway, and require keeping 
    in memory a lot of intermediate frames which can be safely discarded 
    otherwise, we choose to drop this (arguably small anyway) 
    contribution to the variance.
    Following the above line of reasoning to its very end, it could even 
    be argued that the contribution of the slit FF frame to the variance 
    should be dropped altogether, but to play safe we will keep it at 
    the moment. */
            origvalue = total+fdvecbuf2[ijindex];
            if (origvalue > 0) {
                fdvecbuf4[ijindex] = ffsigma+ScienceFrame->gain*
                                (origvalue+ScienceFrame->gain*ScienceFrame->ron);
            }
            else {
                fdvecbuf4[ijindex] = ffsigma+ScienceFrame->gain*
                                ScienceFrame->gain*ScienceFrame->ron;
            }
            /* compute the difference between the predicted and the actual pixel
    value, then square it and divide it by the pixel variance */
            pixeldiff = fdvecbuf3[ijindex]-total;
            chi2= pixeldiff*pixeldiff/fdvecbuf4[ijindex];
            if (chi2>chi2max) {
                chi2max=chi2;
                imax=i;
            }
        }
    }

    /*SCTDIS("Calculating rejected pixels",0);*/
    /* comparing chi squared with the maximum allowed kappa value */
    *nreject=0;
    if (chi2max > kappa2) {
        /* do reject the worst pixel */
        /* set the x and y windows to be rejected */
        ymin = imax-ykillsize;
        if (ymin<0) ymin=0;
        ymax = imax+ykillsize;
        if (ymax>(ScienceFrame->subrows-1)) ymax=ScienceFrame->subrows-1;
        xmin = j-xkillsize;
        if (xmin<0) xmin=0;
        xmax = j+xkillsize;
        if (xmax>(ScienceFrame->subcols-1)) xmax=ScienceFrame->subcols-1;
        for (iy=ymin; iy<=ymax; iy++) {
            iyixoffset = iy*ScienceFrame->subcols;
            if (fmvecbuf1[iyixoffset+j]==0) (*nreject)++;
            for (ix=xmin; ix<=xmax; ix++) fmvecbuf2[iyixoffset+ix]=5;
        }
    }

    /* SCTDIS("Exiting sigma_clip\n",0);*/
    return 0;

}












