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


#include <xsh_utils_wrappers.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <math.h>




static cpl_image * 
xsh_image_filter_wrapper(const cpl_image *b, const cpl_matrix *k, cpl_filter_mode mode)
{
	const double EPSILON = 1E-5;
	int nx   = cpl_image_get_size_x(b);
	int ny   = cpl_image_get_size_y(b);
	int nrow = cpl_matrix_get_nrow(k);
	int ncol = cpl_matrix_get_ncol(k);
	int i, j;
	cpl_type type = cpl_image_get_type(b);
	cpl_image * a = cpl_image_new(nx, ny, type);
	// where m is a cpl_mask with a CPL_BINARY_1 whereever k has a 1.0.
	cpl_mask* m = cpl_mask_new(ncol, nrow);
        /* commented out to speed up
	xsh_msg_dbg_medium("nx[%d], ny[%d], ncol[%d], nrow[%d]", nx, ny, ncol, nrow);
	*/
	for (i = 0; i < ncol ; i++)
	{
		for (j = 0; j < nrow ; j++)
		{
			double value = cpl_matrix_get(k, j, i);
			if (fabs(value - 1.0) < EPSILON)
			{
				cpl_mask_set(m, i + 1, j + 1, CPL_BINARY_1);
			}
		}
	}

	cpl_image_filter_mask(a, b, m, mode, CPL_BORDER_FILTER);
	cpl_mask_delete(m);
	return a;
 }

cpl_image*
xsh_image_filter_mode(const cpl_image* b,
                      const cpl_matrix * ker,
                      cpl_filter_mode filter)
{
  int nx   = cpl_image_get_size_x(b);
  int ny   = cpl_image_get_size_y(b);
  int type = cpl_image_get_type(b);
  cpl_image * a = cpl_image_new(nx, ny, type);

  switch(filter) {
  case CPL_FILTER_MEDIAN:
    check(cpl_image_filter(a, b, ker, CPL_FILTER_MEDIAN, CPL_BORDER_FILTER));
    break;
  case CPL_FILTER_LINEAR:
    check(cpl_image_filter(a, b, ker, CPL_FILTER_LINEAR, CPL_BORDER_FILTER));
    break;
  case CPL_FILTER_STDEV:
    cpl_image_filter(a, b, ker, CPL_FILTER_STDEV, CPL_BORDER_FILTER);
    break;
  case CPL_FILTER_MORPHO:
    cpl_image_filter(a, b, ker, CPL_FILTER_MORPHO, CPL_BORDER_FILTER);
    break;
  default:
    xsh_msg_error("Filter type not supported");
    return NULL;
  }
 cleanup:

  return a;

}

/*
cpl_polynomial * 
xsh_polynomial_fit_1d_create(const cpl_vector    * x_pos,
                             const cpl_vector    * values,
                             int degree,
                             double              * mse)
{
  cpl_polynomial * fit1d = cpl_polynomial_new(1);
  cpl_matrix     * samppos = cpl_matrix_wrap(1, cpl_vector_get_size(x_pos),
					     cpl_vector_get_data_const(x_pos));
  cpl_vector     * fitresidual = cpl_vector_new(cpl_vector_get_size(x_pos));
  cpl_polynomial_fit(fit1d, samppos, NULL, values, NULL,
		     CPL_FALSE, NULL, &degree);

  cpl_vector_fill_polynomial_fit_residual(fitresidual, values, NULL, fit1d,
					  samppos, NULL);
  cpl_matrix_unwrap(samppos);
  *mse = cpl_vector_product(fitresidual, fitresidual)
    / cpl_vector_get_size(fitresidual);


  return fit1d;

}

*/
cpl_polynomial * xsh_polynomial_fit_2d_create(cpl_bivector     *  xy_pos,
                                              cpl_vector       *  values,
                                              cpl_size              * degree,
                                              double           *  mse)
{
	typedef double* (*get_data)(cpl_bivector*);
	get_data data_extractor[2] = { &cpl_bivector_get_x_data, &cpl_bivector_get_y_data};
	//samppos matrix must
	// have two rows with copies of the two vectors in the x_pos bivector.

    double rechisq = 0;
    int i, j;
    cpl_vector     * fitresidual = 0;
    cpl_matrix     * samppos2d = 0;
    cpl_polynomial * fit2d = cpl_polynomial_new(2);
    int xy_size = cpl_bivector_get_size(xy_pos);

    samppos2d = cpl_matrix_new(2, xy_size);
    for (i = 0; i < 2; i++)
    {
    	for (j = 0; j < xy_size; j++)
    	{
    		double value = data_extractor[i](xy_pos)[j];
    		cpl_matrix_set(samppos2d, i, j, value);
    	}
    }

   cpl_polynomial_fit(fit2d, samppos2d, NULL, values, NULL, CPL_FALSE,
                             NULL, degree);

    fitresidual = cpl_vector_new(xy_size);
    cpl_vector_fill_polynomial_fit_residual(fitresidual, values, NULL, fit2d,
                                            samppos2d, &rechisq);
    if (mse)
    {
        *mse = cpl_vector_product(fitresidual, fitresidual)
            / cpl_vector_get_size(fitresidual);
    }
    cpl_matrix_delete(samppos2d);
    cpl_vector_delete(fitresidual);
    return fit2d;
}

cpl_polynomial * 
xsh_polynomial_fit_1d_create(
			       const cpl_vector    *   x_pos,
			       const cpl_vector    *   values,
			       int                     degree,
			       double              *   mse
			       )
{
    cpl_polynomial * fit1d = cpl_polynomial_new(1);
//    cpl_vector* x_copy = cpl_vector_duplicate(x_pos);
//    cpl_vector* values_copy = cpl_vector_duplicate(values);
    int x_size = cpl_vector_get_size(x_pos);
    double rechisq = 0;
    cpl_size loc_deg=(cpl_size)degree;
    cpl_matrix     * samppos = cpl_matrix_wrap(1, x_size,
                                               (double*)cpl_vector_get_data_const(x_pos));
    cpl_vector     * fitresidual = cpl_vector_new(x_size);

    cpl_polynomial_fit(fit1d, samppos, NULL, values, NULL,
                       CPL_FALSE, NULL, &loc_deg);
    cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), NULL);
    
    if ( x_size > (degree + 1) )  {  
      cpl_vector_fill_polynomial_fit_residual(fitresidual, values, NULL, fit1d,
					      samppos, &rechisq);
      cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), NULL);
    }
    if (mse)  
    {
        *mse = cpl_vector_product(fitresidual, fitresidual)
            / cpl_vector_get_size(fitresidual);
    }
    cpl_matrix_unwrap(samppos);
    cpl_vector_delete(fitresidual);
    return fit1d;
}


cpl_image * 
xsh_image_filter_median(const cpl_image * img, const cpl_matrix * mx)
{
	return xsh_image_filter_wrapper(img, mx, CPL_FILTER_MEDIAN);
}

cpl_image * 
xsh_image_filter_linear(const cpl_image *img, const cpl_matrix * mx)
{
	return xsh_image_filter_mode(img, mx, CPL_FILTER_LINEAR);

}

/*---------------------------------------------------------------------------*/
/**@}*/
