/*===========================================================================
  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
  ===========================================================================*/
/*---------------------------------------------------------------------------*/
/**
 * @defgroup flames_creamask
 *    This command creates a basic bad pixel mask to be used for subsequent
 *    processing of FLAMES/UVES data, based on tables of known CCD
 *    blemishes.
 */
/*---------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
  Includes
  ---------------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <flames_creamask.h>
#include <flames_midas_def.h>
#include <flames_newmatrix.h>
#include <uves_error.h>

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
/**@{*/
/*-----------------------------------------------------------------------------
  Functions prototypes
  ---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
  Implementation
  ---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/**
   @name flames_creamask
   @brief    Generates a bad pixel map
   @param IN_A
   @param IN_B
   @param IN_C
   @param OUT_A
   @param IN_B_XS
   @param IN_B_XE
   @param IN_B_YS
   @param IN_B_YE
   @param IN_C_X_PIX
   @param IN_C_Y_PIX


   DRS Functions called:                                                   
   none                                                                    
                                                                         
   Pseudocode:                                                             
   Build the mask, initialising it to zero                                 
   Now read all the pixels listed in the hot pixels table,                 
   and set them to 1                                                       
   now read the limits of the zones were traps are present                 
   and mark the bad region                                                 
   Finally to write the bad pixel mask to disk, create the image file      

   @return   bad pixel image filename

*/
/*-------------------------------------------------------------------------*/




int 
flames_creamask(
		const char *IN_A,
		const char *IN_B,
		const char *IN_C,
		const char *OUT_A,
		const char *IN_B_XS,
		const char *IN_B_XE,
		const char *IN_B_YS,
		const char *IN_B_YE,
		const char *IN_C_X_PIX,
		const char *IN_C_Y_PIX)
{
  char inimage[CATREC_LEN+1];
  char outimage[CATREC_LEN+1];
  char hptable[CATREC_LEN+1];
  char traptable[CATREC_LEN+1];
  char ident[73];
  char cunit[3][16];
  float cuts[4]={0,0,0,0};
  int inid=0;
  char output[200];
  char type=0;
  int noelem=0;
  int bytelem=0;
  int actvals=0;
  int naxis=0;
  int unit=0;
  int null=0;
  double start[2]={0,0};
  double step[2]={0,0};
  int npix[2]={0,0};
  int hptid=0;
  char hpxname[CATREC_LEN+1];
  int hpxcol=0;
  char hpyname[CATREC_LEN+1];
  int hpycol=0;
  int ncols=0;
  int hprows=0;
  int traptid=0;
  char trapstartxname[CATREC_LEN+1];
  int trapstartxcol=0;
  char trapstartyname[CATREC_LEN+1];
  int trapstartycol=0;
  char trapendxname[CATREC_LEN+1];
  int trapendxcol=0;
  char trapendyname[CATREC_LEN+1];
  int trapendycol=0;
  int traprows=0;
  frame_mask **mask=0;
  frame_mask *maski=0;
  int i=0;
  int j=0;
  int row=0;
  flames_err status=0;
  double worldx=0;
  int pixelx=0;
  double worldy=0;
  int pixely=0;
  int pixelstartx=0;
  int pixelstarty=0;
  int pixelendx=0;
  int pixelendy=0;
  int ibuf=0;
  int outid=0;

  SCSPRO("creamask"); /* Get into MIDAS Env. */

  /* first and foremost, I ought to read the appropriate keywords to be used
     as input and output files */
  if (SCKGETC(IN_A, 1, CATREC_LEN, &actvals, inimage)!=0) {
    SCTPUT("Error reading IN_A descriptor!");
    return flames_midas_fail();
  }
  if (SCKGETC(IN_B, 1, CATREC_LEN, &actvals, traptable)!=0) {
    SCTPUT("Error reading IN_B descriptor!");
    return flames_midas_fail();
  }
  if (SCKGETC(IN_C, 1, CATREC_LEN, &actvals, hptable)!=0) {
    SCTPUT("Error reading IN_C descriptor!");
    return flames_midas_fail();
  }
  if (SCKGETC(OUT_A, 1, CATREC_LEN, &actvals, outimage)!=0) {
    SCTPUT("Error reading OUT_A descriptor!");
    return flames_midas_fail();
  }
  if (SCKGETC(IN_B_XS, 1, CATREC_LEN, &actvals, trapstartxname)!=0) {
    SCTPUT("Error reading IN_B_XS descriptor!");
    return flames_midas_fail();
  }
  if (SCKGETC(IN_B_XE, 1, CATREC_LEN, &actvals, trapendxname)!=0) {
    SCTPUT("Error reading IN_B_XS descriptor!");
    return flames_midas_fail();
  }
  if (SCKGETC(IN_B_YS, 1, CATREC_LEN, &actvals, trapstartyname)!=0) {
    SCTPUT("Error reading IN_B_XS descriptor!");
    return flames_midas_fail();
  }
  if (SCKGETC(IN_B_YE, 1, CATREC_LEN, &actvals, trapendyname)!=0) {
    SCTPUT("Error reading IN_B_XS descriptor!");
    return flames_midas_fail();
  }
  if (SCKGETC(IN_C_X_PIX, 1, CATREC_LEN, &actvals, hpxname)!=0) {
    SCTPUT("Error reading IN_C_X_PIX descriptor!");
    return flames_midas_fail();
  }
  if (SCKGETC(IN_C_Y_PIX, 1, CATREC_LEN, &actvals, hpyname)!=0) {
    SCTPUT("Error reading IN_C_Y_PIX descriptor!");
    return flames_midas_fail();
  }


  /* first, open the input image, which is supposed to be used as a reference
     for the size of the output image */

  if (SCFINF(inimage, 99, &inid) != 0) {
    /* no such image, complain! */
    sprintf(output, "Error: Image %s does not exist!", inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  SCFOPN(inimage, D_OLD_FORMAT, 0, F_IMA_TYPE, &inid);
  SCDFND(inid, "NAXIS", &type, &noelem, &bytelem);
  if (type==' ') {
    /* this image does not have an NAXIS descriptor! */
    sprintf(output, "Error: image %s does not have an NAXIS descriptor!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  else if (type!='I') {
    sprintf(output, "Error: descriptor NAXIS in image %s is not an integer!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  SCDRDI(inid, "NAXIS", 1, 1, &actvals, &naxis, &unit, &null);
  if (null!=0) {
    sprintf(output, "Error: descriptor NAXIS in image %s has null value!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (naxis!=2) {
    sprintf(output, "Error: image %s is not 2-dimensional!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }

  SCDFND(inid, "START", &type, &noelem, &bytelem);
  if (type==' ') {
    /* this image does not have a START descriptor! */
    sprintf(output, "Error: image %s does not have a START descriptor!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  else if (type!='D') {
    sprintf(output, "Error: descriptor START in image %s is not a double!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  else if (noelem!=naxis) {
    sprintf(output, "Error: only %d values present for START in image %s!", 
	    noelem, inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  SCDRDD(inid, "START", 1, naxis, &actvals, start, &unit, &null);
  if (null!=0) {
    sprintf(output, "Error: null values in descriptor START in image %s!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (actvals!=naxis) {
    sprintf(output, "Error: only %d values read for START in image %s!", 
	    actvals, inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }

  SCDFND(inid, "STEP", &type, &noelem, &bytelem);
  if (type==' ') {
    /* this image does not have a STEP descriptor! */
    sprintf(output, "Error: image %s does not have a STEP descriptor!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  else if (type!='D') {
    sprintf(output, "Error: descriptor STEP in image %s is not a double!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  else if (noelem!=naxis) {
    sprintf(output, "Error: only %d values present for STEP in image %s!", 
	    noelem, inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  SCDRDD(inid, "STEP", 1, naxis, &actvals, step, &unit, &null);
  if (null!=0) {
    sprintf(output, "Error: null values in descriptor STEP in image %s!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (actvals!=naxis) {
    sprintf(output, "Error: only %d values read for STEP in image %s!", 
	    actvals, inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }

  SCDFND(inid, "NPIX", &type, &noelem, &bytelem);
  if (type==' ') {
    /* this image does not have an NPIX descriptor! */
    sprintf(output, "Error: image %s does not have an NPIX descriptor!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  else if (type!='I') {
    sprintf(output, "Error: descriptor NPIX in image %s is not a double!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  else if (noelem!=naxis) {
    sprintf(output, "Error: only %d values present for NPIX in image %s!", 
	    noelem, inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  SCDRDI(inid, "NPIX", 1, naxis, &actvals, npix, &unit, &null);
  if (null!=0) {
    sprintf(output, "Error: null values in descriptor NPIX in image %s!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (actvals!=naxis) {
    sprintf(output, "Error: only %d values read for NPIX in image %s!", 
	    actvals, inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (npix[0]<=0 || npix[1]<=0) {
    sprintf(output, "Error: bad values read for NPIX in image %s!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  /* we may close inimage, we do not need it any more */
  if (SCFCLO(inid)!=0) {
    sprintf(output, "Error closing image %s!", 
	    inimage);
    SCTPUT(output);
    return flames_midas_fail();
  }


  if (TCTOPN(hptable, F_I_MODE, &hptid)!=0) {
    sprintf(output, "Error opening table %s", hptable);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (TCCSER(hptid, hpxname, &hpxcol)!=0 || hpxcol==-1) {
    sprintf(output, "Error: nonexistent %s column in %s table", 
	    hpxname, hptable);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (TCCSER(hptid, hpyname, &hpycol)!=0 || hpycol==-1) {
    sprintf(output, "Error: nonexistent %s column in %s table", 
	    hpyname, hptable);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (TCIGET(hptid, &ncols, &hprows)!=0) {
    sprintf(output, "Error reading table information for %s", hptable);
    SCTPUT(output);
    return flames_midas_fail();
  }

  if (TCTOPN(traptable, F_I_MODE, &traptid)!=0) {
    sprintf(output, "Error opening table %s", traptable);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (TCCSER(traptid, trapstartxname, &trapstartxcol)!=0 || 
      trapstartxcol==-1) {
    sprintf(output, "Error: nonexistent %s column in %s table", 
	    trapstartxname, traptable);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (TCCSER(traptid, trapstartyname, &trapstartycol)!=0 || 
      trapstartycol==-1) {
    sprintf(output, "Error: nonexistent %s column in %s table", 
	    trapstartyname, traptable);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (TCCSER(traptid, trapendxname, &trapendxcol)!=0 || trapendxcol==-1) {
    sprintf(output, "Error: nonexistent %s column in %s table", 
	    trapendxname, traptable);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (TCCSER(traptid, trapendyname, &trapendycol)!=0 || trapendycol==-1) {
    sprintf(output, "Error: nonexistent %s column in %s table", 
	    trapendyname, traptable);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (TCIGET(traptid, &ncols, &traprows)!=0) {
    sprintf(output, "Error reading table information for %s", traptable);
    SCTPUT(output);
    return flames_midas_fail();
  }

  /* Build the mask, initialising it to zero */
  mask = fmmatrix(0, npix[1]-1, 0, npix[0]-1);
  for (i=0; i<=(npix[1]-1); i++) {
    maski = mask[i];
    for (j=0; j<=(npix[0]-1); j++) maski[j]=0;
  }

  /* Now read all the pixels listed in the hot pixels table, and set them to
     1 */
  for (row=1; row<=hprows; row++) {
    status = TCERDD(hptid, row, hpxcol, &worldx, &null)==0;
    /* was the read successful, and did we get a non-null value? */
    status = status && null==0;
    /* convert to pixel coordinates */
    pixelx = (int) floor((worldx-start[0])/step[0]+.5);
    /* check for frame boundaries */
    status = status && pixelx>=0 && pixelx<=(npix[0]-1);
    status = status && TCERDD(hptid, row, hpycol, &worldy, &null)==0;
    /* was the read successful, and did we get a non-null value? */
    status = status && null==0;
    /* convert to pixel coordinates */
    pixely = (int) floor((worldy-start[1])/step[1]+.5);
    /* check for frame boundaries */
    status = status && pixely>=0 && pixely<=(npix[1]-1);
    uves_error_reset();
    if (status) {
      /* go ahead and mark this pixel as bad */
      mask[pixely][pixelx] = 1;
    }
  }
  /* we can close the hptable now */
  if (TCTCLO(hptid)!=0) {
    sprintf(output, "Error closing table %s", hptable);
    SCTPUT(output);
    return flames_midas_fail();
  }

  /* now read the limits of the zones were traps are present */
  for (row=1; row<=traprows; row++) {
    status = TCERDD(traptid, row, trapstartxcol, &worldx, &null)==0;
    /* was the read successful, and did we get a non-null value? */
    status = status && null==0;
    /* convert to pixel coordinates */
    pixelstartx = (int) floor((worldx-start[0])/step[0]+.5);
    /* check for frame boundaries */
    status = status && pixelstartx>=0 && pixelstartx<=(npix[0]-1);
    status = status && TCERDD(traptid, row, trapstartycol, &worldy, &null)==0;
    /* was the read successful, and did we get a non-null value? */
    status = status && null==0;
    /* convert to pixel coordinates */
    pixelstarty = (int) floor((worldy-start[1])/step[1]+.5);
    /* check for frame boundaries */
    status = status && pixelstarty>=0 && pixelstarty<=(npix[1]-1);
    status = status && TCERDD(traptid, row, trapendxcol, &worldx, &null)==0;
    /* was the read successful, and did we get a non-null value? */
    status = status && null==0;
    /* convert to pixel coordinates */
    pixelendx = (int) floor((worldx-start[0])/step[0]+.5);
    /* check for frame boundaries */
    status = status && pixelendx>=0 && pixelendx<=(npix[0]-1);
    status = status && TCERDD(traptid, row, trapendycol, &worldy, &null)==0;
    /* was the read successful, and did we get a non-null value? */
    status = status && null==0;
    /* convert to pixel coordinates */
    pixelendy = (int) floor((worldy-start[1])/step[1]+.5);
    /* check for frame boundaries */
    status = status && pixelendy>=0 && pixelendy<=(npix[1]-1);
    if (status) {
      /* go ahead and mark this region as bad */
      if (pixelstartx>pixelendx) {
	/* swap pixelstartx and pixelendx */
	ibuf = pixelstartx;
	pixelstartx = pixelendx;
	pixelendx = ibuf;
      }
      if (pixelstarty>pixelendy) {
	/* swap pixelstarty and pixelendy */
	ibuf = pixelstarty;
	pixelstarty = pixelendy;
	pixelendy = ibuf;
      }
      /* we are finally ready to actually mark the bad region */
      for (i=pixelstarty; i<=pixelendy; i++) {
	maski = mask[i];
	for (j=pixelstartx; j<=pixelendx; j++) maski[j]=1;
      }
    }
  }
  /* we can close the traptable now */
  if (TCTCLO(traptid)!=0) {
    sprintf(output, "Error closing table %s", traptable);
    SCTPUT(output);
    return flames_midas_fail();
  }

  /* We are ready to write the bad pixel mask to disk, create the file */
  if (SCFCRE(outimage, FLAMESMASKTYPE, F_O_MODE, F_IMA_TYPE, npix[0]*npix[1], 
	     &outid) != 0) {
    sprintf(output, "Error creating %s image on disk", outimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (SCFPUT(outid, 1, npix[0]*npix[1], (char *) mask[0]) != 0) {
    SCTPUT("Error writing mask to disk");
    return flames_midas_fail();
  }
  /* write standard image descriptors */
  if (SCDWRI(outid, "NAXIS", &naxis, 1, 1, &unit) != 0) {
    sprintf(output, "Error writing NAXIS descriptor in %s image", outimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (SCDWRD(outid, "START", start, 1, naxis, &unit) != 0) {
    sprintf(output, "Error writing START descriptor in %s image", outimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (SCDWRD(outid, "STEP", step, 1, naxis, &unit) != 0) {
    sprintf(output, "Error writing STEP descriptor in %s image", outimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  if (SCDWRI(outid, "NPIX", npix, 1, naxis, &unit) != 0) {
    sprintf(output, "Error writing NPIX descriptor in %s image", outimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  memset(ident, '\0', 73);
  strcpy(ident, "bad pixel mask");
  if (SCDWRC(outid, "IDENT", 1, ident, 1, 72, &unit) != 0) {
    sprintf(output, "Error writing IDENT descriptor in %s image", outimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  memset(cunit[0], '\0', 48);
  strncpy(cunit[0], "                ", 16);
  strncpy(cunit[1], "PIXEL           ", 16);
  strncpy(cunit[2], "PIXEL           ", 16);
  if (SCDWRC(outid, "CUNIT", 1, cunit[0], 1, 48, &unit) != 0) {
    sprintf(output, "Error writing CUNIT descriptor in %s image", outimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  cuts[0]=cuts[1]=cuts[2]=0;
  cuts[3]=1;
  if (SCDWRR(outid, "LHCUTS", cuts, 1, 4, &unit)!=0) {
    sprintf(output, "Error writing LHCUTS descriptor in %s image", outimage);
    SCTPUT(output);
    return flames_midas_fail();
  }
  /* close the mask frame */
  if (SCFCLO(outid)!=0) {
    sprintf(output, "Error closing image %s", outimage);
    SCTPUT(output);
    return flames_midas_fail();
  }

  /* free the mask */
  free_fmmatrix(mask, 0, npix[1]-1, 0, npix[0]-1);

  return SCSEPI();
}
/**@}*/
