/*                                                                           *
 *   This file is part of the ESO X-shooter Pipeline                         *
 *   Copyright (C) 2006 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 b�binxy    *
 *   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: 2013-01-02 16:23:43 $
 * $Revision: 1.11 $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_data_pre_3d     Pre 3D Format
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/

/**@{*/

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

#include <xsh_data_image_3d.h>
#include <xsh_data_pre_3d.h>
#include <xsh_dump.h>
#include <xsh_utils.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <xsh_dfs.h>
#include <math.h>
#include <time.h>
#include <cpl.h>

/*----------------------------------------------------------------------------
                                 Function implementation
 ----------------------------------------------------------------------------*/
/**
  @brief 
    Fill the XSH_PRE structure from FITS header file
  @param[in] pre
    The pre structure to fill
  @param[in] header
    The header of fits image
*/
/*---------------------------------------------------------------------------*/
static void xsh_pre_3d_init(xsh_pre_3d * pre, cpl_propertylist * header)
{

  /* check  parameters input */
  XSH_ASSURE_NOT_NULL(pre);
  XSH_ASSURE_NOT_NULL(header);

  /* set header to pre */
  pre->data_header = header;

  /* must be read */ 
  check( pre->naxis1 = xsh_pfits_get_naxis1(pre->data_header));
  check( pre->naxis2 = xsh_pfits_get_naxis2(pre->data_header));
  check( pre->naxis3 = xsh_pfits_get_naxis3(pre->data_header));

  cleanup:
    return;
}


void xsh_pre_3d_free( xsh_pre_3d ** pre_3d)
{
  xsh_image_3d *img = NULL;
  cpl_propertylist *pl = NULL;

  if ( pre_3d != NULL && *pre_3d != NULL) {
    img = (*pre_3d)->data;
    xsh_image_3d_free( &img);
    img = (*pre_3d)->errs;
    xsh_image_3d_free( &img);
    img = (*pre_3d)->qual;
    xsh_image_3d_free( &img);

    pl = (*pre_3d)->data_header;
    xsh_free_propertylist( &pl);
    pl = (*pre_3d)->errs_header;
    xsh_free_propertylist( &pl);
    pl = (*pre_3d)->qual_header;
    xsh_free_propertylist( &pl);

    XSH_FREE( *pre_3d);
    *pre_3d = NULL;
  }
}

/*---------------------------------------------------------------------------*/
/**
  @brief 
    Create new PRE image
  @param nx
    The x-size
  @param ny
    The y-size
  @param nz
    The z-size
  @return
    The newly allocated PRE image

  The data and errs units are initialized to zero.  
*/

/*---------------------------------------------------------------------------*/
xsh_pre_3d * xsh_pre_3d_new (int nx, int ny, int nz)
{
  xsh_pre_3d *result = NULL;

  assure (nx > 0 && ny > 0 && nz > 0, CPL_ERROR_ILLEGAL_INPUT,
          "Illegal image size: %dx%dx%d", nx, ny, nz );

  XSH_CALLOC( result, xsh_pre_3d, 1);

  result->nx = nx;
  result->ny = ny;
  result->nz = nz;

  check( result->data = xsh_image_3d_new (nx, ny, nz, XSH_PRE_DATA_TYPE));
  check( result->errs = xsh_image_3d_new (nx, ny, nz, XSH_PRE_ERRS_TYPE));
  check( result->qual = xsh_image_3d_new (nx, ny, nz, XSH_PRE_QUAL_TYPE));
  check( result->data_header = cpl_propertylist_new());
  check( result->errs_header = cpl_propertylist_new());
  check( result->qual_header = cpl_propertylist_new());

  cleanup:
    if (cpl_error_get_code () != CPL_ERROR_NONE) {
      xsh_pre_3d_free ( &result);
      result = NULL;
    }
    return result;
}

/*---------------------------------------------------------------------------*/
/**
  @brief
    Load a xsh_pre_3d structure from a frame
  @param[in] frame 
  @return  
    A newly allocated PRE structure
 */
/*---------------------------------------------------------------------------*/
xsh_pre_3d * xsh_pre_3d_load( cpl_frame * frame)
{
  /* MUST BE DEALLOCATED in caller */
  xsh_pre_3d * result = NULL;
  /* Others */
  const char *filename = NULL;
  int extension = 0;
  cpl_propertylist * data_header = NULL;

  /* check  parameters input */
  XSH_ASSURE_NOT_NULL( frame);
  XSH_ASSURE_NOT_NULL( cpl_frame_get_tag (frame));

  check ( filename = cpl_frame_get_filename (frame));

  /* allocate memory */
  XSH_CALLOC( result, xsh_pre_3d, 1);

  /* fill structure */
  check(result->group = cpl_frame_get_group(frame));

  /* get nextensions */
  XSH_ASSURE_NOT_ILLEGAL(cpl_frame_get_nextensions(frame) == 2);

  /* Load data, validate file structure */
  extension = 0;
  check_msg (data_header = cpl_propertylist_load (filename, extension),
    "Cannot read the FITS header from '%s' extension %d",
    filename, extension);
  check(xsh_pre_3d_init( result, data_header));

  result->nx = result->naxis1;
  result->ny = result->naxis2;
  result->nz = result->naxis3;

  xsh_msg_dbg_medium( "Pre_3d_load: %d x %d x %d", result->nx, result->ny, result->nz) ;
  /* load data */
  check_msg (result->data = xsh_image_3d_load (filename, XSH_PRE_DATA_TYPE, 0 ),
    "Error loading image from %s extension 0", filename);

  /* load errs */
  check_msg (result->errs_header = cpl_propertylist_load (filename,1),
    "Cannot read the FITS header from '%s' extension 1",filename); 
  //assure(strcmp(xsh_pfits_get_extname(result->errs_header),"ERRS") == 0,
  // CPL_ERROR_ILLEGAL_INPUT,"extension 1 must be a errs extension");
  check_msg (result->errs = xsh_image_3d_load (filename, XSH_PRE_ERRS_TYPE, 1 ),
    "Error loading image from %s extension 1", filename);
  /* load qual */
  check_msg (result->qual_header = cpl_propertylist_load (filename,2),
    "Cannot read the FITS header from '%s' extension 2",filename);
  //assure(strcmp("QUAL",xsh_pfits_get_extname (result->qual_header)) == 0,
  //CPL_ERROR_ILLEGAL_INPUT,"extension 2 must be a qual extension");
  check_msg (result->qual = xsh_image_3d_load (filename, XSH_PRE_QUAL_TYPE, 2 ),
    "Error loading image from %s extension 2", filename);

  cleanup:
    if (cpl_error_get_code () != CPL_ERROR_NONE) {
      xsh_error_msg("can't load frame %s",cpl_frame_get_filename(frame));
      xsh_pre_3d_free( &result);
    }
    return result;
}

/*---------------------------------------------------------------------------*/
/**
  @brief Get nx of pre_3d structure
  @param pre the pre structure 
  @return the nx value
 */
/*---------------------------------------------------------------------------*/
int xsh_pre_3d_get_nx( const xsh_pre_3d * pre)
{
  int result = 0;

  XSH_ASSURE_NOT_NULL( pre );
  result = pre->nx;

  cleanup:
    return result;
}

/*---------------------------------------------------------------------------*/
/**
  @brief Get ny of pre_3d structure
  @param pre the pre structure
  @return the ny value
 */
/*---------------------------------------------------------------------------*/
int xsh_pre_3d_get_ny( const xsh_pre_3d * pre)
{
  int result = 0;

  XSH_ASSURE_NOT_NULL( pre );
  result = pre->ny;

  cleanup:
    return result;
}

/*---------------------------------------------------------------------------*/
/**
  @brief Get nz of pre_3d structure
  @param pre the pre structure
  @return the ny value
 */
/*---------------------------------------------------------------------------*/
int xsh_pre_3d_get_nz( const xsh_pre_3d * pre)
{
  int result = 0 ;

  XSH_ASSURE_NOT_NULL( pre );
  result = pre->nz ;

  cleanup:
    return result;
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Get data
   @param    pre_3d              The PRE image
   @return   Pointer to existing data image
*/
/*----------------------------------------------------------------------------*/
xsh_image_3d * xsh_pre_3d_get_data( xsh_pre_3d * pre_3d )
{
  xsh_image_3d *data = NULL;

  XSH_ASSURE_NOT_NULL(pre_3d);
  data = pre_3d->data;

  cleanup:
    return data;
}
/*----------------------------------------------------------------------------*/
/**
   @brief    Get errs
   @param    pre_3d              The PRE image
   @return   Pointer to existing data image
*/
/*----------------------------------------------------------------------------*/
xsh_image_3d * xsh_pre_3d_get_errs( xsh_pre_3d * pre_3d )
{
  xsh_image_3d *data = NULL;

  XSH_ASSURE_NOT_NULL(pre_3d);
  data = pre_3d->errs;

  cleanup:
    return data;
}
/*----------------------------------------------------------------------------*/
/**
   @brief    Get qual
   @param    pre_3d              The PRE image
   @return   Pointer to existing data image
*/
/*----------------------------------------------------------------------------*/
xsh_image_3d * xsh_pre_3d_get_qual( xsh_pre_3d * pre_3d )
{
  xsh_image_3d *data = NULL;

  XSH_ASSURE_NOT_NULL(pre_3d);
  data = pre_3d->qual;

  cleanup:
    return data;
}



/**
 * Save the data cube image. Note that the output file is automatically
 * rewritten if it exists.
 *
 * @param img_3d Input Data Cube
 * @param fname Output file name
 * @param header FITS Header
 * @param mode  saving mode (default/extend)
 *
 * @return Possible error code
 */
static cpl_error_code xsh_cube_3d_save_float( xsh_image_3d * img_3d,
          const char * fname,
          cpl_propertylist * header,
          unsigned mode )
{

  int naxis1 = xsh_image_3d_get_size_x(img_3d);
  int naxis2 = xsh_image_3d_get_size_y(img_3d);
  int naxis3 = xsh_image_3d_get_size_z(img_3d);

  cpl_imagelist* cube = NULL;
  register int k = 0;
  register float* base = NULL;

  cube = cpl_imagelist_new();
  base = img_3d->pixels;

  for (k = 0; k < naxis3; k++) {

    cpl_image* plane = cpl_image_wrap_float(naxis1, naxis2, base);
    cpl_imagelist_set(cube, cpl_image_duplicate(plane), k);
    base += naxis1 * naxis2;
    cpl_image_unwrap(plane);

  }

  cpl_imagelist_save(cube, fname, CPL_BPP_IEEE_FLOAT, header, mode);
  xsh_free_imagelist(&cube);

  return cpl_error_get_code();
}


/**
 * Save the data cube image. Note that the output file is automatically
 * rewritten if it exists.
 *
 * @param img_3d Input Data Cube
 * @param fname Output file name
 * @param header FITS Header
 * @param mode  saving mode (default/extend)
 *
 * @return Possible error code
 */
static cpl_error_code xsh_cube_3d_save_int( xsh_image_3d * img_3d,
          const char * fname,
          cpl_propertylist * header,
          unsigned mode )
{

  int naxis1 = xsh_image_3d_get_size_x(img_3d);
  int naxis2 = xsh_image_3d_get_size_y(img_3d);
  int naxis3 = xsh_image_3d_get_size_z(img_3d);

  cpl_imagelist* cube = NULL;
  register int k = 0;
  register int* base = NULL;

  cube = cpl_imagelist_new();
  base = img_3d->pixels;

  for (k = 0; k < naxis3; k++) {

    cpl_image* plane = cpl_image_wrap_int(naxis1, naxis2, base);
    cpl_imagelist_set(cube, cpl_image_duplicate(plane), k);
    base += naxis1 * naxis2;
    cpl_image_unwrap(plane);
  }

  cpl_imagelist_save(cube, fname, CPL_BPP_32_SIGNED, header, mode);
  xsh_free_imagelist(&cube);
  return cpl_error_get_code();
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Save PRE_3D on disk
   @param    pre to save
   @param    filename The file to save to
   @param    temp Flag if 1 frame is temporary file
   @return   A frame pointing to the file that was saved
*/
/*----------------------------------------------------------------------------*/
cpl_frame * xsh_pre_3d_save (const xsh_pre_3d * pre, const char *filename,
			     int temp )
{
  cpl_frame * product_frame = NULL;

  XSH_ASSURE_NOT_NULL(pre);
  XSH_ASSURE_NOT_NULL(filename);

  /* Save the file */
  check_msg(xsh_cube_3d_save_float( pre->data, filename,
			       pre->data_header, CPL_IO_DEFAULT),
	    "Could not save data to %s extension 0", filename);
  check_msg (xsh_cube_3d_save_float( pre->errs, filename,
				pre->errs_header, CPL_IO_EXTEND),
	     "Could not save errs to %s extension 1", filename);
  check_msg (xsh_cube_3d_save_int( pre->qual, filename,
				pre->qual_header, CPL_IO_EXTEND),
	     "Could not save qual to %s extension 2", filename);
  /* Create product frame */
  product_frame = cpl_frame_new ();
  XSH_ASSURE_NOT_NULL( product_frame);
  
  check( cpl_frame_set_filename (product_frame, filename) ) ;
  check( cpl_frame_set_type (product_frame, CPL_FRAME_TYPE_IMAGE) ) ;
/* This is a final product==> do not declare it as temporary
  if ( temp != 0 ) {
    check( cpl_frame_set_level( product_frame,
				CPL_FRAME_LEVEL_TEMPORARY));
    //xsh_add_temporary_file( filename ) ;
  }
*/

cleanup:
  if (cpl_error_get_code () != CPL_ERROR_NONE) {
    xsh_free_frame(&product_frame);
    product_frame = NULL;
  }
  return product_frame;
}

/**@}*/

