/*===========================================================================
  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  : shiftall.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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <flames_midas_def.h>
#include <flames_def_drs_par.h>
#include <flames_uves.h>
#include <flames_newmatrix.h>
#include <flames_shiftcommon.h>
#include <flames_getordpos.h>
#include <flames_getordslope.h>
#include <flames_shift_all_FF.h>
#include <flames_dointerpolate.h>
#include <flames_shiftall.h>

flames_err shift_all_FF(allflats *allflatsin, orderpos *ordpos, double yshift, 
            allflats *allflatsout)
{

  flames_err status=0;
  int32_t i=0;
  int32_t ix=0;
  int32_t iy=0;
  int32_t iorder=0;
  int32_t iframe=0;
  int32_t ifibre=0;
  int32_t lfibre=0;
  shiftstruct *shiftdata=0;
  fitstruct fitdata;
  double x; 
  double order; 
  double ordercentre;
  double orderslope;
  frame_data pixelvalue=0;
  frame_data pixelsigma=0;
  char output[200];

  shiftstruct *myshiftdata=0;
  singleflat *myflatin=0;
  singleflat *myflatout=0;
  frame_data *fdvecbuf1=0;
  frame_data *fdvecbuf2=0;
  frame_mask *fmvecbuf1=0;
  frame_mask *fmvecbuf2=0;
  int32_t *lvecbuf1=0;
  int32_t *lvecbuf2=0;
  int32_t iorderifibreoffset=0;
  int32_t iorderifibreindex=0;
  int32_t iorderifibreixindex=0;
  int32_t iyixindex=0;


  int actvals=0;
  char drs_verbosity[10];
  int mid_stat=0;
  memset(drs_verbosity, 0, 10);
  if ((mid_stat=SCKGETC(DRS_VERBOSITY, 1, 3, &actvals, drs_verbosity))
      != 0) {
    /* the keyword seems undefined, protest... */
    return(MAREMMA);
  }

  fmvecbuf1 = allflatsin->goodfibres[0][0];
  lvecbuf1 = allflatsout->lowfibrebounds[0][0];
  lvecbuf2 = allflatsout->highfibrebounds[0][0];

  fitdata.availpixels=0;
  fitdata.offset = calloc((size_t) 8, sizeof(double));
  fitdata.value = calloc((size_t) 8, sizeof(double));
  fitdata.sigma = calloc((size_t) 8, sizeof(double));



  for (i=0; i<=7; i++) {
    fitdata.offset[i]=0;
    fitdata.value[i]=0;
    fitdata.sigma[i]=0;
  }

  /* allocate the local shiftdata array of structures */
  shiftdata = 
    (shiftstruct *) calloc((size_t)(allflatsin->subcols), sizeof(shiftstruct));
  for (ix=0; ix<=(allflatsin->subcols-1); ix++) {
    myshiftdata = shiftdata+ix;
    myshiftdata->ixoffsets = calloc((size_t) 8, sizeof(int32_t));
    myshiftdata->yfracoffsets = calloc((size_t) 8, sizeof(double));
    myshiftdata->yintoffsets = calloc((size_t) 8, sizeof(int32_t));
    myshiftdata->normfactor = calloc((size_t) 8, sizeof(double));
    myshiftdata->normsigma = calloc((size_t) 8, sizeof(double));
    myshiftdata->goodoverlap = calloc((size_t) 8, sizeof(double));
  }

  /* This function will have to loop over orders, x's, fibres and y's. */
  /* loop over order... */
  for (iorder=0; iorder<=(ordpos->lastorder-ordpos->firstorder); iorder++) {
    iorderifibreoffset = iorder*allflatsin->maxfibres;
    order = (double) (iorder+ordpos->firstorder);
    /* loop over x... */
    for (ix=0; ix<=(allflatsin->subcols-1); ix++) {
      myshiftdata = shiftdata+ix;
      /* convert the ix pixel coordinate to the x world coordinate */
      x = allflatsin->substartx+(allflatsin->substepx)*((double) ix);
      /* find the unshifted central position and slope of this order at 
     this x */
      /* bail out if the function call return an error status */
      if ((status = get_ordpos(ordpos, order, x, &ordercentre))!=NOERR) {
    return(status);
      }
      myshiftdata->ordercentre = ordercentre;
      if ((status = get_ordslope(ordpos, order, x, &orderslope))!=NOERR) {
    return(status);
      }
      myshiftdata->orderslope = orderslope;
      /* WARNING: remember that both ordercentre and orderslope are in 
     world coordinates so far! */
    }
    /* split the loop, calcshifts needs shiftdata to have been filled
       already for all ixes */
    for (ix=0; ix<=(allflatsin->subcols-1); ix++) {
      myshiftdata = shiftdata+ix;
      /* loop over FF frames (even, odd, whatever...) */
      for (iframe=0; iframe<=allflatsin->nflats-1;iframe++) {
    /* compute the pixel coordinates of the pixels to be used for 
       interpolation */
    myflatin = allflatsin->flatdata+iframe;
    myflatout = allflatsout->flatdata+iframe;
    fdvecbuf1 = myflatout->data[0];
    fdvecbuf2 = myflatout->sigma[0];
    fmvecbuf2 = myflatout->badpixel[0];
    if ((status=calcshifts(allflatsin, shiftdata, iframe, ix, yshift)) 
        != NOERR) {
      return(status);
    }
    /* loop over lit fibres in this frame */
    for (lfibre=0; lfibre<=(myflatin->numfibres-1); lfibre++) {
      /* put the actual fibre index in a variable, to avoid using very 
         int32_t strucure and array names and make the code easier to read 
         (well, sort of...) */
      ifibre = myflatin->fibres[lfibre];
      iorderifibreindex = iorderifibreoffset+ifibre;
      iorderifibreixindex = (iorderifibreindex*allflatsin->subcols)+ix;
      /* I might check whether this fibre is actually lit in this frame, 
         but this is redundant, isn't it? */
      /* do check that this order/fibre/x is good overall, otherwise it 
         is useless to try to shift it */
      if (fmvecbuf1[iorderifibreixindex]==GOODSLICE ||
          fmvecbuf1[iorderifibreixindex]==DEMISLICE) {
        /* this fibre/order/x is good (to some extent) in the unshifted 
           frames, go ahead and find this fibre centre and boundaries */
        if ((status=locatefibre(allflatsin, allflatsout, ordpos, 
                    shiftdata, iorder, ifibre, ix, yshift)) 
        != NOERR) {
          return(status);
        }
        /* loop over pixels belonging to the shifted fibre, only if 
           ishiftedyup>=ishiftedydown */
        for (iy=lvecbuf1[iorderifibreixindex]; 
         iy<=lvecbuf2[iorderifibreixindex]; 
         iy++) {
          iyixindex = (iy*allflatsin->subcols)+ix;
          /* build the list of pixels to use for interpolation */
          if ((status=selectavail(allflatsin, shiftdata, &fitdata, 
                      iorder, iframe, ix, iy)) != NOERR) {
        return(status);
          }
          /* do the interpolation (if possible) */
          if ((status=dointerpolate(allflatsout, &fitdata, iorder, 
                    iframe, ifibre, ix, iy)) !=NOERR) {
        return(status);
          }
          /* was the interpolation successful? */
          if (fmvecbuf2[iyixindex]==0) {
        /* negative values and/or anomalously large values 
           may wreak havoc in iterative 
           interpolations, i.e. other pixels interpolated 
           using this value, therefore clip them */
        if ((pixelvalue = fdvecbuf1[iyixindex])<0) {
          if ((pixelvalue*pixelvalue)>4*fdvecbuf2[iyixindex]) {
            pixelsigma = (frame_data) pow(fdvecbuf2[iyixindex],.5);

            if ( strcmp(drs_verbosity,"LOW") == 0 ){
            } else {
                   SCTPUT("Warning: interpolated large negative value:");
                   sprintf(output, "pixel=%g and sigma=%g at x=%d, \
y=%d", pixelvalue, pixelsigma, ix+1, iy+1);
                   SCTPUT(output);
                   SCTPUT("marking it bad");
            }

            fdvecbuf2[iyixindex] = pixelvalue*pixelvalue;
            fdvecbuf1[iyixindex] = 0;
            fmvecbuf2[iyixindex] = 1;
          }
          else {
            /* we are within error, just clip it silently */
            fdvecbuf1[iyixindex] = 0;
          }
        }
        else if (pixelvalue>1) {
          /* no sensible way to clip this, just kill it */
          pixelsigma = (frame_data) pow(fdvecbuf2[iyixindex],.5);

                  if ( strcmp(drs_verbosity,"LOW") == 0 ){
          } else {

             SCTPUT("Warning: interpolated too large normalised \
value:");
             sprintf(output, "pixel=%g and sigma=%g at x=%d, \
y=%d", pixelvalue, pixelsigma, ix+1, iy+1);
             SCTPUT(output);
             SCTPUT("marking it bad");
             }

          fdvecbuf2[iyixindex] = pixelvalue*pixelvalue;
          fdvecbuf1[iyixindex] = 0;
          fmvecbuf2[iyixindex] = 1;
        }
          }
        }
      }
    }
      }
    }
  }

  /* free here all dynamically allocated temporary arrays before returning */
  for (ix=0; ix<=(allflatsin->subcols-1); ix++) {
    myshiftdata = shiftdata+ix;
    free(myshiftdata->ixoffsets);
    free(myshiftdata->yfracoffsets);
    free(myshiftdata->yintoffsets);
    free(myshiftdata->normfactor);
    free(myshiftdata->normsigma);
    free(myshiftdata->goodoverlap);
  }
  free(shiftdata);

  free(fitdata.offset);
  free(fitdata.value);
  free(fitdata.sigma);

  return(NOERR);

}
