/*===========================================================================
  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
  ===========================================================================*/



#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_uves.h>
#include <flames_ffslitmultiply.h>
#include <flames_newmatrix.h>


/*-------------------------------------------------------------------------*/
/**
 * @defgroup flames_ffslitmultiply
 *
 */
/*---------------------------------------------------------------------------
  Includes
  --------------------------------------------------------------------------*/
/**
   @name  flames_fastprepfibreff()  
   @short  This function prepare aframe 
   @author G. Mulas  -  ITAL_FLAMES Consortium. Ported to CPL by A. Modigliani


   @param             allslitflats *slitflats, 
   @param             orderpos *ordpos, 
   @param             allflats *inflats, 
   @param             allflats *outflats)

   @return success or failure code, 


   DRS Functions called:          
   none


   Pseudocode:                                                             


   @note
 */

flames_err 
ffslitmultiply(
                allslitflats *slitflats,
                orderpos *ordpos,
                allflats *inflats,
                allflats *outflats)
{
    int32_t **uplimbuf=0;
    int32_t **lowlimbuf=0;
    frame_mask **goodfibbuf=0;
    int32_t *fibresbuf=0;
    frame_mask **badbuffer=0;
    frame_mask *badbufi1=0;
    frame_mask *badbufi2=0;
    frame_data *datai=0;
    frame_data *sigmai=0;
    frame_data slitpixel=0;
    frame_data flatpixel=0;
    int32_t ix=0;
    int32_t iy=0;
    int32_t iorder=0;
    int32_t iframe=0;
    int32_t iframe2=0;
    int32_t lfibre=0;
    int32_t ifibre=0;
    int32_t nfibres=0;
    int32_t starty=0;
    int32_t endy=0;
    int32_t offsetiorder=0;
    int32_t offsetiorderixindex=0;
    int32_t ifibreixindex=0;
    int32_t iyixindex=0;
    int32_t *slitlowbuf=0;
    int32_t *slithighbuf=0;
    char found=0;
    int32_t orderoffset=0;
    int32_t realfirstorder=0;
    int32_t reallastorder=0;
    char output[200];

    frame_data *fdvecbuf1=0;
    frame_data *fdvecbuf2=0;
    frame_data *fdvecbuf3=0;
    frame_data *fdvecbuf5=0;


    frame_mask *fmvecbuf2=0;
    frame_mask *fmvecbuf3=0;
    frame_mask *fmvecbuf5=0;
    int32_t *lvecbuf1=0;
    int32_t *lvecbuf2=0;
    int32_t *lvecbuf3=0;
    int32_t *lvecbuf4=0;

    memset(output, 0, 200);

    orderoffset = slitflats->tab_io_oshift-ordpos->tab_io_oshift;
    realfirstorder = ordpos->firstorder;
    if (orderoffset > 0) {
        sprintf(output, "Warning: the first %d order(s) is/are not covered in \
slit flats and will be dropped in the subsequent reduction.", orderoffset);
        SCTPUT(output);
        strcpy(output, "Consider recreating the slit flats structure using the \
current order definition table");
        SCTPUT(output);
        realfirstorder += orderoffset;
    }
    else if (orderoffset < 0) {
        sprintf(output, "Warning: the first %d order(s) is/are present in the \
slit flats, but were not detected in the current order table.", orderoffset);
        SCTPUT(output);
        strcpy(output, "Consider repeating the order/fibre positioning step to \
detect those lost orders");
        SCTPUT(output);
    }

    reallastorder = slitflats->lastorder+orderoffset;
    if (reallastorder < ordpos->lastorder) {
        sprintf(output, "Warning: the last %d order(s) is/are not covered in \
slit flats and will be dropped in the subsequent reduction.", 
ordpos->lastorder-reallastorder);
        SCTPUT(output);
        strcpy(output, "Consider recreating the slit flats structure using the \
current order definition table");
        SCTPUT(output);
    }
    else if (reallastorder > ordpos->lastorder) {
        sprintf(output, "Warning: the last %d order(s) is/are present in the \
slit flats, but were not detected in the current order table.", 
reallastorder-ordpos->lastorder);
        SCTPUT(output);
        strcpy(output, "Consider repeating the order/fibre positioning step to \
detect those lost orders");
        SCTPUT(output);
        reallastorder = ordpos->lastorder;
    }

    if (inflats->nflats == 0) {
        SCTPUT("Error: no slit flats defined (nflats==0), aborting...\n");
        SCSEPI();
        return flames_midas_fail();
    }

    if (outflats == inflats) {
        badbuffer = fmmatrix(0, outflats->subrows-1, 0, outflats->subcols-1);
    }

    for (iframe=0; iframe<=inflats->nflats-1; iframe++) {

        fdvecbuf2 = inflats->flatdata[iframe].data[0];
        frame_data* fdvecbuf4 = outflats->flatdata[iframe].sigma[0];
        fdvecbuf5 = inflats->flatdata[iframe].sigma[0];
        frame_data* fdvecbuf6 = outflats->flatdata[iframe].data[0];
        frame_mask* fmvecbuf4 = inflats->flatdata[iframe].badpixel[0];

        /* do the frames in inflats one by one */

        if (outflats != inflats) {
            badbuffer = outflats->flatdata[iframe].badpixel;
        }

        nfibres = inflats->flatdata[iframe].numfibres;
        /* printf("nfibres=%d\n",nfibres); */

        /* to begin with, mark all pixels in outframe as bad, good ones will
       be marked as such when they are computed, later in the function */
        frame_mask* fmvecbuf1 = badbuffer[0];
        for (ix=0; ix<=((inflats->subrows*inflats->subcols)-1); ix++) {
            fmvecbuf1[ix] = 1;
        }
        /* is there at least one lit fibre in this frame? */
        if (nfibres>0) {

            /* now proceed with the loop over orders */
            for(iorder=realfirstorder-ordpos->firstorder;
                            iorder<=(reallastorder-ordpos->firstorder);
                            iorder++) {
                offsetiorder = iorder-orderoffset;
                /* now run over the pixels belonging to each order, provided that the
	   slit FF is defined on them, and do the multiplication */
                lowlimbuf = inflats->lowfibrebounds[iorder];
                uplimbuf = inflats->highfibrebounds[iorder];
                goodfibbuf = inflats->goodfibres[iorder];
                slitlowbuf = slitflats->lowbound[offsetiorder];
                slithighbuf = slitflats->highbound[offsetiorder];
                fibresbuf = inflats->flatdata[iframe].fibres;
                fmvecbuf2 = slitflats->goodx[offsetiorder];
                fmvecbuf3 = goodfibbuf[0];
                lvecbuf1 = lowlimbuf[0];
                lvecbuf2 = uplimbuf[0];
                for (ix=0; ix<=(inflats->subcols-1); ix++) {
                    offsetiorderixindex = (offsetiorder*inflats->subcols)+ix;
                    if (fmvecbuf2[ix] == 0) {
                        /* this slice can be multiplied by the slit FF */
                        /* now run a loop over the fibres which are in this frame: they
	       should be non-overlapping by definition and by construction */
                        /* find the interval which we must multiply by the slit FF */
                        /* first look for the first fibre which is good at this ix */
                        for (lfibre=0;
                                        (lfibre<=(nfibres-1))&&
                                                        (fmvecbuf3[ifibreixindex=
                                                                        (((ifibre=fibresbuf[lfibre])*inflats->subcols)
                                                                                        +ix)]==BADSLICE);
                                        lfibre++){
                        };
                        if (lfibre<=(nfibres-1)) {
                            starty = lvecbuf1[ifibreixindex];
                            endy = lvecbuf2[ifibreixindex];
                            /* continue the loop over remaining fibres */
                            for (lfibre++; lfibre<=(nfibres-1); lfibre++) {
                                ifibre = fibresbuf[lfibre];
                                ifibreixindex = (ifibre*inflats->subcols)+ix;
                                if (fmvecbuf3[ifibreixindex] != BADSLICE) {
                                    if ((lvecbuf1[ifibreixindex])<starty)
                                        starty = lvecbuf1[ifibreixindex];
                                    if (lvecbuf2[ifibreixindex]>endy)
                                        endy = lvecbuf2[ifibreixindex];
                                }
                            }
                            if (slitlowbuf[ix]>starty) starty=slitlowbuf[ix];
                            if (slithighbuf[ix]<endy) endy=slithighbuf[ix];
                            /* now the y loop */
                            for (iy=starty; iy<=endy; iy++) {
                                iyixindex = (iy*inflats->subcols)+ix;
                                /* only bother to flatfield if the pixel is good */
                                if(fmvecbuf4[iyixindex] == 0) {
                                    /* now we must decide which of the int32_t slit FF frames must
		     be used for the division: take the first one which 
		     includes the pixel at hand */
                                    found=0;
                                    for (iframe2=0; (iframe2<=slitflats->nflats-1)&&(found==0);
                                                    iframe2++) {
                                        lvecbuf3 = slitflats->slit[iframe2].lowbound[0];
                                        lvecbuf4 = slitflats->slit[iframe2].highbound[0];
                                        fmvecbuf5 = slitflats->slit[iframe2].badpixel[0];
                                        fdvecbuf1 = slitflats->slit[iframe2].data[0];
                                        fdvecbuf3 = slitflats->slit[iframe2].sigma[0];
                                        if((iy>=lvecbuf3[offsetiorderixindex]) &&
                                                        (iy<=lvecbuf4[offsetiorderixindex])) {
                                            /* ok, it is in this frame */
                                            /* only divide if the slit FF is good here */
                                            if (fmvecbuf5[iyixindex] == 0) {
                                                found=1;
                                                /* this pixel is good in the outframe */
                                                fmvecbuf1[iyixindex] = 0;
                                                slitpixel = fdvecbuf1[iyixindex];
                                                flatpixel = fdvecbuf2[iyixindex];
                                                /* set the sigma of the normalised pixel value */
                                                fdvecbuf4[iyixindex] = fdvecbuf5[iyixindex]*
                                                                (slitpixel*slitpixel)+fdvecbuf3[iyixindex]*
                                                                (flatpixel*flatpixel);
                                                /* set the normalised pixel value */
                                                fdvecbuf6[iyixindex] = flatpixel*slitpixel;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else {
                        /* this whole slice has no corresponding slit FF,
	       mark it bad for all fibres */
                        for (ifibre=0; ifibre<inflats->maxfibres; ifibre++)
                            goodfibbuf[ifibre][ix] = BADSLICE;
                    }
                }
            }

            /* although this is strictly unneeded, explicitly zero all frame_array
	 and frame_sigma values for bad pixels (it helps purify checks) */
            for (iy=0; iy<=(outflats->subrows-1); iy++) {
                badbufi1 = outflats->flatdata[iframe].badpixel[iy];
                badbufi2 = badbuffer[iy];
                datai = outflats->flatdata[iframe].data[iy];
                sigmai = outflats->flatdata[iframe].sigma[iy];
                for (ix=0; ix<=(outflats->subcols-1); ix++) {
                    badbufi1[ix] = badbufi2[ix];
                    if (badbufi2[ix] != 0) {
                        datai[ix] = 0;
                        sigmai[ix] = 0;
                    }
                }
            }
            if (outflats == inflats) {
                /* I used a temporary badpixel buffer, copy it to outflats */
                for (iy=0; iy<=(outflats->subrows-1); iy++) {
                    badbufi1 = outflats->flatdata[iframe].badpixel[iy];
                    badbufi2 = badbuffer[iy];
                    for (ix=0; ix<=(outflats->subcols-1); ix++) {
                        badbufi1[ix] = badbufi2[ix];
                    }
                }
            }
        }
    }

    /* free temporary memory allocations */
    if (outflats == inflats) {
        /* I used a temporary badpixel buffer, free it */
        free_fmmatrix(badbuffer, 0, outflats->subrows-1, 0, outflats->subcols-1);
    }

    return(NOERR);

}
/**@}*/
