/*===========================================================================
  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  : prepstand.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>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* MIDAS include files */
#include <flames_midas_def.h>
#include <flames_prepstand.h>
#include <flames_allocspectrum.h>
#include <flames_getordpos.h>
/* FLAMES-UVES include files */ 
#include <flames_uves.h>

flames_err prepstand(flames_frame *ScienceFrame, allflats *Shifted_FF, 
                     orderpos *Order, double ***pfibrecentre,
                     frame_data **normcover, int32_t orderoffset,
                     int32_t realfirstorder, int32_t reallastorder,
                     frame_mask **mask, double phalfwinsize)
{
    char output[CATREC_LEN+1];
    int32_t nm=0, mj=0, k=0, l=0, m=0, n=0, goodslices=0;
    int32_t iframe=0, ylow=0, yup=0;
    double usedfluxave=0, dx=0, dy=0, pdy=0, pylow=0, pyup=0;
    double dreduce=0;
    frame_data fraction=0;
    frame_data ffcoverage=0;

    double *dvecbuf1=0;
    frame_data *fdvecbuf1=0;

    frame_mask *fmvecbuf1=0;
    frame_mask *fmvecbuf2=0;
    frame_mask *fmvecbuf3=0;
    frame_mask *fmvecbuf4=0;
    int32_t *lvecbuf1=0;
    int32_t *lvecbuf2=0;
    int32_t maxklindex=0;
    int32_t klindex=0;
    int32_t firstmj=0;
    int32_t lastmj=0;
    int32_t mjnoffset=0;
    int32_t mjnlindex=0;
    int32_t iorder=0;
    int32_t iorder2=0;
    int32_t iorder2loffset=0;
    int32_t iordernindex=0;
    int32_t iordernloffset=0;
    int32_t iordernlindex=0;
    int32_t iorder2lindex=0;


    maxklindex = (ScienceFrame->subrows*ScienceFrame->subcols)-1;
    fdvecbuf1 = normcover[0];
    fmvecbuf1 = mask[0];
    fmvecbuf2 = Shifted_FF->goodfibres[0][0];
    fmvecbuf3 = ScienceFrame->badpixel[0];
    lvecbuf1 = Shifted_FF->lowfibrebounds[0][0];
    lvecbuf2 = Shifted_FF->highfibrebounds[0][0];
    dvecbuf1 = pfibrecentre[0][0];
    /* find the lowest and highest lit fibres in this frame; we actually want
     fibres to be lit also in the fibre ff frames, or we will be unable to
     extract them anyway, but we will check for that later. */
    SCTPUT("Searching for lit fibres");

    nm=0;
    ScienceFrame->num_lit_fibres=0;
    for (nm=0;
                    nm<ScienceFrame->maxfibres &&
                    (!ScienceFrame->fibremask[nm] || !Shifted_FF->fibremask[nm]); nm++);
    if (nm<ScienceFrame->maxfibres) {
        ScienceFrame->min_lit_fibre = nm;
        ScienceFrame->max_lit_fibre = nm;
        ScienceFrame->ind_lit_fibres[0] = nm;
        ScienceFrame->num_lit_fibres = 1;
        for (nm++; nm<ScienceFrame->maxfibres; nm++) {
            if (ScienceFrame->fibremask[nm] && Shifted_FF->fibremask[nm]) {
                ScienceFrame->max_lit_fibre=nm;
                ScienceFrame->ind_lit_fibres[ScienceFrame->num_lit_fibres]=nm;
                ScienceFrame->num_lit_fibres++;
            }
        }
    }
    else {
        /* no fibres lit both in the Science Frame and in the FF frames,
       bail out */
        SCTPUT("No extractable fibres in this frame");
        return flames_midas_fail();
    }

    sprintf(output,"min = %d ; max = %d ; num = %d",
            ScienceFrame->min_lit_fibre,
            ScienceFrame->max_lit_fibre,
            ScienceFrame->num_lit_fibres);
    SCTPUT(output);
    memset(output, '\0', 70);

    /* the following section initialises the overall mask to be used for
     the extraction and, in the same loop, it checks for adequate coverage of 
     each fibre slice, to avoid ill-posed problems altogether */
    /* clean up the mask first */
    /* I want to use only the pixels which are somewhat covered by some
     at least partially good fibre slice; hence initially mark all pixels
     to be bad in the overall mask, then clean up the ones for which the 
     following conditions hold:
     1) that pixel is good both in the fibre FF frame(s) and in the Science
        frame
     2) that pixel belongs at least to one extractible slice with good 
        enough coverage
     */
    /*
  sprintf(output,"Initializing the mask");
  SCTPUT(output);
     */
    /* the "out of boundaries" value in the mask is 3, it means that the
     pixel is not included in any slice to be extracted */
    for (klindex=0; klindex<=maxklindex; klindex++) {
        fmvecbuf1[klindex] = 3;
    }

    /* locate order centres */
    firstmj = realfirstorder-Order->firstorder;
    lastmj = reallastorder-Order->firstorder;
    for (mj=firstmj; mj<=lastmj; mj++) {
        double dorder = (double) (mj+Order->firstorder);
        mjnoffset = mj*ScienceFrame->maxfibres;
        for (l=0; l<=(ScienceFrame->subcols-1); l++) {
            dx = ScienceFrame->substartx+((double) l)*ScienceFrame->substepx;
            if (get_ordpos(Order, dorder, dx, &dy) != NOERR) {
                SCTPUT("Error finding order centres");
                return flames_midas_fail();
            }
            pdy = (dy-ScienceFrame->substarty)/ScienceFrame->substepy;
            for (m=0; m<=(ScienceFrame->num_lit_fibres-1); m++) {
                n=ScienceFrame->ind_lit_fibres[m];
                if(ScienceFrame->fibremask[n]==TRUE &&
                                Shifted_FF->fibremask[n]==TRUE) {
                    mjnlindex = ((mjnoffset+n)*ScienceFrame->subcols)+l;
                    iframe = Shifted_FF->fibre2frame[n];
                    dvecbuf1[mjnlindex] = pdy+ScienceFrame->yshift[iframe]
                                                                   +Order->fibrepos[n];
                }
            }
        }
    }

    /* run the first loop over fibres */
    for (m=0; m<=(ScienceFrame->num_lit_fibres-1); m++) {
        /* run the loop over orders only if appropriate */
        n = ScienceFrame->ind_lit_fibres[m];
        iframe = Shifted_FF->fibre2frame[n];
        frame_data* fdvecbuf2 = Shifted_FF->flatdata[iframe].data[0];
        fmvecbuf4 = Shifted_FF->flatdata[iframe].badpixel[0];
        if(ScienceFrame->fibremask[n]==TRUE && Shifted_FF->fibremask[n]==TRUE) {
            for (mj=realfirstorder; mj<=reallastorder; mj++) {
                iorder = mj-Order->firstorder;
                iorder2 = mj-Order->firstorder-orderoffset;
                iorder2loffset = iorder2*ScienceFrame->subcols;
                iordernindex = (iorder*ScienceFrame->maxfibres)+n;
                iordernloffset = iordernindex*ScienceFrame->subcols;
                /* now run the loop over x */
                for (l=0; l<=(ScienceFrame->subcols-1); l++) {
                    iordernlindex = iordernloffset+l;
                    iorder2lindex = iorder2loffset+l;
                    /* is this slice any good at all? */
                    if (fmvecbuf2[iordernlindex]!=BADSLICE) {
                        /* yes, therefore run the loop over y */
                        ffcoverage=0;
                        /* reduce the y loop to the intervals to be used in the
           integration */
                        pylow = dvecbuf1[iordernlindex]-phalfwinsize;
                        pyup = dvecbuf1[iordernlindex]+phalfwinsize;
                        ylow = (int32_t) floor(pylow+.5);
                        if (ylow<0) ylow = 0;
                        if (ylow<lvecbuf1[iordernlindex]) ylow = lvecbuf1[iordernlindex];
                        yup = (int32_t) floor(pyup+.5);
                        if (yup>(ScienceFrame->subrows-1)) yup = ScienceFrame->subrows-1;
                        if (yup>lvecbuf2[iordernlindex]) yup = lvecbuf2[iordernlindex];
                        for (k=ylow; k<=yup; k++) {
                            klindex = (k*ScienceFrame->subcols)+l;
                            /* is this pixel good everywhere? */
                            if (fmvecbuf3[klindex]==0 && fmvecbuf4[klindex]==0) {
                                /* take care of fractional pixels */
                                fraction = 1;
                                if ((dreduce=pylow+.5-(double)k)>0)
                                    fraction -= (frame_data) dreduce;
                                if ((dreduce=.5-pyup+(double)k)>0)
                                    fraction -= (frame_data) dreduce;
                                /* add its contribution to the fibre coverage factor */
                                ffcoverage += fraction*fdvecbuf2[klindex];
                            }
                        }
                        /* divide by the normalisation fraction */
                        ffcoverage /= fdvecbuf1[iorder2lindex];
                        /* does the fraction of collected light for this fibre exceed the
           threshold making it worth extracting? */
                        if(ffcoverage<Shifted_FF->minfibrefrac) {
                            /* no, forget it and mark this fact where it belongs */
                            fmvecbuf2[iordernlindex] = BADSLICE;
                        }
                        else {
                            /* yes, mark good pixels good in the overall mask */
                            usedfluxave += (double) ffcoverage;
                            goodslices++;
                            for (k=ylow; k<=yup; k++) {
                                klindex = (k*ScienceFrame->subcols)+l;
                                /* is this pixel good everywhere? */
                                if (fmvecbuf3[klindex]==0) {
                                    if (fmvecbuf4[klindex]==0) {
                                        /* mark it good */
                                        fmvecbuf1[klindex] = 0;
                                    }
                                    else {
                                        /* mark it bad from the fibre FF */
                                        fmvecbuf1[klindex] = 2;
                                    }
                                }
                                else {
                                    /* mark it bad from the ScienceFrame */
                                    fmvecbuf1[klindex] = 1;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    /* run the second loop over fibres, to make sure to exclude pixels which
     are not good in all the slices to be extracted */
    for (m=0; m<=(ScienceFrame->num_lit_fibres-1); m++) {
        /* run the loop over orders only if appropriate */
        n=ScienceFrame->ind_lit_fibres[m];
        iframe = Shifted_FF->fibre2frame[n];
        fmvecbuf4 = Shifted_FF->flatdata[iframe].badpixel[0];
        if(ScienceFrame->fibremask[n]==TRUE && Shifted_FF->fibremask[n]==TRUE) {
            for (mj=realfirstorder; mj<=reallastorder; mj++) {
                iorder = mj-Order->firstorder;
                iordernindex = (iorder*ScienceFrame->maxfibres)+n;
                iordernloffset = iordernindex*ScienceFrame->subcols;
                /* now run the loop over x */
                for (l=0; l<=(ScienceFrame->subcols-1); l++) {
                    iordernlindex = iordernloffset+l;
                    /* is this slice any good at all? */
                    if (fmvecbuf2[iordernlindex]!=BADSLICE) {
                        /* yes, therefore run the loop over y */
                        /* reduce the y loop to the intervals to be used in the
           integration */
                        pylow = dvecbuf1[iordernlindex]-phalfwinsize;
                        pyup = dvecbuf1[iordernlindex]+phalfwinsize;
                        ylow = (int32_t) floor(pylow+.5);
                        if (ylow<0) ylow=0;
                        if (ylow<lvecbuf1[iordernlindex]) ylow = lvecbuf1[iordernlindex];
                        yup = (int32_t) floor(pyup+.5);
                        if (yup>(ScienceFrame->subrows-1)) yup=ScienceFrame->subrows-1;
                        if (yup>lvecbuf2[iordernlindex]) yup = lvecbuf2[iordernlindex];
                        for (k=ylow; k<=yup; k++) {
                            klindex = (k*ScienceFrame->subcols)+l;
                            /* is this pixel bad anywhere? */
                            if (fmvecbuf3[klindex]!=0) {
                                /* mark it bad from the ScienceFrame */
                                fmvecbuf1[klindex] = 1;
                            }
                            else if (fmvecbuf4[klindex]!=0) {
                                /* mark this pixel as bad in the composite mask */
                                fmvecbuf1[klindex]=2;
                            }
                        }
                    }
                }
            }
        }
    }

    alloc_spectrum(ScienceFrame);

    if (goodslices>0) usedfluxave /= (double) goodslices;

    sprintf(output,"firstorder (from ScienceFrame) is %d",
            ScienceFrame->firstorder);
    SCTPUT(output);
    memset(output, '\0', 70);
    sprintf(output,"lastorder (from ScienceFrame) is %d",
            ScienceFrame->lastorder);
    SCTPUT(output);
    memset(output, '\0', 70);
    sprintf(output,
            "the average flux fraction in the integration window is %3f\n",
            usedfluxave);
    SCTPUT(output);
    memset(output, '\0', 70);

    return NOERR;

}
