/*===========================================================================
  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 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;
        double 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);

}
