/*===========================================================================
  Copyright (C) 1995-2011 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        spintegr.c
.MODULE       main program -- spintegr.exe
.LANGUAGE     C
.AUTHOR       Cristian Levin - ESO La Silla
.PURPOSE      This program makes the flux integration.
.KEYWORDS     flux integration, flux calibration.
.VERSION 1.0  1-Apr-1991   Implementation

 111216         last modif
------------------------------------------------------------*/

#include <stdio.h>
#include <midas_def.h>

#define EPS		0.1

int	Line = 1;	/* suppose 1-dim spectrum */
int	Npix[2];
int 	Ncols, Nrows;
int 	IdIma, IdTab;

char 	Image[81], Table[81], Outtab[81]; 
char    msg[120];

double	Start, Step;

float 	*Flux, *Sumf, *Wavet, *Intens, *Wavei;
float	*Binw;
float 	Rnull;

struct colids {
	int wavet, flux, binw;
} col;


extern void free_fvector();

void init_midas();
void init_purify();
void end_midas();
void read_values();
void calculate_flux();
void update_table();

float linint();
float *fvector();

int   *ivector();



int main()
{
    init_purify();
    init_midas();

    read_values();
    calculate_flux();
    update_table();

    end_midas();
return 0;
}

void calculate_flux()
{
    int i, j;
    int jmid = 0, jlo=0, jhi=0;
    int  impix;

    float binmid, histep, lostep, hiint;


    impix = Npix[0]-1;          /* highest index for Wavei */

    for ( i = 0; i < Nrows; i++ ) 
        {
        binmid = Binw[i] * 0.5;                         /* was: / 2.0; */

        for ( j = jmid; j < Npix[0]-1; j++ )
	    if ( Wavet[i] < Wavei[j+1] )
		break;

	if ( Wavet[i] + binmid > Wavei[Npix[0]-1] ||
	     Wavet[i] - binmid < Wavei[0] )
	    continue;

        jmid = j+1;

        for ( j = jmid+1; j < Npix[0]; j++ )
	    if ( Wavet[i] + binmid <= Wavei[j] ) {
		jhi = j-1;
		break;
	    }

        for ( j = jmid; j >= 0; j-- )
	    if ( Wavet[i] - binmid >= Wavei[j] ) { 
                if (j < impix) jlo = j+1;
		break;
	    }
 
        if ( jhi < jlo)			/* check that jlo, jhi are o.k. */
           {
           (void)
           sprintf(msg,"jlo(=%d) > jhi(=%d) - algorithm failure!",jlo,jhi);
           SCETER(13,msg);
           }

        histep  = linint( Wavet[i] + binmid, Wavei[jhi], 0.0,
			 Wavei[jhi+1], Step );
        lostep  = linint( Wavet[i] - binmid, Wavei[jlo-1], Step,
			 Wavei[jlo], 0.0 );
	hiint   = linint( Wavet[i] + binmid, Wavei[jhi], Intens[jhi],
			 Wavei[jhi+1], Intens[jhi+1] );

        Sumf[i] = (Intens[jlo] * lostep +  hiint * histep) / Binw[i];

	for ( j = jlo+1; j <= jhi; j++ )
            Sumf[i] = Sumf[i] + Intens[j] * Step / Binw[i];

    }
}


float linint( x, x1, y1, x2, y2 )	/* Linear interpolation */
float x, x1, y1, x2, y2;
{
    float y, quot, eps = 10.e-30;


    quot = x2 - x1;			/* check, before we divide... */
    if ( (quot > -eps) && (quot < eps) )
       {
       (void) sprintf(msg,"x2 - x1 = %f in linear interp.!",quot);
       SCETER(22,msg);
       }

    y = y1 + (y2 - y1)*(x - x1) / quot;

    return(y);
}

void read_values()
{
    int i, j;
    int actval; 	/* actual values returned */
    int unit;		/* useless */
    int nulval;		/* useless */
    int  sortcol, aw, ar;

    float binmin, rval, flux0, fluxn, wave_end;
    float first_wave, last_wave; 


binmin = 0.0;
    	/* read parameters */
    SCKGETC( "IN_A",  1, 80, &actval, Image );
    SCKGETC( "IN_B",  1, 80, &actval, Table );
    SCKGETC( "OUT_A", 1, 80, &actval, Outtab );

    	/* read info of table */
    if ( TCTOPN( Table, F_I_MODE, &IdTab ) != 0 ) {
	sprintf( msg, "Table %s invalid. Stop.", Table );
	SCETER(33, msg );
    }
    TCIGET( IdTab, &Ncols, &Nrows, &sortcol, &aw, &ar );

    TCCSER( IdTab, ":WAVE", &col.wavet );
    TCCSER( IdTab, ":FLUX_W", &col.flux );
    TCCSER( IdTab, ":BIN_W",  &col.binw );
    if ( col.wavet == -1 || col.flux == -1 || col.binw == -1 ) {
	SCTPUT( "**Error**  Wrong table columns. Correct names are:" );
	SCTPUT( "         :WAVE    (wavelength)" );
	SCTPUT( "         :FLUX_W  (flux in wavelength units)" );
	SCTPUT( "         :BIN_W   (Bin width)" );
	end_midas();
    }
    Flux  = fvector(0, Nrows + 2);
    Wavet = fvector(0, Nrows + 2);
    Binw  = fvector(0, Nrows + 2);
    Sumf  = fvector(0, Nrows + 2);
    for ( i = 0; i < Nrows; i++ ) {
	TCERDR( IdTab, i+1, col.flux,  &Flux[i], &nulval );
	TCERDR( IdTab, i+1, col.wavet, &Wavet[i], &nulval );
	TCERDR( IdTab, i+1, col.binw,  &Binw[i], &nulval );
	Sumf[i] = Rnull;
        if (i == 0 || Binw[i] < binmin) 
           binmin = Binw[i]; 
    }

    	/* read info of image */
    if ( SCFOPN( Image, D_R4_FORMAT, 0, F_IMA_TYPE, &IdIma ) != 0 ) {
	sprintf( msg, "Image %s invalid. Stop.", Image );
	SCETER(34,msg );
    }
    SCDRDI( IdIma, "NPIX", 1, 2, &actval, Npix, &unit, &nulval );
    SCDRDD( IdIma, "START", 1, 1, &actval, &Start, &unit, &nulval );
    SCDRDD( IdIma, "STEP", 1, 1, &actval, &Step, &unit, &nulval );

    /* check stepsize and :BIN_W */

    rval  = (float) Step;
    rval *= 8.0;		/* min of :BIN_W should be >= 8*stepsize */
    if (rval > binmin) 
       {
       (void) sprintf(msg,"WARNING: Min of :BIN_W (%f) < 8*stepsize(%f)",
              binmin,rval);
       SCTPUT(msg);
       SCTPUT("Algorithm may fail...");
       }

    Intens = fvector(0, Npix[0] - 1);
    Wavei  = fvector(0, Npix[0] - 1);

    SCFGET( IdIma, (Line-1)*Npix[0]+1, Npix[0], &nulval, (char *)Intens );

    for ( i = 0; i < Npix[0]; i++ )
	Wavei[i] = Start + Step * i;


	/* interpolation of the table */

    if ( Start + Binw[0] / 2.0 > Wavet[0] ) 
	for ( i = 1; i < Nrows; i++ ) {
	    /* EPS is used to ensure the value will be inside the interval */
    	    first_wave = Start + Binw[i] / 2.0 + EPS;
	    if ( first_wave < Wavet[i] ) {
		flux0 = linint( first_wave, Wavet[i-1], Flux[i-1],
				Wavet[i], Flux[i] ); 
		for ( j = Nrows; j > i; j-- ) {
		    Wavet[j] = Wavet[j-1];
		    Flux[j] = Flux[j-1];
		    Binw[j] = Binw[j-1];
		}
		Wavet[i] = first_wave;
		Flux[i] = flux0;
		break;
	    }
	}

    wave_end = Wavei[Npix[0]-1];
    if ( wave_end - Binw[Nrows-1] / 2.0 < Wavet[Nrows-1] ) 
	for ( i = Nrows-2; i > 0; i-- ) {
    	    last_wave = wave_end - Binw[i] / 2.0 - EPS;
	    if ( last_wave > Wavet[i] ) {
		fluxn = linint( last_wave, Wavet[i], Flux[i],
				Wavet[i+1], Flux[i+1] ); 
		for ( j = Nrows; j > i+1; j-- ) {
		    Wavet[j] = Wavet[j-1];
		    Flux[j] = Flux[j-1];
		    Binw[j] = Binw[j-1];
		}
		Wavet[i+1] = last_wave;
		Flux[i+1] = fluxn;
		break;
	    }
	}
}

void update_table()
{
    int    unit;		/* useless */
    int i, idcol[6], id=0, j = 1;
    float divf, wfirst, wlast;

    if ( TCTOPN(Outtab, F_IO_MODE, &id ) != 0 ) {
	sprintf( msg, "Table %s couldn't be opened. Stop.", Outtab );
	SCETER(35,msg);
    }
    TCCINI( id, D_R4_FORMAT, 1, "F10.1", "Angstrom", "WAVE", &idcol[0] );
    TCCINI( id, D_R4_FORMAT, 1, "F13.5", " ", "FLUX",   &idcol[1] );
    TCCINI( id, D_R4_FORMAT, 1, "F13.5", " ", "SUM",    &idcol[2] );
    TCCINI( id, D_R4_FORMAT, 1, "F13.5", " ", "RATIO",  &idcol[3] );
    TCCINI( id, D_R4_FORMAT, 1, "F13.5", " ", "COLOUR", &idcol[4] );
    TCCINI( id, D_R4_FORMAT, 1, "F13.5", " ", "FREQ",   &idcol[5] );
	
    for ( i = 0; i < Nrows; i++ )
	if ( Sumf[i] != Rnull ) {
	    divf   = Sumf[i] / Flux[i];
	    TCEWRR( id, j, idcol[0], &Wavet[i] );
	    TCEWRR( id, j, idcol[1], &Flux[i] );
	    TCEWRR( id, j, idcol[2], &Sumf[i] );
	    TCEWRR( id, j, idcol[3], &divf );
	    if ( j == 1 ) 
		wfirst = Wavet[i];
	    wlast = Wavet[i];
	    j++;
	}

    /* save first wave and no. of waves for doing b-spline fitting */

    /* This way eventually cuts the borders, so we will force the size of the image
    SCDWRR( id, "WSTART", &wfirst, 1, 1, &unit ); 
    nwaves = (wlast - wfirst + 1) / Step;
    SCDWRI( id, "NWAVES", &nwaves, 1, 1, &unit );
    */

    SCDWRD( id, "WSTART", &Start, 1, 1, &unit );
    SCDWRD( id, "WSTEP", &Step, 1, 1, &unit );
    SCDWRI( id, "NWAVES", &Npix[0], 1, 1, &unit );

    TCTCLO( id );
}

void init_midas()
{
    int inull;
    double dnull;

    SCSPRO( "FLUX" );
    TCMNUL( &inull, &Rnull, &dnull ); /* NULL values */
}

void end_midas()
{
    free_fvector(Flux,   0, Nrows + 2);
    free_fvector(Wavet,  0, Nrows + 2);
    free_fvector(Binw,   0, Nrows + 2);
    free_fvector(Sumf,   0, Nrows + 2);
    free_fvector(Intens, 0, Npix[0] - 1);
    free_fvector(Wavei,  0, Npix[0] - 1);

    SCSEPI();
}
void init_purify()
{
    strcpy(Outtab,"");
    strcpy(Image,"");
    strcpy(Table,"");
}
