/*===========================================================================
  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 *fdvecbuf4=0;
  frame_data *fdvecbuf5=0;
  frame_data *fdvecbuf6=0;
  frame_mask *fmvecbuf1=0;
  frame_mask *fmvecbuf2=0;
  frame_mask *fmvecbuf3=0;
  frame_mask *fmvecbuf4=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];
    fdvecbuf4 = outflats->flatdata[iframe].sigma[0];
    fdvecbuf5 = inflats->flatdata[iframe].sigma[0];
    fdvecbuf6 = outflats->flatdata[iframe].data[0];
    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 */
    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);

}
/**@}*/
