/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004,2005 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include "sinfo_utilities.h"
#include "sinfo_error.h"
#include "sinfo_dfs.h"
#include "sinfo_utils_wrappers.h"
#include "sinfo_function_1d.h"
/**@{*/
/**
 * @defgroup sinfo_utilities utilities
 *
 * TBD
 */



cpl_error_code
sinfo_ima_line_cor(cpl_parameterlist * parlist, cpl_frameset* in)
{
    int n=0;
    int i=0;
    int kappa=18;
    int filt_rad=3;
    int width=4;

    cpl_frame* frm=NULL;
    const char* name=NULL;
    const char* bname=NULL;

    cpl_image * ima=NULL ;
    cpl_image * ima_out=NULL ;
    cpl_parameter* p=NULL;
    cpl_propertylist* plist=NULL;
    char* tag=NULL;
    char name_deb[80];


    check_nomsg(p=cpl_parameterlist_find(parlist, "sinfoni.general.lc_kappa"));
    check_nomsg(kappa=cpl_parameter_get_int(p));
    check_nomsg(p=cpl_parameterlist_find(parlist,
                    "sinfoni.general.lc_filt_rad"));
    check_nomsg(filt_rad = cpl_parameter_get_int(p)) ;

    n=cpl_frameset_get_size(in);

    for(i=0;i<n;i++) {
        check_nomsg(frm=cpl_frameset_get_frame(in,i));
        tag= (char*) cpl_frame_get_tag(frm);
        if(sinfo_frame_is_raw(tag) == 1) {
            check_nomsg(name=cpl_frame_get_filename(frm));

            bname=sinfo_new_get_basename(name);
            check_nomsg(ima=cpl_image_load(name,CPL_TYPE_FLOAT,0,0));
            check_nomsg(sinfo_image_line_corr(width,filt_rad,kappa,ima,
                            &ima_out));
            check_nomsg(plist=cpl_propertylist_load(name,0));
            //sprintf(name_deb,"dark_org_%d.fits",i);
            sprintf(name_deb,"org_%2.2d_%s",i,bname);

            check(cpl_image_save(ima,
                            name_deb,
                            CPL_BPP_IEEE_FLOAT,
                            plist,
                            CPL_IO_DEFAULT),
                  "Could not save product");


            check(cpl_image_save(ima_out,
                            bname,
                            CPL_BPP_IEEE_FLOAT,
                            plist,
                            CPL_IO_DEFAULT),
                  "Could not save product");

            cpl_frame_set_filename(frm,bname);
            sinfo_free_image(&ima);
            sinfo_free_propertylist(&plist);
        }

    }

    cleanup:
    sinfo_free_image(&ima);
    sinfo_free_propertylist(&plist);

    return cpl_error_get_code();

}

int sinfo_new_intarray_get_value( int * array, int i )
{
    return array[i] ;
}
/**
@name sinfo_table_shift_column_spline3
@param shift a table of an double shift
@param t input table
@param col input table's column
@param shift shift to be applied
@return pointer to a new allocated shifted table
 */
/* TODO: not used */
cpl_table*
sinfo_table_shift_column_spline3(cpl_table* t,
                                 const char* col,
                                 const double shift)
{
    cpl_table* out=NULL;
    int nrow=0;
    int i=0;
    int z=0;

    float sum=0;
    float new_sum=0;

    float* pi=NULL;
    float* po=NULL;
    float* eval=NULL;
    float* xnum=NULL;
    float* spec=NULL;
    float* corrected_spec=NULL;

    cknull(t,"null input table");
    out=cpl_table_duplicate(t);

    nrow=cpl_table_get_nrow(t);
    check_nomsg(cpl_table_cast_column(t,col,"FINT",CPL_TYPE_FLOAT));
    check_nomsg(cpl_table_cast_column(out,col,"FINT",CPL_TYPE_FLOAT));
    pi=cpl_table_get_data_float(t,"FINT");
    po=cpl_table_get_data_float(out,"FINT");



    xnum=cpl_calloc(nrow,sizeof(float)) ;
    /* fill the xa[] array for the spline function */
    for ( i = 0 ; i < nrow ; i++ ) {
        xnum[i] = i ;
    }

    spec=cpl_calloc(nrow,sizeof(float)) ;
    corrected_spec=cpl_calloc(nrow,sizeof(float)) ;
    eval=cpl_calloc(nrow,sizeof(float)) ;

    sum = 0. ;
    for ( z = 0 ; z < nrow ; z++ ) {
        spec[z] = pi[z] ;
        if (isnan(spec[z]) ) {
            for ( i = z-1 ; i <= z+1 ; i++ ) {
                if ( i < 0 ) continue ;
                if ( i >= nrow) continue ;
                corrected_spec[i] = ZERO ;
            }
            spec[z] = 0. ;
        }
        sum += spec[z] ;
        eval[z] = (float)shift+(float)z ;
    }
    /* now we do the spline interpolation*/
    if ( -1 == sinfo_function1d_natural_spline(xnum,spec, nrow,
                    eval,corrected_spec, nrow))
    {
        sinfo_msg_error("error in spline interpolation!") ;
        goto cleanup;
    }

    new_sum = 0. ;
    for ( z = 0 ; z < nrow ; z++ ) {
        if ( isnan(corrected_spec[z]) ) {
            continue ;
        }
        new_sum += corrected_spec[z] ;
    }
    /* fill output imagelist */
    for ( z = 0 ; z < nrow ; z++ ) {
        if ( new_sum == 0. ) new_sum =1. ;
        {
            if ( isnan(corrected_spec[z]) ) {
                po[z] = ZERO ;
            } else {
                corrected_spec[z] *= sum / new_sum ;
                po[z] = corrected_spec[z] ;
            }
        }
    }

    sinfo_free_float(&xnum);
    sinfo_free_float(&spec) ;
    sinfo_free_float(&corrected_spec) ;
    sinfo_free_float(&eval) ;

    check_nomsg(cpl_table_erase_column(t,"FINT"));
    check_nomsg(cpl_table_erase_column(out,col));
    check_nomsg(cpl_table_cast_column(out,"FINT",col,CPL_TYPE_DOUBLE));
    check_nomsg(cpl_table_erase_column(out,"FINT"));

    return out;
    cleanup:

    sinfo_free_float(&xnum);
    sinfo_free_float(&spec) ;
    sinfo_free_float(&corrected_spec) ;
    sinfo_free_float(&eval) ;
    sinfo_free_table(&out);
    return NULL;


}


/**
@name sinfo_table_shift_column_int
@param shift a table of an integer step
@param t input table
@param col input table's column
@param s shift to be applied
@param r shift rest
@return pointer to a new allocated shifted table
 */
/* TODO: not used */
cpl_table*
sinfo_table_shift_column_int(const cpl_table* t,
                             const char* col,
                             const double s,
                             double* r)
{
    cpl_table* out=NULL;
    int is=(int)s;
    int nrow=0;
    int i=0;

    const double* pi=NULL;
    double* po=NULL;

    cknull(t,"null input table");
    out=cpl_table_duplicate(t);
    *r=s-is;
    nrow=cpl_table_get_nrow(t);
    pi=cpl_table_get_data_double_const(t,col);
    po=cpl_table_get_data_double(out,col);
    for(i=0;i<nrow;i++) {
        if( ((i-is) >=0) && ((i-is) < nrow)) {
            po[i-is]=pi[i];
        }
    }
    return out;
    cleanup:
    sinfo_free_table(&out);
    return NULL;

}


/**
@name sinfo_table_shift_column_poly
@param shift a table of an double shift
@param t input table
@param col input table's column
@param shift shift to be applied
@param order polynomial order
@return pointer to a new allocated shifted table
 */
/* TODO: not used */
cpl_table*
sinfo_table_shift_column_poly(cpl_table* t,
                              const char* col,
                              const double shift,
                              const int order)
{
    cpl_table* out=NULL;
    int nrow=0;
    int i=0;
    int flag=0;
    int n_points=0;
    int firstpos=0;
    int z=0;
    float eval=0;
    float sum=0;
    float new_sum=0;
    float* pi=NULL;
    float* po=NULL;
    float* spec=NULL ;
    float* corrected_spec=NULL ;
    float* xnum=NULL ;
    float* tableptr=NULL;

    cknull(t,"null input table");
    if ( order <= 0 ) {
        sinfo_msg_error("wrong order of interpolation polynom given!") ;
        goto cleanup;
    }

    out=cpl_table_duplicate(t);

    nrow=cpl_table_get_nrow(t);
    cpl_table_cast_column(t,col,"FINT",CPL_TYPE_FLOAT);
    cpl_table_cast_column(out,col,"FINT",CPL_TYPE_FLOAT);
    pi=cpl_table_get_data_float(t,"FINT");
    po=cpl_table_get_data_float(out,"FINT");

    n_points = order + 1 ;
    if ( n_points % 2 == 0 ) {
        firstpos = (int)(n_points/2) - 1 ;
    } else {
        firstpos = (int)(n_points/2) ;
    }
    spec=cpl_calloc(nrow,sizeof(float)) ;
    corrected_spec=cpl_calloc(nrow,sizeof(float)) ;
    xnum=cpl_calloc(order+1,sizeof(float)) ;
    /* fill the xa[] array for the polint function */
    for ( i = 0 ; i < n_points ; i++ ) {
        xnum[i] = i ;
    }


    for(i=0;i<nrow;i++) {
        corrected_spec[i] = 0. ;
    }

    sum = 0. ;
    for ( z = 0 ; z < nrow ; z++ ) {
        spec[z] = pi[z] ;
        if (isnan(spec[z]) ) {
            spec[z] = 0. ;

            for ( i = z - firstpos ; i < z-firstpos+n_points ; i++ ) {
                if ( i < 0 ) continue ;
                if ( i >= nrow) continue  ;
                corrected_spec[i] = ZERO ;
            }
        }
        if ( z != 0 && z != nrow - 1 ) {
            sum += spec[z] ;
        }
    }

    new_sum = 0. ;
    for ( z = 0 ; z < nrow ; z++ ) {
        /* ---------------------------------------------------------------
         * now determine the arrays of size n_points with which the
         * polynom is determined and determine the position eval
         * where the polynom is evaluated in polynomial interpolation.
         * Take care of the points near the row edges!
         */
        if (isnan(corrected_spec[z])) continue ;
        if ( z - firstpos < 0 ) {
            tableptr = &spec[0] ;
            eval     = shift + z ;
        } else if ( z - firstpos + n_points >= nrow ) {
            tableptr = &spec[nrow - n_points] ;
            eval     = shift + z + n_points - nrow ;
        } else {
            tableptr = &spec[z-firstpos] ;
            eval     = shift + firstpos ;
        }

        flag=0;
        corrected_spec[z]=sinfo_new_nev_ille(xnum,tableptr,order,eval,&flag);
        if ( z != 0 && z != nrow - 1 ) {
            new_sum += corrected_spec[z] ;
        }
    }

    /* fill the output spectrum */
    for (z = 0 ; z < nrow ; z++ ) {
        if ( new_sum == 0. ) {
            new_sum = 1. ;
        }
        if ( z == 0 ) {
            po[z] = ZERO ;
        } else if ( z == nrow - 1 ) {
            po[z] = ZERO ;
        } else if ( isnan(corrected_spec[z]) ) {
            po[z] = ZERO ;
        } else {
            corrected_spec[z] *= sum / new_sum ;
            po[z] = corrected_spec[z] ;
        }
    }
    check_nomsg(cpl_table_erase_column(t,"FINT"));
    check_nomsg(cpl_table_erase_column(out,col));
    check_nomsg(cpl_table_cast_column(out,"FINT",col,CPL_TYPE_DOUBLE));
    check_nomsg(cpl_table_erase_column(out,"FINT"));

    sinfo_free_float(&spec) ;
    sinfo_free_float(&corrected_spec) ;
    sinfo_free_float(&xnum) ;

    return out;
    cleanup:


    sinfo_free_float(&spec) ;
    sinfo_free_float(&corrected_spec) ;
    sinfo_free_float(&xnum) ;
    sinfo_free_table(&out);
    return NULL;


}




void sinfo_new_array_set_value( float * array, float value, int i )
{
    array[i] = value ;
}
float sinfo_new_array_get_value( float * array, int i )
{
    return array[i] ;
}



void sinfo_new_destroy_array(float ** array)
{
    if(*array != NULL) {
        cpl_free( *array ) ;
        *array = NULL;
    }
}

void sinfo_new_destroy_2Dintarray(int *** array, int size_x)
{

    if((*array) != NULL) {
        for ( int i = 0 ; i < size_x ; i++ ) {
            if((*array)[i] != NULL) {
                cpl_free( (*array)[i] );
                (*array)[i]=NULL;
            }
        }
        cpl_free( *array ) ;
        *array=NULL;
    }

}


void sinfo_new_intarray_set_value( int * array, int value, int i )
{
    array[i] = value ;
}
float sinfo_new_array2D_get_value( float ** array, int x, int y )
{
    return array[x][y] ;
}
int ** sinfo_new_2Dintarray( int size_x, int size_y)
{
    int ** retVal ;
    int i ;

    retVal = (int **) cpl_calloc( size_x, sizeof (int*) ) ;
    for ( i = 0 ; i < size_x ; i++ )
    {
        retVal[i] = (int *) cpl_calloc( size_y, sizeof (int)) ;
    }
    return retVal ;
}

float * sinfo_new_floatarray( int size)
{
    return (float *) cpl_calloc( size, sizeof (float) ) ;
}


void sinfo_new_destroy_2Dfloatarray(float *** array, int size_x)
{
    if((*array) != NULL) {
        for ( int i = 0 ; i < size_x ; i++ ) {
            if((*array)[i] != NULL) {
                cpl_free( (*array)[i] );
                (*array)[i]=NULL;
            }
        }
        cpl_free( *array ) ;
        *array=NULL;
    }
}

void sinfo_new_destroy_2Ddoublearray(double *** array, int size_x)
{

    if((*array) != NULL) {
        for ( int i = 0 ; i < size_x ; i++ ) {
            if((*array)[i] != NULL) {
                cpl_free( (*array)[i] );
                (*array)[i]=NULL;
            }
        }
        cpl_free( *array ) ;
        *array=NULL;
    }

}


void sinfo_new_array2D_set_value(float ** array,float value,int x,int y)
{
    array[x][y] = value ;
}

double sinfo_new_doublearray_get_value( double * array, int i )
{
    return array[i] ;
}
void sinfo_new_doublearray_set_value( double * array, double value, int i )
{
    array[i] = value ;
}

void sinfo_new_destroy_doublearray(double * array)
{
    cpl_free( array ) ;
}
double * sinfo_new_doublearray( int size)
{
    return (double *) cpl_calloc( size, sizeof (double) ) ;
}

double ** sinfo_new_2Ddoublearray( int size_x, int size_y)
{
    double ** retVal ;
    int i ;

    retVal = (double **) cpl_calloc( size_x, sizeof (double*) ) ;
    for ( i = 0 ; i < size_x ; i++ )
    {
        retVal[i] = (double *) cpl_calloc( size_y, sizeof (double)) ;
    }
    return retVal ;
}

float ** sinfo_new_2Dfloatarray( int size_x, int size_y)
{
    float ** retVal ;
    int i ;

    retVal = (float **) cpl_calloc( size_x, sizeof (float*) ) ;
    for ( i = 0 ; i < size_x ; i++ )
    {
        retVal[i] = (float *) cpl_calloc( size_y, sizeof (float)) ;
    }
    return retVal ;
}


int * sinfo_new_intarray( int size)
{
    return (int *) cpl_calloc( size, sizeof (int) ) ;
}
void sinfo_new_destroy_intarray(int ** array)
{
    cpl_free( *array ) ;
    *array=NULL;
}

char * sinfo_new_get_basename(const char *filename)
{
    char *p ;
    p = strrchr (filename, '/');
    return p ? p + 1 : (char *) filename;
}



char * sinfo_new_get_rootname(const char * filename)
{
    static char path[MAX_NAME_SIZE+1];
    char * lastdot ;

    if (strlen(filename)>MAX_NAME_SIZE) return NULL ;
    memset(path, MAX_NAME_SIZE, 0);
    strcpy(path, filename);
    lastdot = strrchr(path, '.');
    if (lastdot == NULL) return path ;
if ((!strcmp(lastdot, ".fits")) || (!strcmp(lastdot, ".FITS"))
                || (!strcmp(lastdot, ".paf")) || (!strcmp(lastdot, ".PAF"))
                || (!strcmp(lastdot, ".dat")) || (!strcmp(lastdot, ".DAT"))
                || (!strcmp(lastdot, ".fits")) || (!strcmp(lastdot, ".TFITS"))
                || (!strcmp(lastdot, ".ascii"))
                || (!strcmp(lastdot, ".ASCII")))
    {
        lastdot[0] = (char)0;
    }
    return path ;
}



/*----------------------------------------------------------------------------*/
/**
  @brief    Convert a NACO frameset to an images set
  @param    fset    input frames set
  @return   the newly allocated images set or NULL in error case

  The first plane of the primary unit of each frame is loaded
 */
/*----------------------------------------------------------------------------*/
cpl_imagelist * sinfo_new_frameset_to_iset(cpl_frameset * fset)
{
    cpl_imagelist   *   iset=NULL ;
    char        **  filenames ;
    int             nfiles=0 ;

    /* Test entries */
    if (fset == NULL) return NULL ;

    /* Get the filenames */
    if ((filenames = sinfo_new_frameset_to_filenames(fset, &nfiles)) == NULL) {
        sinfo_msg_error( "Cannot get the files names") ;
        return NULL ;
    }
    /* Load image set */
    if ((iset = sinfo_new_imagelist_load_frameset(fset,
                    CPL_TYPE_FLOAT, 0, 0)) == NULL) {
        sinfo_msg_error( "Cannot load *** the image set") ;
        sinfo_msg_error("%s", (char* ) cpl_error_get_message());

        cpl_free(filenames) ;
        return NULL ;
    }

    /* Free and Return  */
    cpl_free(filenames) ;
    return iset ;
}
#include "cpl_imagelist_io.h"

/*----------------------------------------------------------------------------*/
/**
  @brief    Load an imagelist from a frameset
  @param    frameset  A frameset containing name(s) of image FITS files
  @param    type      cpl_type
  @param    pnum      The plane (FITS NAXIS3) number (0 for all planes)
  @param    extnum    The required FITS extension (-1 for all)
  @return   The newly created imagelist or NULL on error
  @see      cpl_imset_load()

 */
/*----------------------------------------------------------------------------*/
cpl_imagelist *
sinfo_new_imagelist_load_frameset(const cpl_frameset * frameset,
                                  cpl_type type,
                                  int pnum,
                                  int extnum)
{
    cpl_image  * image     = NULL;
    cpl_imagelist  * imagelist = NULL;

    cpl_frameset_iterator* it = cpl_frameset_iterator_new(frameset);
    const cpl_frame *frame = cpl_frameset_iterator_get_const(it);

    const int nz = cpl_frameset_get_size(frameset);
    int       i;

    /* Require imagelist to contain at least one image */
    cpl_ensure(nz > 0, CPL_ERROR_DATA_NOT_FOUND, NULL);

    for (i = 0; i<nz; i++) {

        const char * name = cpl_frame_get_filename(frame);
        if (name == NULL) break; /* Error check */


        image = cpl_image_load(name, type, pnum, extnum);

        if (image == NULL) break; /* Error check */

        if (i == 0) {
            const int nx = cpl_image_get_size_x(image);
            const int ny = cpl_image_get_size_y(image);

            if (nx < 1 || ny < 1) break; /* Error check */
            imagelist = cpl_imagelist_new();
            if (imagelist == NULL) break; /* Error check */
        }

        if (cpl_imagelist_set(imagelist, image, i)) break;
        image = NULL; /* Image is now part of the imagelist */
        cpl_frameset_iterator_advance(it, 1);
        frame = cpl_frameset_iterator_get_const(it);

    }

    if (i != nz) {
        /* Error handling */
        cpl_image_delete(image);
        cpl_imagelist_delete(imagelist);
        imagelist = NULL;
    }
    cpl_frameset_iterator_delete(it);
    return imagelist;

}

/**
 * @brief
 *   Get the list of filenames from a cpl_frameset
 *
 * @param set     input frame set
 * @param nfiles  the number of file names returned
 *
 * @return the newly allocated list of filenames
 */

char ** sinfo_new_frameset_to_filenames(cpl_frameset *set, int *nfiles)
{
    char **filenames=NULL;

    int nbframes=0;
    int i=0;

    //cpl_frame *curr_frame;

    if (set == NULL) {
        return NULL;
    }

    nbframes = cpl_frameset_get_size(set);

    if (nbframes < 1) {
        return NULL;
    }
    /*
     * Create the list of filenames and fill it
     */
    filenames = cpl_malloc(nbframes * sizeof(char *));

    cpl_frameset_iterator* it = cpl_frameset_iterator_new(set);
    const cpl_frame *curr_frame = cpl_frameset_iterator_get(it);

    for (i = 0; i < nbframes; i++) {
        filenames[i]=(char*) cpl_frame_get_filename(curr_frame);

        cpl_frameset_iterator_advance(it, 1);
        curr_frame = cpl_frameset_iterator_get_const(it);
    }
    cpl_frameset_iterator_delete(it);

    /*
     * Set the number of files found
     */
    *nfiles = nbframes;

    return filenames;

}


/*---------------------------------------------------------------------------*/
/**
   @brief        Spline interpolation based on Hermite polynomials
   @param xp     x-value to interpolate
   @param x      x-values
   @param y      y-values
   @param n      array length
   @param istart    (input/output) initial row (set to 0 to search all row)

   @return The interpolated value.

   The x column must be sorted (ascending or descending) and all x column
   values must be different.

   Adopted from: Cristian Levin - ESO La Silla, 1-Apr-1991
 */
/*----------------------------------------------------------------------------*/
/* TODO: not used */
double sinfo_spline_hermite(double xp,
                            const double *x,
                            const double *y,
                            int n,
                            int *istart )
{
    double yp1, yp2, yp = 0;
    double xpi, xpi1, l1, l2, lp1, lp2;
    int i;

    if ( x[0] <= x[n-1] && (xp < x[0] || xp > x[n-1]) )    return 0.0;
    if ( x[0] >  x[n-1] && (xp > x[0] || xp < x[n-1]) )    return 0.0;

    if ( x[0] <= x[n-1] )
    {
        for ( i = (*istart)+1; i <= n && xp >= x[i-1]; i++ )
            ;
    }
    else
    {
        for ( i = (*istart)+1; i <= n && xp <= x[i-1]; i++ )
            ;
    }

    *istart = i;
    i--;

    lp1 = 1.0 / (x[i-1] - x[i]);
    lp2 = -lp1;

    if ( i == 1 )
    {
        yp1 = (y[1] - y[0]) / (x[1] - x[0]);
    }
    else
    {
        yp1 = (y[i] - y[i-2]) / (x[i] - x[i-2]);
    }

    if ( i >= n - 1 )
    {
        yp2 = (y[n-1] - y[n-2]) / (x[n-1] - x[n-2]);
    }
    else
    {
        yp2 = (y[i+1] - y[i-1]) / (x[i+1] - x[i-1]);
    }

    xpi1 = xp - x[i];
    xpi  = xp - x[i-1];
    l1   = xpi1*lp1;
    l2   = xpi*lp2;

    yp = y[i-1]*(1 - 2.0*lp1*xpi)*l1*l1 +
                    y[i]*(1 - 2.0*lp2*xpi1)*l2*l2 +
                    yp1*xpi*l1*l1 + yp2*xpi1*l2*l2;

    return yp;
}

/**
  @brief    Update the bad pixel map for the image - mark all pixels with NaN
            value as a bad
  @param    im             Image with pixel-type float or double
  @return   0 iff ok
 */
cpl_error_code update_bad_pixel_map(cpl_image* im)
{
    int szx = cpl_image_get_size_x(im);
    int szy = cpl_image_get_size_y(im);
    int x = 0;
    cpl_mask* bpm = cpl_image_get_bpm(im);

    for (x = 1; x <=szx; x++)
    {
        int y = 0;
        for(y = 1; y <= szy; y++)
        {
            int isnull = 0;
            double value = cpl_image_get(im, x, y, &isnull);
            if (isnan(value))
            {
                cpl_mask_set(bpm, x, y, CPL_BINARY_1);
            }
        }
    }
    return cpl_error_get_code();
}
cpl_polynomial * sinfo_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 * sinfo_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);
    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 * sinfo_image_filter_
static cpl_image * sinfo_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);
    cpl_msg_warning(cpl_func, "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;
}


static cpl_image*
sinfo_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_nomsg(cpl_image_filter(a, b, ker, CPL_FILTER_MEDIAN,
                        CPL_BORDER_FILTER));
        break;
    case CPL_FILTER_LINEAR:
        check_nomsg(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:
        sinfo_msg_error("Filter type not supported");
        return NULL;
    }
    cleanup:

    return a;

}

cpl_image * 
sinfo_image_filter_linear(const cpl_image *img, const cpl_matrix * mx)
{
    return sinfo_image_filter_mode(img, mx, CPL_FILTER_LINEAR);

}



cpl_image * sinfo_image_filter_median(const cpl_image * img,
                                      const cpl_matrix * mx)
{
    return sinfo_image_filter_wrapper(img, mx, CPL_FILTER_MEDIAN);
}


/**@}*/
