/*                                                                              *
 *   This file is part of the ESO UVES Pipeline                                 *
 *   Copyright (C) 2004,2005 European Southern Observatory                      *
 *                                                                              *
 *   This library 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA       *
 *                                                                              */

/*
 * $Author: amodigli $
 * $Date: 2010-09-24 09:32:02 $
 * $Revision: 1.26 $
 * $Name: not supported by cvs2svn $
 *
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/*----------------------------------------------------------------------------*/
/**
 * @defgroup uves_corrbadpix  Bad pixel correction
 *
 * Correct the bad pixels in an image
 */
/*----------------------------------------------------------------------------*/
/**@{*/

/*-----------------------------------------------------------------------------
                                Includes
 -----------------------------------------------------------------------------*/

#include "uves_corrbadpix.h"

#include <uves_pfits.h>
#include <uves_dump.h>
#include <uves_error.h>
#include <uves_msg.h>

#include <cpl.h>

#include <stdbool.h>
/*-----------------------------------------------------------------------------
                            Functions prototypes
 -----------------------------------------------------------------------------*/
static int
uves_correct_badpix(cpl_image *master_bias, uves_propertylist *header, int **badmap,
            bool mark_bad);

/*-----------------------------------------------------------------------------
                            Implementation
 -----------------------------------------------------------------------------*/


/*----------------------------------------------------------------------------*/
/**
   @brief    Correct all bad pixels on a chip
   @param    master_bias        The image to correct
   @param    mbias_header       Image FITS header, 
   @param    chip               CCD chip
   @param    binx               x-binning
   @param    biny               y-binning
   @param    mark_bad           If true, the relevant pixels are not interpolated
                                but simply marked as bad.
   @return   Number of pixels that was corrected, or undefined on error.

   The bad pixels are replaced by the average of nearest good pixels in the 
   same column, or simply marked as bad.

   The positions of bad pixels are hard-coded (as function of UVES chip).
*/
/*----------------------------------------------------------------------------*/
int
uves_correct_badpix_all(cpl_image *master_bias, uves_propertylist *mbias_header,
            enum uves_chip chip,
                        int binx, int biny, int mark_bad, bool red_ccd_new)
{
    int badpixels_cleaned = -1; /* result */

    int **badmap = NULL;

    check( badmap = uves_get_badpix(chip, binx, biny, mark_bad,red_ccd_new),
       "Could not get bad pixel map");

    check( badpixels_cleaned = 
       uves_correct_badpix(master_bias, mbias_header, badmap, mark_bad),
       "Error cleaning bad pixels");

  cleanup:
    uves_badmap_free(&badmap);
    return badpixels_cleaned;
}


/*----------------------------------------------------------------------------*/
/**
   @brief    Deallocate bpm position
   @param    badmap       to deallocate (pointer to 2d array)
*/
/*----------------------------------------------------------------------------*/
void
uves_badmap_free(int ***badmap)
{
    if (badmap != NULL)
    {
        if (*badmap != NULL)
        {
            int row;
            for (row = 0;
             (*badmap)[row][0] != -1;
             row++)
            {
                cpl_free((*badmap)[row]);
            }
            cpl_free((*badmap)[row]);

            cpl_free(*badmap);
            *badmap = NULL;
        }
    }
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Copy bpm to heap
   @param    badmap       to copy
   @return   dynamically allocated copy of input
*/
/*----------------------------------------------------------------------------*/
static int **
dup_map(int badmap[][4])
{
    int **map = NULL;
    int row;
    bool finished = false;

    /* The execution time is O(n*n) which is okay, due to the low number
       of entries */
    
    for (row = 0; !finished; row++)
    {
        map = cpl_realloc(map, (row+1)*sizeof(int *));
        /* First time equivalent to: malloc(sizeof(int *)) */

        map[row] = cpl_calloc(4, sizeof(int));
        map[row][0] = badmap[row][0];
        map[row][1] = badmap[row][1];
        map[row][2] = badmap[row][2];
        map[row][3] = badmap[row][3];

        finished = (badmap[row][0] == -1);
    }
    
    return map;
}


/*----------------------------------------------------------------------------*/
/**
   @brief    Get hard-coded bpm map
   @param    chip          UVES CCD chip
   @param    binx          x binning
   @param    biny          y binning
   @param    mark_bad      a strange parameter, if true, a few more regions
                           are marked as bad (for compatibility with MIDAS)
   @return   dynamically allocated array of bad pixel areas, must be deallocated
             with uves_badmap_free()

   The array is terminated with a row of -1

*/
/*----------------------------------------------------------------------------*/
int **
uves_get_badpix(enum uves_chip chip, 
                int binx, int biny, int mark_bad,bool red_ccd_new)
{
    int **map = NULL;

    if (chip == UVES_CHIP_REDL)
    {
        if (binx == 1 && biny == 1)
        {
           if(red_ccd_new) {

           int badmap[][4] = {{1,4,2088,4},
                       {1,63,2282,63},
                       {1,108,1778,108},
                       {1,176,2443,176},
                       {1,196,2021,196},
                       {1,285,1974,285},
                       {1,352,1942,352},
                       {-1,-1,-1,-1}};

           if (!mark_bad)
            {
                badmap[2][0] = -1;
                badmap[2][1] = -1;
                badmap[2][2] = -1;
                badmap[2][3] = -1;
            }

/*
            int badmap[][4] = {{1,4,2088,4},
                               {1,63,2282,63},
                               {1,176,2443,176},
                               {1,196,2021,196},
                               {1,285,1974,285},
                               {1,352,1942,352},
                               {-1,-1,-1,-1}};
*/
          map = dup_map(badmap);

           } else {
            int badmap[][4] = {{1,4,2088,4},
                       {1,63,2282,63},
                       {1,108,1778,108},
                       {1,176,2443,176},
                       {1,196,2021,196},
                       {1,285,1974,285},
                       {1,352,1942,352},
                       {-1,-1,-1,-1}};
          map = dup_map(badmap);

           }
 
        }
        else if (binx == 1 && biny == 2)
        {


          if(red_ccd_new) {

            int badmap[][4] = {{1,4,1045,4},
                    {1,63,1141,63},
                    {1,108,894,108},
                    {1,176,1222,176},
                    {1,196,1011,196},
                    {1,285,988,285},
                    {1,352,971,352},
                    {-1,-1,-1,-1}};
          map = dup_map(badmap);
          } else {

           int badmap[][4] = {{1,4,1044,4},
                    {1,63,1141,63},
                    {1,108,894,108},
                    {1,176,1222,176},
                    {1,196,1011,196},
                    {1,285,988,285},
                    {1,352,971,352},
                    {-1,-1,-1,-1}};

          map = dup_map(badmap);
          }
        }
        else if (binx == 2 && biny == 2)
        {
          if(red_ccd_new) {

            int badmap[][4] = {{1,4,1045,4},
                               {1,14,1255,14},
                               {1,33,1141,33},
                               {1,89,1222,89},
                               {1,99,1011,100},
                               {1,144,988,144},
                               {1,177,971,178},
                               {-1,-1,-1,-1}};

          map = dup_map(badmap);
          } else {
           int badmap[][4] = {{1,3,1044,3},
                       {1,33,1141,33},
                       {1,55,894,56},
                       {1,89,1222,90},
                       {1,99,1011,100},
                       {1,144,988,145},
                       {1,177,971,178},
                       {-1,-1,-1,-1}};
          map = dup_map(badmap);
          }
        }
        else if (binx == 2 && biny == 3)
        {

          if(red_ccd_new) {

            int badmap[][4] = {{1,3,696,3},
                       {1,14,836,15},
                       {1,33,761,33},
                       {1,55,596,56},
                       {1,89,814,90},
                       {1,97,805,90},
                       {1,99,674,100},
                       {1,144,658,144},
                       {1,156,784,156},
                       {1,168,759,168},
                       {1,177,647,178},
                       {1,203,826,203},
                       {1,263,714,263},
                        {-1,-1,-1,-1}};
          map = dup_map(badmap);
          } else {

            int badmap[][4] = {{1,3,696,3},
                       {1,33,761,33},
                       {1,55,592,56},
                       {1,89,814,90},
                       {1,99,674,100},
                       {1,144,658,144},
                       {1,177,647,178},
                       {-1,-1,-1,-1}};

          map = dup_map(badmap);
          }
        }
        else
        {
            assure( false, CPL_ERROR_ILLEGAL_INPUT,
                "Don't know bad pixel map for %dx%d binning, red, lower chip", 
                binx, biny);
        }
    }
    else if  (chip == UVES_CHIP_REDU)
    {
        /* Use different bad pixels maps for bias and
           orderpos recipes !
        */
        if (binx == 1 && biny ==1)
        {
          if(red_ccd_new) {

            int badmap[][4] = {
               {1,845,1268,845},
               {-1,-1,-1,-1}};

           if (!mark_bad)
            {
                badmap[2][0] = -1;
                badmap[2][1] = -1;
                badmap[2][2] = -1;
                badmap[2][3] = -1;
            }
           map = dup_map(badmap);
          } else {

            int badmap[][4] = {
               {1,2030,1268,2033},
               {1269,2033,4096,2033},
               {1201, 491, 3271, 492},
               {-1,-1,-1,-1}};

            if (!mark_bad)
            {
                badmap[2][0] = -1;
                badmap[2][1] = -1;
                badmap[2][2] = -1;
                badmap[2][3] = -1;
            }
            map = dup_map(badmap);
          }
        }
        else if (binx == 1 && biny == 2)
        {

          if(red_ccd_new) {

            int badmap[][4] = {{1,1396,845,1396},
                       {-1,-1,-1,-1}};

            if (!mark_bad)
            {
                badmap[2][0] = -1;
                badmap[2][1] = -1;
                badmap[2][2] = -1;
                badmap[2][3] = -1;
            }
            map = dup_map(badmap);
          } else {

            int badmap[][4] = {{1,2030,634,2033},
                       {635,2033,2048,2033},
                       {600, 491,1635, 492},
                       {-1,-1,-1,-1}};

            if (!mark_bad)
            {
                badmap[2][0] = -1;
                badmap[2][1] = -1;
                badmap[2][2] = -1;
                badmap[2][3] = -1;
            }
            map = dup_map(badmap);
          }
        }
        else if (binx == 2 && biny == 2)
        {

          if(red_ccd_new) {

            int badmap[][4] = {{1,422,1526,422},
                       {-1,-1,-1,-1}};

             if (!mark_bad)
             {
                badmap[2][0] = -1;
                badmap[2][1] = -1;
                badmap[2][2] = -1;
                badmap[2][3] = -1;
             }
             map = dup_map(badmap);
          } else {

             int badmap[][4] = {{1,1013,634,1016},
                                {635,1015,2048,1016},
                                {600, 244,1635, 245},
                                {-1,-1,-1,-1}};

             if (!mark_bad)
             {
                badmap[2][0] = -1;
                badmap[2][1] = -1;
                badmap[2][2] = -1;
                badmap[2][3] = -1;
             }
             map = dup_map(badmap);
          }
        }
        else if (binx == 2 && biny == 3)
        {

          if(red_ccd_new) {
            int badmap[][4] = {{1,61,287,62},
                       {1,422,1051,422},
                       {400, 872,1265, 872},
                       {-1,-1,-1,-1}};

            if (!mark_bad)
             {
                badmap[2][0] = -1;
                badmap[2][1] = -1;
                badmap[2][2] = -1;
                badmap[2][3] = -1;
             }
            map = dup_map(badmap);

          } else {

             int badmap[][4] = {{1,1013,423,1016},
                                {424,1015,1365,1016},
                                {400, 244,1090, 245},
                                {-1,-1,-1,-1}};

             if (!mark_bad)
             {
                badmap[2][0] = -1;
                badmap[2][1] = -1;
                badmap[2][2] = -1;
                badmap[2][3] = -1;
             }
             map = dup_map(badmap);


          }
        }
        else
        {
            assure( false, CPL_ERROR_ILLEGAL_INPUT,
                "Don't know bad pixel map for %dx%d binning, red, upper chip", 
                binx, biny);
        }
    }
    else
    {
        /* No blue chip bad pixels */
        int badmap[][4] = {{-1,-1,-1,-1}};
        
        map = dup_map(badmap);
    }

  cleanup:
    return map;
}



/*----------------------------------------------------------------------------*/
/**
   @brief    Correct bad pixels
   @param    master_bias        The image to correct
   @param    header             Image FITS header, 
   @param    badmap             Matrix of bad pixel traps. Each row is in the format
                                (xstart, ystart, xend, yend), except the last row which must
                be (-1, -1, -1, -1).
   @param    mark_bad           If true, the relevant pixels are not interpolated
                                but simply marked as bad.
   @return   Number of pixels that was corrected, or undefined on error.

   The bad pixels are replaced by the average of nearest good pixels in the same column.

*/
/*----------------------------------------------------------------------------*/
static int
uves_correct_badpix(cpl_image *master_bias, uves_propertylist *header, int **badmap,
            bool mark_bad)
{
    int ncorrect = 0;   /* Result */
    int xstart, ystart, xend, yend;
    int row;
    bool finished = false;
    int nx, ny;
    cpl_mask  *image_bad = NULL;
    cpl_binary*image_bpm = NULL;
    cpl_type type=0;

    type=cpl_image_get_type(master_bias);
    assure( (type == CPL_TYPE_DOUBLE) || (type == CPL_TYPE_FLOAT),
        CPL_ERROR_UNSUPPORTED_MODE,
        "Image type must be float or double. It is %s",
        uves_tostring_cpl_type(cpl_image_get_type(master_bias)));

    image_bad = cpl_image_get_bpm(master_bias);
    image_bpm = cpl_mask_get_data(image_bad);

    nx = cpl_image_get_size_x(master_bias);
    ny = cpl_image_get_size_y(master_bias);

    row = 0;
    while (!finished)
    {
        xstart = badmap[row][0];
        ystart = badmap[row][1];
        xend   = badmap[row][2];
        yend   = badmap[row][3];

        if (xstart > 0)
        {
            int ylow, yhigh;
            int x, y;
            
            assure( 1 <= xstart && xstart <= nx &&
                1 <= xend   && xend   <= nx &&
                1 <= ystart && ystart <= ny &&
                1 <= yend   && yend   <= ny, CPL_ERROR_ILLEGAL_INPUT, 
                "Illegal window (%d, %d) - (%d, %d). Image size = %dx%d",
                xstart, ystart, xend, yend, nx, ny);
            
            if ( ystart < 3 )
            {
                assure( yend + 2 <= ny, CPL_ERROR_ILLEGAL_INPUT,
                    "Too large range in y: %d - %d", ystart, yend);

                ylow  = yend + 1;
                yhigh = yend + 2;
            }
            else if (yend > ny - 3 )
            {
                assure( ystart - 2 >= 1, CPL_ERROR_ILLEGAL_INPUT,
                    "Too large range in y: %d - %d", ystart, yend);
                
                ylow  = ystart - 2;
                yhigh = ystart - 1;
            }
            else
            {
                ylow  = ystart - 2;
                yhigh = yend + 2;
            }
            
            uves_msg("Correcting window (%d, %d)-(%d, %d)", xstart, ystart, xend, yend);

            if(type == CPL_TYPE_DOUBLE) {

            for (x = xstart; x <= xend; x++) {
            for (y = ystart; y <= yend; y++) {
                int pis_rejected;
                
                if (mark_bad)
                {
                    /* This is extremely slow with CPL2:
                       cpl_image_reject(master_bias, x, y);
                    */
                    image_bpm[(x-1) + (y-1)*nx] = CPL_BINARY_1;
                }
                else
                /* interpolate */
                {
                    double *master_bias_data;

                    double i1 = cpl_image_get(master_bias, x, ylow , &pis_rejected);
                    double i2 = cpl_image_get(master_bias, x, yhigh, &pis_rejected);
                    
                    /* Write average */
                    
                    /* This will make the bpm invalid:
                       cpl_image_set(master_bias, x, y, (i1+i2)/2);
                    */
                    master_bias_data = cpl_image_get_data_double(master_bias);
                    master_bias_data[(x-1) + (y-1)*nx] = (i1+i2)/2;
                }
                ncorrect += 1;
            }

            }

	    } else {

            for (x = xstart; x <= xend; x++) {
            for (y = ystart; y <= yend; y++) {
                int pis_rejected;
                if (mark_bad)
                {
                    /* This is extremely slow with CPL2:
                       cpl_image_reject(master_bias, x, y);
                    */
                    image_bpm[(x-1) + (y-1)*nx] = CPL_BINARY_1;
                }
                else
                /* interpolate */
                {
                    float *master_bias_data;

                    float i1 = cpl_image_get(master_bias, x, ylow , &pis_rejected);
                    float i2 = cpl_image_get(master_bias, x, yhigh, &pis_rejected);
                    
                    /* Write average */
                    
                    /* This will make the bpm invalid:
                       cpl_image_set(master_bias, x, y, (i1+i2)/2);
                    */
                    master_bias_data = cpl_image_get_data_float(master_bias);
                    master_bias_data[(x-1) + (y-1)*nx] = (i1+i2)/2;
                }
                ncorrect += 1;
            }

            }

	    }

        }
        else
        {
            finished = true;
        }
        row++;
    }

    /* Update product header */
    if (ncorrect > 0)
    {
        check( uves_pfits_set_badpixcorr(header, "true"),
           "Error updating product header");
    }

  cleanup:
    return ncorrect;
}
/**@}*/
