/*
 * 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
 */
/*******************************************************************************
* E.S.O. - VLT project
*
*
*
* who       when      what
* --------  --------  ----------------------------------------------
* schreib  19/12/00  created
*/

/************************************************************************
*   NAME
*        sinfo_coltilt.c -
*        procedures to correct for tilted spectra 
*
*   SYNOPSIS
*   1) float sinfo_new_slope_of_spectrum(cpl_image * ns_image,
*                             int        box_length,
*                             float      fwhm,
*                             float      minDiff )
*   2) cpl_image * sinfo_new_shift_rows(cpl_image * image,
*                            float      slope )
*   3) void sinfo_new_parameter_to_ascii ( float * parameter, 
*                              int n,
*                              char * filename )
*   4) float * sinfo_new_ascii_to_parameter ( char * filename,
*                                 int * n )
*   5) double * sinfo_new_curvature_of_spectrum( cpl_image * ns_image,
*                              int        order,
*                                    int        box_length,
*                                    int        left_pos,
*                                    int        right_pos,
*                                    float      fwhm,
*                                    float      minDiff )
*
*
*   DESCRIPTION
*   1)  determines the sub-pixel shifts of each row by using
*       an image with at least one continuum spectrum of a pinhole
*       this is done by searching the spectrum within the image
*       then fitting the spectrum along the rows within a given box
*       by a sinfo_gaussian, so that the exact position is determined for
*       each row. Afterwards, a straight line is fitted through the
*       fitted positions. The slope of this linear fit is returned.
*   2)  shifts the rows of a raw image by using the output of 
*       sinfo_slopeOfSpectrum and applying polynomial interpolation
*   3)  stores parameters in an ASCII file 
*   4)  writes parameters stored in an ASCII file in an float array
*   5)  this routine determines the curvature of a spectrum by fitting
*       a polynomial to a continuum spectrum. This is done by using
*       an image with at least one continuum spectrum of a pinhole.
*       this is done by searching the spectrum within the image
*       then fitting the spectrum along the rows within a given box
*       by a sinfo_gaussian, so that the exact position is determined for
*       each row. Afterwards, a polynomial is fitted through the
*       found positions. The polynomial coefficients are returned.
*
*
*   FILES
*
*   ENVIRONMENT
*
*   RETURN VALUES
*
*   CAUTIONS
*
*   EXAMPLES
*
*   SEE ALSO
*
*   BUGS
*
*------------------------------------------------------------------------
*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#define POSIX_SOURCE 1
#include "sinfo_vltPort.h"

/*
 * System Headers
 */

/*
 * Local Headers
 */

#include "sinfo_coltilt.h"
#include "sinfo_new_resampling.h"
#include "sinfo_fit_curve.h"
#include "sinfo_functions.h"
/**@{*/
/**
 * @defgroup sinfo_coltilt Column tilt computation
 *
 * TBD
 */

/*----------------------------------------------------------------------------
 *                            Function codes
 *--------------------------------------------------------------------------*/

/**
   @name   sinfo_new_slope_of_spectrum()
   @memo determines the sub-pixel shifts of each row
   @param  ns_image   image with at least one continuum spectrum of a pinhole
   @param  box_length width of the box in which the lines are fit by a Gaussian
   @param  fwhm       first guess of the full width at half maximum
   @param  min_amplitude_factor factor peak/background below given threshold 
           the fit is not carried through
   @return slope of a straight line fitted to the spectrum. -FLT_MAX if 
           something went wrong.
   @doc    determines the sub-pixel shifts of each row by using an image with 
           at least one continuum spectrum of a pinhole this is done by 
           searching the spectrum within the image then fitting the spectrum 
           along the rows within a given box by a Gaussian, so that the exact 
           position is determined for each row. Afterwards, a straight line is 
           fitted through the fitted positions. The slope of this linear fit 
           is returned.

  */

float 
sinfo_new_slope_of_spectrum( cpl_image * ns_image,
                       int        box_length,
                       float      fwhm,
                       float      minDiff )
{
    int i, k, row, col ;
    int counter, iters ;
    int xdim, ndat, its, numpar ;
    float maxval ;
    float tol, lab ;
    float* col_value=NULL ;
    float* column_value=NULL ;
    pixelvalue* col_position=NULL ;
    int* column_position=NULL ;
    float* x_position=NULL ;


    int col_median ;
    float * xdat, * wdat ;
    int * mpar ;
    Vector * line ;
    FitParams ** dec_par ;
    FitParams *  par ;

    float* x=NULL; 
    float* y=NULL; 
    float* sig=NULL ;

    int position ;
    int ndata, mwt ;
    float a, b, siga, sigb, chi2, q ;
    int bad_ind ;
    int lx=0;
    int ly=0;
    float* pdata=NULL;

    if ( ns_image == NULL )
    {
        sinfo_msg_error(" sorry, no image given") ;
        return FLAG ;
    }
    lx=cpl_image_get_size_x(ns_image);
    ly=cpl_image_get_size_x(ns_image);


    if ( box_length <= 1 || box_length >= (int) sqrt(lx) )
    {
        sinfo_msg_error(" wrong box length given") ;
        return FLAG ;
    }
    if ( fwhm < 1. || fwhm > 10. )
    {
        sinfo_msg_error("wrong full width at half maximum given") ;
        return FLAG ;
    }
    if ( minDiff < 1. )
    {
        sinfo_msg_error("wrong amplitude threshold given") ;
        return FLAG ;
    }



    col_value=cpl_calloc(ly,sizeof(float)) ;
    column_value=cpl_calloc(ly,sizeof(float)) ;
    col_position=(pixelvalue*)cpl_calloc(ly,sizeof(pixelvalue)) ;
    column_position=cpl_calloc(ly,sizeof(int)) ;
    x_position=cpl_calloc(ly,sizeof(float)) ;



    x=cpl_calloc(lx,sizeof(float)); 
    y=cpl_calloc(lx,sizeof(float)); 
    sig=cpl_calloc(lx,sizeof(float)) ;
    pdata=cpl_image_get_data_float(ns_image);
    /* go through the image rows */
    for ( row = 0 ; row < ly ; row++ )
    {
        col_value[row] = -FLT_MAX ;
        col_position[row] = -1. ;
        /* find the maximum value in each row and store the found column */
        for ( col = 0 ; col < lx ; col++ )
        {
            if ( pdata[col+row*lx] > col_value[row] )
            {
                col_value[row]    = pdata[col+row*lx] ;
                col_position[row] = (pixelvalue)col ;
            }
        }
    }

    /* now determine the sinfo_new_median of the found columns to be sure 
       to have the brightest spectrum */
    col_median = (int)sinfo_new_median(col_position, ly) ;
    sinfo_msg ("sinfo_new_median column position of brightest spectrum %d\n", 
              col_median) ;

    /* now find the peaks around col_median over the whole spectral range */
    for ( row = 0 ; row < ly ; row++ )
    {
        x_position[row] = 0. ;
        column_value[row] = -FLT_MAX ;
        column_position[row] = -1 ;
        for ( col = col_median - box_length ; 
              col <= col_median + box_length ; col++ )
        {
            if ( pdata[col+row*lx] > column_value[row] )
            {
                column_value[row] = pdata[col+row*lx] ; 
                column_position[row] = col ;
            }
        }

        /* allocate memory for the array where the line is fitted in */
        if ( NULL == (line = sinfo_new_vector (2*box_length + 1)) )
        {
            sinfo_msg_error ("cannot allocate new Vector in row %d", row) ;
            return FLAG ;
        }

        /* allocate memory */
        xdat = (float *) cpl_calloc( line -> n_elements, sizeof (float) ) ;
        wdat = (float *) cpl_calloc( line -> n_elements, sizeof (float) ) ;
        mpar = (int *)   cpl_calloc( MAXPAR, sizeof (int) ) ;
        dec_par = sinfo_new_fit_params(1) ;
    par = dec_par[0];

        counter = 0 ;
        bad_ind = 0 ;
        /* store the values to fit in a Vector object */
        for ( col = column_position[row] - box_length ; 
              col <= column_position[row] + box_length ; col++ )
        {
            if ( col < 0 || col >= lx )
            {
                sinfo_msg_error ("wrong spectrum position or box_length "
                                 "given in row: %d", row) ;
                cpl_free (xdat) ;
                cpl_free (wdat) ;
                cpl_free (mpar) ;
                sinfo_new_destroy_fit_params(&dec_par) ;
                sinfo_new_destroy_vector( line ) ;
                return FLAG ;
            }
            else if ( isnan(pdata[col+row*lx]) )
            {
                bad_ind = 1 ;
            }
            else
            {
                line -> data[counter] = pdata[col + row*lx] ;
                counter++ ;
            }
        }
 
        /* go to the next row if a bad pixel is inside the box */
        if ( bad_ind == 1 )
        {
            sinfo_msg_warning ("sorry, bad pixel inside fitting "
                               "box in row: %d", row) ;
            cpl_free (xdat) ;
            cpl_free (wdat) ;
            cpl_free (mpar) ;
            sinfo_new_destroy_fit_params(&dec_par) ;
            sinfo_new_destroy_vector( line ) ;
            continue ;
        }

        /*--------------------------------------------------------------------
         * go through the line sinfo_vector
         * determine the maximum pixel value in the line sinfo_vector
         */
        maxval = -FLT_MAX ;
        position = -INT32_MAX ;
        for ( i = 0 ; i < counter ; i++ )
        {
            xdat[i] = i ;
            wdat[i] = 1.0 ;
            if ( line -> data[i] >= maxval )
            {
                maxval = line -> data[i] ;
                position = i ;
            }
        }

        /* set initial values for the fitting routine */
        xdim     = XDIM ;
        ndat     = line -> n_elements ;
        numpar   = MAXPAR ;
        tol      = TOL ;
        lab      = LAB ;
        its      = ITS ;
        (*par).fit_par[1] = fwhm ;
        (*par).fit_par[2] = (float) position ;
        (*par).fit_par[3] = (float) (line -> data[0] + 
                            line -> data[line->n_elements - 1]) / 2.0 ;

        (*par).fit_par[0]  = maxval - ((*par).fit_par[3]) ;
        /* exclude negative peaks and low signal cases */
        if ( (*par).fit_par[0] < minDiff )
        {
            sinfo_msg_warning ("sorry, negative peak or signal of line "
                               "too low to fit in row: %d", row) ;
            cpl_free (xdat) ;
            cpl_free (wdat) ;
            cpl_free (mpar) ;
            sinfo_new_destroy_fit_params(&dec_par) ;
            sinfo_new_destroy_vector( line ) ;
            continue ;
        }

        for ( k = 0 ; k < MAXPAR ; k++ )
        {
            (*par).derv_par[k] = 0.0 ;
            mpar[k] = 1 ;
        }

        /* finally, do the least square fit using a sinfo_gaussian */
        if ( 0 > ( iters = sinfo_new_lsqfit_c( xdat, &xdim, 
                                               line -> data, wdat, 
                                               &ndat, (*par).fit_par,
                                               (*par).derv_par, mpar, 
                                               &numpar, &tol, &its, &lab )) )
        {
            sinfo_msg_warning ("sinfo_new_lsqfit_c: least squares fit "
                               "failed in row: %d, error no.: %d",row,iters) ;
            cpl_free (xdat) ;
            cpl_free (wdat) ;
            cpl_free (mpar) ;
            sinfo_new_destroy_fit_params(&dec_par) ;
            sinfo_new_destroy_vector( line ) ;
            continue ;
        }

        /* check for negative fit results */
        if ( (*par).fit_par[0] <= 0. || (*par).fit_par[1] <= 0. ||
             (*par).fit_par[2] <= 0. )
        {
            sinfo_msg_warning ("negative parameters as fit result, "
                               "not used! in row: %d", row) ;
            cpl_free (xdat) ;
            cpl_free (wdat) ;
            cpl_free (mpar) ;
            sinfo_new_destroy_fit_params(&dec_par) ;
            sinfo_new_destroy_vector( line ) ;
            continue ;
        }

        /* correct the fitted position for the given row of the line in 
           image coordinates */
        x_position[row] =  (float) (column_position[row] - box_length) + 
                           (*par).fit_par[2] ;

        /* store the fit errors of the positions as weights for the later 
           linear fit */
        sig[row] = (*par).derv_par[2] ;

        /* free memory */
        sinfo_new_destroy_fit_params(&dec_par) ;
        sinfo_new_destroy_vector ( line ) ;
        cpl_free ( xdat ) ;
        cpl_free ( wdat ) ;
        cpl_free ( mpar ) ;
    }

    /* -----------------------------------------------------------------------
     * now that we have a sub-pixel resolved list of spectral maxima stored 
     * in x_position[row]
     * We can fit a flux weighted straight line to the positions to determine 
     * the spectral column shifts.
     */
    ndata = 0 ;
    for ( row = 0 ; row < lx ; row++ )
    {
        if ( x_position[row] == 0. || sig[row] == 0. )
        {
            continue ;
        }
        else 
        {
            y[ndata]   = x_position[row] ;
            x[ndata]   = (float)row ;
            sig[ndata] = sig[row] ;
            ndata++ ;
        }
    }
    if ( ndata < 10 )
    {
        sinfo_msg_error("not enough positions to do the linear fit") ;
        return FLAG ;
    }

    /* now do the fit and return the slope of the straight line */
    mwt = 0 ;
    sinfo_my_fit(x, y, ndata, sig, mwt, &a, &b, &siga, &sigb, &chi2, &q) ;

    cpl_free(col_value) ;
    cpl_free(column_value) ;
    cpl_free(col_position) ;
    cpl_free(column_position) ;
    cpl_free(x_position) ;

    cpl_free(x); 
    cpl_free(y); 
    cpl_free(sig) ;

    return b ;
}

/**
   @name sinfo_new_shift_rows()
   @param image   raw image in which the rows should be shifted
   @param slope   slope of a fitted straight line along a spectrum
                  output of sinfo_slopeOfSpectrum
   @param n_order order of the interpolation polynom
   @return resulting image with shifted rows
   @doc   shifts the rows of a raw image by using the output of
          sinfo_slopeOfSpectrum and applying polynomial interpolation
   
 */

cpl_image * 
sinfo_new_shift_rows( cpl_image * image,
                      float      slope, 
                      int        n_order )
{
    cpl_image * returnImage=NULL ;
    float xshift=0 ;
    int   intshift = 0 ;
    float sum=0;
    float new_sum=0;

    float* xnum=NULL ;
    float* row_data=NULL ;
    float* corrected_row_data=NULL ;

    float eval=0 /*, dy*/ ;
    float * imageptr=NULL ;
    int col=0;
    int row=0;
    int firstpos=0;
    int n_points=0;
    int i=0;
    int flag=0;
    int ilx=0;
    int ily=0;
    int olx=0;
    int oly=0;
    float* pidata=NULL;
    float* podata=NULL;

    if ( image == NULL )
    {
        sinfo_msg_error("sorry, no image given") ;
        return NULL ;
    }

    if ( slope == 0. )
    {
        sinfo_msg_error("there is no need to shift the image rows!") ;
        return NULL ;
    }

    if ( n_order <= 0 )
    {
        sinfo_msg_error("wrong order of interpolation polynom given!") ;
        return NULL ;
    }

    returnImage = cpl_image_duplicate( image ) ;
    ilx=cpl_image_get_size_x(image);
    ily=cpl_image_get_size_y(image);
    olx=cpl_image_get_size_x(returnImage);
    oly=cpl_image_get_size_y(returnImage);
    pidata=cpl_image_get_data_float(image);
    podata=cpl_image_get_data_float(returnImage);


    n_points = n_order + 1 ;
    if ( n_points % 2 == 0 )
    {
        firstpos = (int)(n_points/2) - 1 ;
    }
    else
    {
        firstpos = (int)(n_points/2) ;
    }


    xnum=cpl_calloc(n_order + 1,sizeof(float)) ;
    row_data=cpl_calloc(ilx,sizeof(float)) ;
    corrected_row_data=cpl_calloc(ilx,sizeof(float)) ;

    /* fill the xa[] array for the polint function */
    for ( i = 0 ; i < n_points ; i++ )
    {
        xnum[i] = i ;
    }

    /* go through the image rows */
    for ( row = 0 ; row < ily ; row++ )
    {
        /* determine the shift for each row, the middle row is not shifted */
        xshift = slope * (float)( (ily / 2) - row ) ;

        intshift = sinfo_new_nint(xshift) ;
        xshift = xshift - (float)intshift ;

        for ( col = 0 ; col < ilx ; col++ )
        {
            corrected_row_data[col] = 0. ;
        }
        sum = 0. ; /* initialize flux for later rescaling */
        /* go through the image columns */
        for ( col = 0 ; col < ilx ; col++ )
        {
        /* consider integer pixel shifts */
        if ( intshift < 0 )
            {
        if ( col - intshift < ilx )
        {
            row_data[col] = pidata[col-intshift+row*ilx] ;
                }
        else
        {
            row_data[col] = 0. ;
                }
        }
        else if ( intshift > 0 )
        {
        if ( col - intshift >= 0 )
        {
            row_data[col] = pidata[col-intshift+row*ilx] ;
                }
        else
        {
            row_data[col] = 0. ;
        }
        }
        else
        {
               row_data[col] = pidata[col+row*ilx] ;
        }

            /* don't consider the sinfo_edge pixels for flux calculation */
            if ( col != 0 && col != ilx - 1 && !isnan(row_data[col]) )
            {
                sum += row_data[col] ;
            }
            if (isnan(row_data[col])) 
            {
                row_data[col] = 0. ;
                for (i = col - firstpos ; i < col - firstpos + n_points ; i++ )
                {
                    if ( i < 0 ) continue ;
                    if ( i >= ilx) continue ;
                    corrected_row_data[i] = ZERO ;
                }
            }
        }


        /* --------------------------------------------------------------------
         * now we do the polynomial interpolation to achieve the fractional
         * shift that means call polint
         */
        new_sum = 0. ;
        for ( col = 0 ; col < ilx ; col++ )
        {
            /* ---------------------------------------------------------------
             * 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 polint of N.R..
             * Take care of the points near the row edges!
             */
            if ( isnan(corrected_row_data[col]) )
            {
                 continue ;
            }
            if ( col - firstpos < 0 )
            {
                imageptr = &row_data[0] ;
                eval     = (float)col - xshift ;
            }
            else if ( col - firstpos + n_points >= ilx )
            {
                imageptr = &row_data[ilx - n_points] ;
                eval     = (float)(col + n_points - ilx) - xshift ;
            }
            else
            {
                imageptr = &row_data[col-firstpos] ;
                eval     = (float)firstpos - xshift ;
            }

        flag=0;
        corrected_row_data[col]=sinfo_new_nev_ille(xnum,imageptr,
                                                       n_order,eval,&flag);
            /*polint( xnum - 1, imageptr, n_points, eval, 
                      &corrected_row_data[col], &dy ) ;*/

            /* don't take the sinfo_edge points to calculate 
               the scaling factor */
            if (col != 0 && col != ilx - 1 && !isnan(corrected_row_data[col]) )
            {
                new_sum += corrected_row_data[col] ;
            }
        }

        if ( new_sum == 0. )
        {
            new_sum = 1. ;
        }
        for ( col = 0 ; col < ilx ; col++ )
        {
            if ( isnan(corrected_row_data[col]))
            {
                podata[col+row*ilx] = ZERO ;
            }
            else
            {
              /* rescale the row data and fill the returned image */
              /* This gives sometimes inconsistent results if 
                 bad pixels are around */
              /* rescaling is commented out because it delivers wrong results
                 in case of appearance of blanks or bad pixels */
             /*   corrected_row_data[col] *= sum / new_sum ; */
                podata[col+row*ilx] = corrected_row_data[col] ;
            }
        }
    }      

    cpl_free(xnum) ;
    cpl_free(row_data) ;
    cpl_free(corrected_row_data);

    return returnImage ;
}


/**
   @name  sinfo_new_parameter_to_ascii()
   @memo stores parameters in an ASCII file
   @param parameter float parameter array to be stored in an ASCII file
   @param number number of parameters
   @param filename filename of ASCII file
   @return void
    
*/

void 
sinfo_new_parameter_to_ascii ( float * parameter, 
                        int n,
                        char * filename )
{
    FILE * fp ;
    int     i=0 ;

    if ( parameter == NULL || filename == NULL || n <= 0 )
    {
        sinfo_msg_error ("input is missing or wrong!") ;
        return ;
    }
    
    if ( NULL == (fp = fopen ( filename, "w" ) ) )
    {
        sinfo_msg_error("cannot open %s", filename) ;
        return ;
    }

    for ( i = 0 ; i < n ; i++ )
    {
        fprintf (fp, "%le\n", parameter[i] ) ;
    }
    fclose (fp ) ;
}

/**
   @name  sinfo_new_ascii_to_parameter()
   @param filename filename of ASCII file
   @param n number of parameters in the parameter array array of parameters
   @doc writes parameters stored in an ASCII file in an float array
*/

float * 
sinfo_new_ascii_to_parameter ( char * filename,
                           int * n )
{
    FILE * fp ;
    float * parameter=NULL ;
    int     i=0 ;

    if ( filename == NULL || n == NULL )
    {
        sinfo_msg_error ("Input is missing or wrong") ;
        return NULL ;
    }
    
    if ( NULL == (fp = fopen ( filename, "r" ) ) )
    {
        sinfo_msg_error("cannot open %s", filename) ;
        return NULL ;
    }

    /* allocate memory */

    if (NULL == ( parameter = (float*) cpl_calloc (ESTIMATE, sizeof(float)) ) )
    {
        sinfo_msg_error ("cannot allocate memory") ;
        fclose (fp ) ;
        return NULL ;
    }

    i = 0 ;
    while ( fscanf(fp, "%g\n", &parameter[i]) != EOF )
    {
        i++ ;
    }
    *n = i ;

    fclose (fp ) ;

    return parameter ;
}


/**
    @name  sinfo_new_curvature_of_spectrum()
   @param ns_image   image with at least one continuum spectrum of a pinhole
   @param order      order of the fit polynomial
   @param box_length width of the box in which the lines are fit by a Gaussian
   @param left_pos
   @param right_pos left and right positions between which the spectrum should 
                    be located
   @param fwhm      first guess of the full width at half maximum
   @param min_amplitude_factor factor peak/background below given
                       threshold the fit is not carried through
   @return resulting polynomial coefficients.
   @doc    this routine determines the curvature of a spectrum by fitting
           a polynomial to a continuum spectrum. This is done by using
       an image with at least one continuum spectrum of a pinhole.
       this is done by searching the spectrum within the image
       then fitting the spectrum along the rows within a given box
       by a sinfo_gaussian, so that the exact position is determined for
       each row. Afterwards, a polynomial is fitted through the
       found positions. The polynomial coefficients are returned.
*/  

double * 
sinfo_new_curvature_of_spectrum( cpl_image * ns_image,
                    int        order,
                         int        box_length,
                 int        left_pos,
             int        right_pos,
                         float      fwhm,
                         float      minDiff )
{
    int i=0;
    int k=0;
    int row=0;
    int col=0;
    int counter=0;
    int iters=0;
    int xdim=0;
    int ndat=0;
    int its=0;
    int numpar=0;
    float maxval=0 ;
    float tol=0;
    float lab=0;

    float* col_value=NULL ;
    float* column_value=NULL ;
    pixelvalue* col_position=NULL ;
    int* column_position=NULL ;
    float* x_position=NULL ;

    int col_median=0;
    float * xdat=NULL;
    float * wdat=NULL;
    int * mpar=NULL;
    Vector * line=NULL;
    FitParams ** dec_par=NULL ;
    FitParams * par=NULL ;
    int position=0 ;
    int ndata=0 ;
    int bad_ind=0 ;
    dpoint * list=NULL ;
    double * coeffs=NULL ;
    double offset=0 ;
    int lx=0;
    int ly=0;
    float* pdata=NULL;

    if ( ns_image == NULL )
    {
        sinfo_msg_error("sorry, no image given") ;
        return NULL ;
    }
    lx=cpl_image_get_size_x(ns_image);
    ly=cpl_image_get_size_y(ns_image);

    if ( box_length <= 1 || box_length >= right_pos - left_pos )
    {
        sinfo_msg_error("wrong box length given") ;
        return NULL ;
    }
    if ( fwhm < 1. || fwhm > 10. )
    {
        sinfo_msg_error("wrong full width at half maximum given") ;
        return NULL ;
    }
    if ( left_pos < 0 || right_pos <= left_pos || right_pos > lx )
    {
        sinfo_msg_error("wrong left and right positions") ;
        return NULL ;
    }
    if ( minDiff < 1. )
    {
        sinfo_msg_error("wrong amplitude threshold given!") ;
        return NULL ;
    }



    col_value=cpl_calloc(ly,sizeof(float)) ;
    column_value=cpl_calloc(ly,sizeof(float)) ;
    col_position=(pixelvalue*)cpl_calloc(ly,sizeof(pixelvalue)) ;
    column_position=cpl_calloc(ly,sizeof(int)) ;
    x_position=cpl_calloc(ly,sizeof(float)) ;

    /* go through the image rows */
    for ( row = 0 ; row < ly ; row++ )
    {
        col_value[row] = -FLT_MAX ;
        col_position[row] = -1. ;
        /* find the maximum value in each row and store the found column */
        for ( col = left_pos ; col < right_pos ; col++ )
        {
            if ( pdata[col+row*lx] > col_value[row] )
            {
                col_value[row]    = pdata[col+row*lx] ;
                col_position[row] = (pixelvalue)col ;
            }
        }
    }

    /* now determine the sinfo_new_median of the found columns to be sure 
       to have the brightest spectrum */
    col_median = (int)sinfo_new_median(col_position, right_pos - left_pos) ;

    /* now find the peaks around col_median over the whole spectral range */
    for ( row = 0 ; row < ly ; row++ )
    {
        x_position[row] = 0. ;
        column_value[row] = -FLT_MAX ;
        column_position[row] = -1 ;
        for ( col = col_median - box_length ; 
              col <= col_median + box_length ; col++ )
        {
            if ( pdata[col+row*lx] > column_value[row] )
            {
                column_value[row] = pdata[col+row*lx] ; 
                column_position[row] = col ;
            }
        }

        /* allocate memory for the array where the line is fitted in */
        if ( NULL == (line = sinfo_new_vector (2*box_length + 1)) )
        {
            sinfo_msg_error ("cannot allocate new Vector in row: %d", row) ;
            return NULL ;
        }

        /* allocate memory */
        xdat = (float *) cpl_calloc( line -> n_elements, sizeof (float) ) ;
        wdat = (float *) cpl_calloc( line -> n_elements, sizeof (float) ) ;
        mpar = (int *)   cpl_calloc( MAXPAR, sizeof (int) ) ;
        dec_par = sinfo_new_fit_params(1) ;
        par = dec_par[0];
    
        counter = 0 ;
        bad_ind = 0 ;
        /* store the values to fit in a Vector object */
        for ( col = column_position[row] - box_length ; 
              col <= column_position[row] + box_length ; col++ )
        {
            if ( col < 0 || col >= lx )
            {
                sinfo_msg_error ("wrong spectrum position or box_length "
                                 "given in row: %d", row) ;
                cpl_free (xdat) ;
                cpl_free (wdat) ;
                cpl_free (mpar) ;
                sinfo_new_destroy_fit_params(&dec_par) ;
                sinfo_new_destroy_vector( line ) ;
                return NULL ;
            }
            else if ( isnan(pdata[col+row*lx]) )
            {
                bad_ind = 1 ;
            }
            else
            {
                line -> data[counter] = pdata[col + row*lx] ;
                counter++ ;
            }
        }
 
        /* go to the next row if a bad pixel is inside the box */
        if ( bad_ind == 1 )
        {
            sinfo_msg_warning ("sorry, bad pixel inside fitting box "
                               "in row: %d", row) ;
            cpl_free (xdat) ;
            cpl_free (wdat) ;
            cpl_free (mpar) ;
            sinfo_new_destroy_fit_params(&dec_par) ;
            sinfo_new_destroy_vector( line ) ;
            continue ;
        }

        /*--------------------------------------------------------------------
         * go through the line sinfo_vector
         * determine the maximum pixel value in the line sinfo_vector
         */
        maxval = -FLT_MAX ;
        position = -INT32_MAX ;
        for ( i = 0 ; i < counter ; i++ )
        {
            xdat[i] = i ;
            wdat[i] = 1.0 ;
            if ( line -> data[i] >= maxval )
            {
                maxval = line -> data[i] ;
                position = i ;
            }
        }

        /* set initial values for the fitting routine */
        xdim     = XDIM ;
        ndat     = line -> n_elements ;
        numpar   = MAXPAR ;
        tol      = TOL ;
        lab      = LAB ;
        its      = ITS ;
        (*par).fit_par[1] = fwhm ;
        (*par).fit_par[2] = (float) position ;
        (*par).fit_par[3] = (float) (line -> data[0] + 
                                     line -> data[line->n_elements - 1]) / 2.0;

        (*par).fit_par[0]  = maxval - ((*par).fit_par[3]) ;
        /* exclude negative peaks and low signal cases */
        if ( (*par).fit_par[0] < minDiff )
        {
            sinfo_msg_warning ("sorry, negative peak or signal of line "
                               "too low to fit in row: %d", row) ;
            cpl_free (xdat) ;
            cpl_free (wdat) ;
            cpl_free (mpar) ;
            sinfo_new_destroy_fit_params(&dec_par) ;
            sinfo_new_destroy_vector( line ) ;
            continue ;
        }

        for ( k = 0 ; k < MAXPAR ; k++ )
        {
            (*par).derv_par[k] = 0.0 ;
            mpar[k] = 1 ;
        }

        /* finally, do the least square fit using a sinfo_gaussian */
        if ( 0 > ( iters = sinfo_new_lsqfit_c( xdat, &xdim, 
                                               line -> data, wdat, 
                                               &ndat, (*par).fit_par,
                                               (*par).derv_par, mpar, 
                                               &numpar, &tol, &its, &lab )) )
        {
            sinfo_msg_warning ("least squares fit failed in row: "
                               "%d, error no.: %d", row, iters) ;
            cpl_free (xdat) ;
            cpl_free (wdat) ;
            cpl_free (mpar) ;
            sinfo_new_destroy_fit_params(&dec_par) ;
            sinfo_new_destroy_vector( line ) ;
            continue ;
        }

        /* check for negative fit results */
        if ( (*par).fit_par[0] <= 0. || (*par).fit_par[1] <= 1. ||
             (*par).fit_par[2] <= 0. )
        {
            sinfo_msg_warning ("negative parameters as fit result, "
                               "not used! in row: %d", row) ;
            cpl_free (xdat) ;
            cpl_free (wdat) ;
            cpl_free (mpar) ;
            sinfo_new_destroy_fit_params(&dec_par) ;
            sinfo_new_destroy_vector( line ) ;
            continue ;
        }

        /* correct the fitted position for the given row of the line 
           in image coordinates */
        x_position[row] =  (float) (column_position[row] - box_length) + 
                                   (*par).fit_par[2] ;
        printf("%d %f %f\n",row, (*par).fit_par[1], x_position[row] ) ;

        /* free memory */
        sinfo_new_destroy_fit_params(&dec_par) ;
        sinfo_new_destroy_vector ( line ) ;
        cpl_free ( xdat ) ;
        cpl_free ( wdat ) ;
        cpl_free ( mpar ) ;
    }
    /* now allocate memory for the data to fit */
    if ( NULL == ( list = (dpoint*) cpl_calloc (ly, sizeof (dpoint)) ) ) 
    {
        sinfo_msg_error("could not allocate memory!") ;
        return NULL ;
    }

    /* ------------------------------------------------------------------------
     * now that we have a sub-pixel resolved list of spectral maxima stored 
     * in x_position[row] We can fit a flux weighted straight line to the 
     * positions to determine the spectral column shifts.
     */
    offset = (double) ly/2. ;
    ndata = 0 ;
    for ( row = 0 ; row < ly ; row++ )
    {
        if ( x_position[row] == 0. )
        {
            continue ;
        }
        else 
        {
            list[ndata].y   = (double)x_position[row] ;
            list[ndata].x   = (double)row - offset ;
            ndata++ ;
        }
    }
    

    if ( NULL == (coeffs = sinfo_fit_1d_poly(order, list, ndata, NULL)) )
    {
        sinfo_msg_error("eclipse function sinfo_fit_1d_poly() did not work!") ;
        return NULL ;
    }
    cpl_free ( list ) ;



    cpl_free(col_value) ;
    cpl_free(column_value) ;
    cpl_free(col_position) ;
    cpl_free(column_position) ;
    cpl_free(x_position) ;
     
    return coeffs ;
}

/**
@name sinfo_new_image_warp_fits
@memo correct optical distortions
@param image distorted image
@param kernel_type type of kernel to correct distortions
@param poly_table table containing distortion coefficients
@return image distortion corrected
*/    
cpl_image * 
sinfo_new_image_warp_fits( cpl_image * image,
                      char      * kernel_type, 
                      char      * poly_table )
{
        cpl_image  * warped=NULL;
    /* Following are for polynomial transforms */
    cpl_polynomial    * poly_u=NULL;        /* polynomial definition */
    cpl_polynomial    * poly_v=NULL;        /* polynomial definition */
        cpl_table* poly_tbl=NULL;
    cpl_vector      *   profile=NULL ;
        cpl_size local_pow[2];
        int             i=0;

    /*fscanf(poly_in,"%s",poly_string);*/
    /* sinfo_msg("%s",poly_string); */
    
    poly_u = cpl_polynomial_new(2);
        if (poly_u == NULL) {
            sinfo_msg_error("cannot read 2D poly from arc table") ;
            return NULL ;
        }
 
        if (poly_u != NULL) {
      sinfo_msg_debug("Get the arc distortion from the file %s",
                          poly_table);
      if(sinfo_is_fits_file(poly_table) != 1) {
        sinfo_msg_error("Input file %s is not FITS",poly_table);
        return NULL;
      }

      if(NULL==(poly_tbl = cpl_table_load(poly_table,1,0))) {
        sinfo_msg_error("cannot load the arc table") ;
        cpl_polynomial_delete(poly_u) ;
        return NULL ;
      }

      for (i=0 ; i<cpl_table_get_nrow(poly_tbl) ; i++) {
            local_pow[0] = cpl_table_get_int(poly_tbl, "degx", i, NULL) ; 
            local_pow[1] = cpl_table_get_int(poly_tbl, "degy", i, NULL) ;
            cpl_polynomial_set_coeff(poly_u, local_pow,
           cpl_table_get_double(poly_tbl, "coeff", i, NULL)) ;
      }

      cpl_table_delete(poly_tbl) ;
    } else {
      sinfo_msg("Use the ID polynomial for the arc dist") ;
      local_pow[0] = 1 ;
      local_pow[1] = 0 ;
      cpl_polynomial_set_coeff(poly_u, local_pow, 1.0) ;
    }

        poly_v=cpl_polynomial_new(2);
        local_pow[0]=0;
        local_pow[1]=1;

        cpl_polynomial_set_coeff(poly_v,local_pow,1.0);
    profile = cpl_vector_new(CPL_KERNEL_DEF_SAMPLES) ;
    cpl_vector_fill_kernel_profile(profile, CPL_KERNEL_TANH,
                       CPL_KERNEL_DEF_WIDTH) ;
    warped=sinfo_new_warp_image_generic(image,kernel_type,poly_u,poly_v);
    /* YVES WAY 
      warped = cpl_image_new(cpl_image_get_size_x(image),
                                 cpl_image_get_size_y(image),
                                 CPL_TYPE_FLOAT);
       
    if (cpl_image_warp_polynomial(warped, image, poly_u, poly_v, 
                      profile,CPL_KERNEL_DEF_WIDTH,
                      profile,CPL_KERNEL_DEF_WIDTH)
        != CPL_ERROR_NONE) {
            sinfo_msg_error("cannot correct the distortion") ;
            cpl_image_delete(warped) ;
            cpl_polynomial_delete(poly_u) ;
            cpl_polynomial_delete(poly_v) ;
            cpl_vector_delete(profile) ;
            return NULL;
    }
    */

      cpl_vector_delete(profile) ;
      if (poly_u!=NULL) cpl_polynomial_delete(poly_u);
      if (poly_v!=NULL) cpl_polynomial_delete(poly_v);
    
      return warped;
}
    
/**@}*/
