/*===========================================================================
  Copyright (C) 1991-2009 European Southern Observatory (ESO)
 
  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., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.IDENT        sprebin.c
.MODULE       main program -- sprebin.exe
.LANGUAGE     C
.AUTHOR       Cristian Levin - ESO La Silla
.PURPOSE      linear, quadratic an spline rebinning.
.KEYWORDS     rebinning.
.ENVIRONMENT  UNIX
.VERSION 1.0  1-Apr-1991   Implementation

 090723		last modif
------------------------------------------------------------*/

#include <midas_def.h>

#include <gl_defs.h>
#include <lndisp.h>
#include <mutil.h>

#include <stdio.h>
#include <math.h>

#define MAXINTENS	99999.0
#define MININTENS	-99999.0

#define LINEAR		0
#define QUADRATIC	1
#define SPLINE		2



/* Keywords */
int	Fitd;
int	Npix[2];

char 	Coerbr[81], FramIn[81], FramOut[81]; 
int 	IdIn, IdOut;

double	Starti, Stepi,
	Starto, Endo, Stepo;
float	Cutsi[2];
float	Imax = MININTENS,
	Imin = MAXINTENS;

int 	NpixOut;
int	Method;
int     clony;			/* descr. clone flag */

extern void free_dvector(), free_fvector();
extern void read_parameters(), update_frame(), rebin();
extern void closest_index(), read_coefs();
extern void flip_double(), flip_float();

extern double  *dvector(), **dmatrix();

extern float   *fvector();

extern int file_exists();




int main()
{
    int  nulval; /* garbage */
    double *x,    *win, *wout;
    float  *yin,  *yout;
    int done = 1, slice;
    int i, fsize,f_in, f_out;
    char msg[80];
    double    pixel;

    SCSPRO("sprebin");
    read_parameters();

    read_coefs();  /* Now read only descriptor REBPAR */
    initdisp(Coerbr, "OLD",0);

    x    = dvector(0, Npix[0] - 1);
    win  = dvector(0, Npix[0] - 1);
    wout = dvector(0, NpixOut - 1);
    yin  = fvector(0, Npix[0] - 1);
    yout = fvector(0, NpixOut - 1);

    for ( i = 0; i < Npix[0]; i++ )
	x[i]  = (Starti + Stepi * i);
    for ( i = 0; i < NpixOut; i++ )
	wout[i] = (Starto + Stepo * i);


    f_out  = (Stepo < 0.) ? -1 : 1;
    if (f_out == -1) flip_double(wout, NpixOut);

    fsize = NpixOut * Npix[1];

    /* SCFCRE( FramOut, D_R4_FORMAT, F_O_MODE, F_IMA_TYPE, fsize, &IdOut ); */

    /* create new frame + clone descr. if possible (then: clony = 1) */

    (void) CGN_IBUILD(IdIn,FramOut,D_R4_FORMAT,fsize,&IdOut,&clony);


    slice = Npix[1]/5;

    for ( i = 1; i <= Npix[1]; i++ ) {
    	SCFGET( IdIn, (i-1)*Npix[0]+1, Npix[0], &nulval, (char *)yin );

        pixel = readdisp(i);
        eval_disp(x, win, Npix[0]);

        f_in  = (win[0] > win[Npix[0]-1]) ? -1 : 1;
        if (f_in == -1) {flip_double(win,Npix[0]); flip_float(yin,Npix[0]);}

	rebin( win, wout, yin, yout, Npix[0], NpixOut);

        if (f_out == -1) flip_float(yout, NpixOut);

    	SCFPUT( IdOut, (i-1)*NpixOut+1, NpixOut, (char *)yout );

        /* printf("Now processing line %d\n",i); */
        if ( i == slice * done && done != 5 ) {
	    sprintf( msg, "%3d%% done...", done * 20 );
	    SCTPUT(msg);
	    done++;
        }
    }
    sprintf( msg, "100%% done..." );
    SCTPUT(msg);

    finishdisp();

    update_frame();

    free_dvector(x, 0, Npix[0] - 1);
    free_dvector(win, 0, Npix[0] - 1);
    free_dvector(wout, 0, Npix[0] - 1);
    free_fvector(yin, 0, Npix[0] - 1);
    free_fvector(yout, 0, Npix[0] - 1);

    SCSEPI();

    return 0;
}

void rebin( win, wout, yin, yout, nin, nout)
double     *win, *wout;
float      *yin, *yout;
int        nin, nout;
{
    int i, j, k, idx, ix = 0;
    int j1, j2;
    int istart = 2;

    double x1, x2, dx;
    double a, b, c, y;
    double d, ddd;
    double dd = 0.0, rx = -0.5;
    double yval[3];


    if ( Method == SPLINE ) {	/* No flux conservation ! */
    	for ( i = 0; i < nout; i++ ) {
    	    yout[i] = hsplint( wout[i], win-1, yin-1, nin, &istart );
	    if ( yout[i] < Imin ) Imin = yout[i];
	    if ( yout[i] > Imax ) Imax = yout[i];
	}
	return;
    }

    closest_index( rx, win, wout, nin, nout, &x1, &ix );
    j1 = NINT(x1);

    for ( i = 0; i < nout; i++ ) {
	rx += 1.0;
    	closest_index( rx, win, wout, nin, nout, &x2, &ix );
    	j2 = NINT(x2);

	dx = x2 - x1;
	d = 0.0;
	if ( Method == QUADRATIC ) {
	    if ( i == 0 ) {
		for ( k = 0; k < 3; k++ ) {
		    idx = j1 + k - 1;
		    if ( idx < 0 || idx > nin - 1 )
			yval[k] = 0.0;
		    else
			yval[k] = yin[idx];
		}
		a = 0.5*(yval[0] + yval[2]);
		b = 0.5*(a - yval[0]);
		c = (13.0*yval[1] - a) / 12.0;
		a = (a - yval[1]) / 3.0;
		y = x1 - j1;
		dd = ((a*y + b)*y + c)*y - 0.25*b + 0.125*a + 0.5*c;
	    }

	    for ( k = 0; k < 3; k++ ) {
	        idx = j2 + k - 1;
	        if ( idx < 0 || idx > nin - 1 )
		    yval[k] = 0.0;
	        else
		    yval[k] = yin[idx];
	    }
	    a = 0.5*(yval[0] + yval[2]);
	    b = 0.5*(a - yval[0]);
	    c = 1.083333333333*yval[1] - 0.083333333333*a;
	    a = 0.333333333333*(a - yval[1]);
	    y = x2 - j2;
	    d -= dd;
	    dd = ((a*y + b)*y + c)*y - 0.25*b;
	    ddd = 0.125*a + 0.5*c;
	    d += dd - ddd;
	    dd += ddd;
	}
	else if ( Method == LINEAR ) {
	    if ( i == 0 ) {
		if ( j1 < 0 || j1 > nin - 1 )
		    dd = 0.0;
	 	else
		    dd = (j1 - x1 - 0.5)*yin[j1];
	    }

	    if ( j2 < 0 || j2 > nin - 1 )
	        ddd = 0.0;
	    else
	        ddd = yin[j2];

	    d += dd;
	    dd = (j2 - x2 - 0.5)*ddd;
	    d = d - dd - ddd;
	}

	for ( j = j1; j <= j2; j++ )
	    if ( j >= 0 && j < nin )
		d += yin[j];

        yout[i] = d;
        x1 = x2;
	j1 = j2;

	if ( yout[i] < Imin ) Imin = yout[i];
	if ( yout[i] > Imax ) Imax = yout[i];

    } /* end of loop */
}

void closest_index( rx, win, wout, nin, nout, x, ix )
double rx;
double *win, *wout;
int nin, nout;
double *x;
int *ix; 		/* index of initial guess */
{
    double wave, waux;
    int irx, ix2;

    irx = rx; /* integer part of rx */

    if ( irx < 0 )
	irx = 0;
    else if ( irx >= nout - 1 )
	irx = nout - 2;

    wave = wout[irx] + (rx - irx) * Stepo;

    waux = win[*ix];
    if ( win[0] <= win[nin-1] ) {  /* increasing wavelength */
        if ( waux > wave ) { /* initial guess is too high */
	    while ( *ix > 0 && waux > wave ) {
	        (*ix)--;
	        waux = win[*ix];
	    }
	    ix2 = *ix + 1;
        }
        else {		/* initial guess is too low */
	    while ( *ix < nin - 1 && waux < wave ) {
	        (*ix)++;
	        waux = win[*ix];
	    }
	    ix2 = *ix - 1;
        }
    }
    else { /* decreasing wavelength */
        if ( waux > wave ) { /* initial guess is too high */
	    while ( *ix < nin - 1 && waux > wave ) {
	        (*ix)++;
	        waux = win[*ix];
	    }
	    ix2 = *ix - 1;
        }
        else {		/* initial guess is too low */
	    while ( *ix > 0 && waux < wave ) {
	        (*ix)--;
	        waux = win[*ix];
	    }
	    ix2 = *ix + 1;
        }
    }
	    
    /* interpolate to find X */
    *x = *ix + (wave - waux) / (win[ix2]-waux) * (ix2 - *ix);
}

void update_frame()
{
    int   unit;		/* useless */
    float cuts[4];

    cuts[0] = Cutsi[0];
    cuts[1] = Cutsi[1];
    cuts[2] = Imin;
    cuts[3] = Imax;

    if (clony == 0) SCDCOP( IdIn, IdOut, ALL_DESC, NULL ); 
    SCDWRD( IdOut, "START", &Starto, 1, 1, &unit );
    SCDWRD( IdOut, "STEP", &Stepo, 1, 1, &unit );
    SCDWRI( IdOut, "NPIX", &NpixOut, 1, 1, &unit );
    SCDWRR( IdOut, "LHCUTS", cuts, 1, 4, &unit );
}

void read_parameters()
{
    int  unit;		/* useless */
    char msg[80];
    int actval,nulval;	/* useless */
    int naxis;

    SCKGETC( "IN_A", 1, 60, &actval, FramIn );
    SCKGETC( "IN_B", 1, 60, &actval, Coerbr );
    SCKGETC( "OUT_A", 1, 60, &actval, FramOut );
    SCKRDI( "INPUTI", 1, 1, &actval, &Method, &unit, &nulval );
    SCKRDI( "FITD", 1, 1, &actval, &Fitd, &unit, &nulval );

    if ( SCFOPN( FramIn, D_R4_FORMAT, 0, F_IMA_TYPE, &IdIn ) != 0 ) {
	sprintf( msg, "Frame %s invalid...", FramIn );
	SCTPUT( msg );
	return;
    }

    SCDRDI( IdIn, "NAXIS", 1, 1, &actval, &naxis, &unit, &nulval );
    SCDRDI( IdIn, "NPIX", 1, 2, &actval, Npix, &unit, &nulval );
    if ( naxis == 1 )
	Npix[1] = 1;
    SCDRDD( IdIn, "START", 1, 1, &actval, &Starti, &unit, &nulval );
    SCDRDD( IdIn, "STEP", 1, 1, &actval, &Stepi, &unit, &nulval );
    SCDRDR( IdIn, "LHCUTS", 1, 2, &actval, Cutsi, &unit, &nulval );
}

void read_coefs()
{
    int  id, actval, nulval;	/* useless */
    int   unit;
 
    float rebpar[3];



    if ( ! file_exists(Coerbr, ".tbl") )
	SCETER( 9 , "Coefficients table couldn't be opened. Stop.\n");

    TCTOPN(Coerbr, F_IO_MODE, &id);

    SCDRDR( id, "REBPAR", 1, 3, &actval, rebpar, &unit, &nulval );
    Starto = (double) rebpar[0];
    Endo   = (double) rebpar[1];
    Stepo  = (double) rebpar[2];
    NpixOut = (int) ((Endo - Starto) / Stepo + 0.5);

    TCTCLO( id );

}


void flip_double (w,n)

double *w;
int    n;

  {

    int middle, i;
    double tmp;

    middle = (int) (n-1)/2;

    for (i=0; i<=middle; i++) {
         tmp=w[i];
         w[i] = w[n-i-1];
         w[n-i-1] = tmp;
       }

  }

void flip_float (w,n)

float *w;
int    n;

  {

    int middle, i;
    float tmp;

    middle = (int) (n-1)/2;

    for (i=0; i<=middle; i++) {
         tmp=w[i];
         w[i] = w[n-i-1];
         w[n-i-1] = tmp;
       }

  }

