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

    double worldy=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 */
        int 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 */
        int 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 */
        int 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 */
        int 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 */
        int 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 */
        int 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();
}
/**@}*/
