/* $Id: moses.c,v 1.116 2013/10/15 09:27:38 cgarcia Exp $
 *
 * This file is part of the MOSES library
 * Copyright (C) 2002-2010 European Southern Observatory
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

/*
 * $Author: cgarcia $
 * $Date: 2013/10/15 09:27:38 $
 * $Revision: 1.116 $
 * $Name:  $
 */

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

#include <cmath>
#include <stdexcept>
#include <string>
#include <cpl.h>
#include "fors_trimm_illum.h"
#include "fors_image.h"
#include "fors_pfits.h"
#include "rect_region.h"


/*
 * This function will compute the illuminated area based on the
 * information in the illum_regions and the matching of detector
 * and collimator as present in setting.
 * If there is no matching the function return false.
 * Otherwise it returns true and crop_region will be filled
 * with the illuminated region.*/
bool fors_trimm_non_illum_get_region(cpl_propertylist * wcs_header,
                                     fors_setting * setting,
                                     const cpl_table * illum_regions,
                                     mosca::rect_region& crop_region)
{
    if(setting == NULL || illum_regions == NULL)
        return false;

    if (!cpl_table_has_column(illum_regions, "det_chip1_id") ||
        !cpl_table_has_column(illum_regions, "ins_coll_name") ||
        !cpl_table_has_column(illum_regions, "illuminated_region_llx") ||
        !cpl_table_has_column(illum_regions, "illuminated_region_lly") ||
        !cpl_table_has_column(illum_regions, "illuminated_region_urx") ||
        !cpl_table_has_column(illum_regions, "illuminated_region_ury"))
        throw std::invalid_argument("Table DETECTOR_ILLUMINATED_REGION"
            " does not contain a detector illumated region configuration");


    if (cpl_table_get_column_type(illum_regions, "det_chip1_id") != 
            CPL_TYPE_STRING ||
        cpl_table_get_column_type(illum_regions, "ins_coll_name") != 
            CPL_TYPE_STRING ||
        cpl_table_get_column_type(illum_regions, "illuminated_region_llx") != 
            CPL_TYPE_INT ||
        cpl_table_get_column_type(illum_regions, "illuminated_region_lly") != 
            CPL_TYPE_INT ||
        cpl_table_get_column_type(illum_regions, "illuminated_region_urx") != 
            CPL_TYPE_INT ||
        cpl_table_get_column_type(illum_regions, "illuminated_region_ury") != 
            CPL_TYPE_INT)
        throw std::invalid_argument("Unexpected column types "
            "for DETECTOR_ILLUMINATED_REGION table.");

    std::string science_det_chip1_id = setting->chip_id;
    std::string science_ins_coll_name = setting->coll_name;

    bool match = false;
    for(size_t i = 0; i < (size_t)cpl_table_get_nrow(illum_regions); i++)
    {
        int null;

        if(science_det_chip1_id == 
               std::string(cpl_table_get_string(illum_regions, "det_chip1_id", i)) &&
           science_ins_coll_name == 
               std::string(cpl_table_get_string(illum_regions, "ins_coll_name", i)))
        {
            int llx = cpl_table_get_int(illum_regions,"illuminated_region_llx", i, &null);
            int lly = cpl_table_get_int(illum_regions,"illuminated_region_lly", i, &null);
            int urx = cpl_table_get_int(illum_regions,"illuminated_region_urx", i, &null);
            int ury = cpl_table_get_int(illum_regions,"illuminated_region_ury", i, &null);
            int llx_binning = std::floor(1+(float)(llx-1) / setting->binx);
            int lly_binning = std::ceil((float)lly / setting->biny);
            int urx_binning = std::floor(1+(float)(urx-1) / setting->binx);
            int ury_binning = std::ceil((float)ury / setting->biny);
            match = true;
            crop_region = mosca::rect_region(llx_binning, lly_binning, 
                                             urx_binning, ury_binning);
            if(wcs_header != NULL)
            {
                if(cpl_propertylist_has(wcs_header, FORS_PFITS_CRPIX1))
                    cpl_propertylist_update_double(wcs_header, FORS_PFITS_CRPIX1,
                        cpl_propertylist_get_double(wcs_header, FORS_PFITS_CRPIX1) 
                        - llx_binning + 1);
                if(cpl_propertylist_has(wcs_header, FORS_PFITS_CRPIX2))
                    cpl_propertylist_update_double(wcs_header, FORS_PFITS_CRPIX2,
                        cpl_propertylist_get_double(wcs_header, FORS_PFITS_CRPIX2)
                        - lly_binning + 1);
            }
        }
    }

    return match;
}

/*
 * This function will trimm an image removing the non-illuminated
 * area of the detector. This is based on information found in the
 * table illum_regions.
 * Note also that the WCS information is updated in the wcs_header
 * If there is no matching in illum_regions table some of the
 * input pointers are NULL, the function return false.
 */
bool fors_trimm_non_illum(fors_image * image, 
                          cpl_propertylist * wcs_header,
                          fors_setting * setting,
                          const cpl_table * illum_regions)
{
    if(image == NULL)
        return false;

    //Get the region to crop
    mosca::rect_region crop_region;
    bool success = fors_trimm_non_illum_get_region(wcs_header, setting, 
                                                   illum_regions, crop_region);
    if(!success)
        return success;

    if(crop_region.is_empty())
        throw std::invalid_argument("Illuminated region is empty");


    //Crop the image
    fors_image_crop(image,
                    crop_region.llx(), crop_region.lly(),
                    crop_region.urx(), crop_region.ury());

    return success;
}

/*
 * This function will trimm an image removing the non-illuminated
 * area of the detector. This is based on information found in the
 * table illum_regions.
 * Note also that the WCS information is updated in the wcs_header
 * A new cpl_image is created and the input double pointer now
 * points to the new cpl_image.
 * If there is no matching in illum_regions table some of the
 * input pointers are NULL, the function return false.
 */
bool fors_trimm_non_illum(cpl_image ** image, 
                          cpl_propertylist * wcs_header,
                          fors_setting * setting,
                          const cpl_table * illum_regions)
{
    if(image == NULL)
        return false;

    if(*image == NULL)
        return false;

    //Get the region to crop
    mosca::rect_region crop_region;
    bool success = fors_trimm_non_illum_get_region(wcs_header, setting, 
                                                   illum_regions, crop_region);
    if(!success)
        return success;

    if(crop_region.is_empty())
        throw std::invalid_argument("Illuminated region is empty");

    //Crop the image
    cpl_image* new_image = cpl_image_extract(*image,
                                             crop_region.llx(), crop_region.lly(),
                                             crop_region.urx(), crop_region.ury());
    //Replace original image
    cpl_image_delete(*image);
    *image = new_image;
    
    return success;
}
