/*===========================================================================
  Copyright (C) 1995-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        gspec.c
.MODULE       subroutines -- gmidas.exe
.LANGUAGE     C
.AUTHOR       Cristian Levin - ESO La Silla
.PURPOSE      This routines implement the application part of
              the Spectra context.
.KEYWORDS     application routines, Midas related routines.
.VERSION 1.0  1-Mar-1991   Implementation
 
090423		last modif
------------------------------------------------------------*/

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

#include <xm_defs.h>
#include <gl_defs.h>
#include <agl_defs.h>
#include <main_defs.h>
#include <spec_defs.h>
#include <UxLib.h>
#include <midas_def.h>
#include <osparms.h>

#define LINE_ADD_TABLE	"lineadd"

/*******************
  Local variables
 *******************/
static LCTAB *	Lc = NULL;	/* Line catalog table */
static float *	LinePos;	/* array of positions of LINE.tbl */
static float *	LinePeak;	/* array of intensities of LINE.tbl */
static float *	Ident;		/* array of :IDENT values of LINE.tbl */
static int *	LineRow;	/* row numbers. of each element of LinePos */
static int	NumLinePos = 0;	/* LinePos cardinality */

static char *	List[MAXTEXT];
static int	LincatAllocated = FALSE;
static char 	LineTable[MAXLINE];

/**********************************
   application external variables 
 **********************************/
extern char 	Wlc[],		/* WLC name */
		WlcIdent[],	/* WLC identification */
		Lincat[];	/* Line catalog name */

extern float 	LincatPos[];	/* :WAVE in 'Lincat' pointed by the user */
extern float 	WlcPos[];	/* :X values in LINE matching LincatPos[] */
extern int 	NumLincatPos;	/* cardinality of LincatPos[] */
extern int 	NumWlcPos;	/* cardinality of WlcPos[] */
extern int	IdentBegin;

float *fvector();
int *ivector();
char **cmatrix();
int  file_exists(), read_catalog_table();
int  Areadim();
int  Acutx(), Acuty(), Ashift();

void Alabelx(), Agetcur(),Aunzoom();

void free_fvector();
void free_ivector();
void free_cmatrix();

/*******************
  local functions
 *******************/
void read_params_long();
void ident_spec();
void label_spec();
void free_lincat();
void label_spectrum();
void init_graphic();
void set_viewport();
void end_graphic();
void redraw_spectrum();




void read_params_long()
{
    int actval; 	/* actual values returned */
    int unit;		/* useless */
    int nulval;		/* useless */

    SCKGETC("LINTAB", 1, 20, &actval, LineTable);
    SCKGETC("WLC", 1, 20, &actval, Wlc);
    SCKGETC("LINCAT", 1, 20, &actval, Lincat);
    SCKRDI("WRANG", 1, 2, &actval, Wrang, &unit, &nulval);
    SCKRDR("IMIN", 1, 1, &actval, &Imin, &unit, &nulval);
    SCKRDI("YSTART", 1, 1, &actval, &Ystart, &unit, &nulval);
    SCKRDI("NPIX", 1, 2, &actval, Npix, &unit, &nulval);
}

/*****************************************************************
 * read_lincat_table(): reads line catalog named by 'Lincat' and
 * stores the values in the 'Lc' structure, under the 
 * restrictions Wrang & Imin.
 *****************************************************************/
int read_lincat_table()
{
    if ( ! file_exists( Lincat, ".tbl" ) ) {
    	SCTPUT( "*** Line catalogue doesn't exist ***" );
    	return(FALSE);
    }

    if ( Lc != NULL ) { /* deallocate space */
    	free_fvector(Lc->wave, 0, Lc->nrows - 1);
    	free_fvector(Lc->intens, 0, Lc->nrows - 1);
    	free_cmatrix(Lc->ion, 0, Lc->nrows - 1, 0, MAXION - 1);
	osmmfree((char *)Lc);
    }
    Lc  = (LCTAB *)osmmget( sizeof(LCTAB) );

    /* call to a library routine */
    if( !read_catalog_table( Lc, Lincat, Wrang, Imin ) ) {
	Lc = NULL;
	return FALSE;
    }
    return TRUE;
}

/***************************************************************
 * read_line_x(): reads columns :X and :PEAK of LINCAT table,
 * OUT: float LinePos[] = array of X values.
 *      float LineRow[] = indexes to the rows in the table.
 *      int NumLinePos  = no. of elements of LinePos.
 */
int read_line_x( ident_step )
int ident_step;	/* IDBEGIN ==> Identifications are cleared */
		/* IDCONT  ==> Identifications are read */
{
    int nulval, sortcol, aw, ar, ncols;
    int i, id, colid, colx, colpeak, colerased, selected;
    int lines_search = FALSE;
    char val_ok[2];
    static int nrows;

    if ( ! file_exists( LineTable, ".tbl" ) ) 
	return(FALSE);

    if ( NumLinePos > 0 ) {
    	free_fvector(LinePos, 0, nrows - 1);
    	free_fvector(LinePeak, 0, nrows - 1);
    	free_fvector(Ident, 0, nrows - 1);
    	free_ivector(LineRow, 0, nrows - 1);
    }

    sprintf(val_ok, "%c", VAL_OK);
    NumLinePos = 0;

    TCTOPN( LineTable, F_IO_MODE, &id );

    TCIGET( id, &ncols, &nrows, &sortcol, &aw, &ar );

    TCCSER( id, ":X", &colx );
    TCCSER( id, ":PEAK", &colpeak );
    TCCSER( id, ":ERASED", &colerased );

    TCCSER( id, ":IDENT", &colid );
    if ( colid == -1 )
	TCCINI( id, D_R4_FORMAT, 1, "F10.3", "ANGSTROM", "IDENT", &colid );
	
    
    /* allocate space for data */
    LinePos  = fvector(0, nrows - 1);
    LinePeak = fvector(0, nrows - 1);
    Ident    = fvector(0, nrows - 1);
    LineRow  = ivector(0, nrows - 1);

    for ( i = 1; i <= nrows; i++ ) {
	TCSGET(id, i, &selected);
	if ( selected ) {
	    lines_search = TRUE;
	    TCERDR( id, i, colx,    &LinePos[NumLinePos], &nulval );
	    TCERDR( id, i, colpeak, &LinePeak[NumLinePos], &nulval );
	    Ident[NumLinePos] = Rnull;
	    if ( ident_step == IDBEGIN ) { /* :IDENT <-- NULL */
		TCEWRR( id, i, colid, &Rnull );
		if ( colerased != -1 )
		    TCEWRC( id, i, colerased, val_ok );
	    }
 	    else 
	    	TCERDR( id, i, colid, &Ident[NumLinePos], &nulval );
	    LineRow[NumLinePos]  = i;
	    NumLinePos++;
        }
    }
    TCTCLO( id );

    if ( !lines_search ) {
	SCTPUT( "*** YSTART row doesn't have lines searched ***" );
	return(FALSE);
    }
    return(TRUE);
}

int update_line_table( action )
int action;
{
    char erased; 
    char invalid_value = VAL_INVALID,
         valid_value = VAL_OK;
    int nulval, sortcol, aw, ar, ncols, nrows;
    int i, id, col;

    if ( ! file_exists( LineTable, ".tbl" ) ) {
    	SCTPUT( "*** Lines have not been searched ***" );
    	return(FALSE);
    }

    if ( TCTOPN( LineTable, F_IO_MODE, &id ) != 0 ) {
	SCTPUT( "Table LINE couldn't be opened." );
	return(FALSE);
    }
    TCCSER( id, ":ERASED", &col );
    if ( col == -1 )	/* nothing to update */
	return(TRUE);

    TCIGET( id, &ncols, &nrows, &sortcol, &aw, &ar );
    for ( i = 0; i < nrows; i++ ) {
	erased = VAL_OK;
	TCERDC( id, i+1, col, &erased, &nulval );
	if ( erased == VAL_ERASED && action == DELMARK )
    	    TCEWRC( id, i+1, col, &invalid_value );
	else if ( erased != VAL_OK && action == ADDMARK )
    	    TCEWRC( id, i+1, col, &valid_value );
    }
    TCTCLO(id);
    return(TRUE);
}

void display_lincat_table( wlist )
Widget wlist;
{
    int i;
    XmStringTable str_list;

    if ( LincatAllocated )
	free_lincat();
    LincatAllocated = TRUE;

    for ( i = 0; i < Lc->nrows; i++ )
    	List[i] = (char *)osmmget( 80 );
    List[Lc->nrows] = NULL;

    for ( i = 0; i < Lc->nrows; i++ )
    	sprintf( List[i], " %8.2f    %10.5g  %10s", Lc->wave[i],
			     Lc->intens[i], Lc->ion[i] );

    str_list = (XmStringTable)XtMalloc(Lc->nrows * sizeof(XmString *));
    for ( i = 0; i < Lc->nrows; i++ )
        str_list[i] = XmStringCreateSimple(List[i]);

    XmListSetPos(wlist, 1);
    XmListDeleteAllItems(wlist);
    XmListAddItems(wlist, str_list, Lc->nrows, 1);

    for ( i = 0; i < Lc->nrows; i++ )
        XmStringFree(str_list[i]);
    XtFree((char *)str_list);
}

void free_lincat()
{
    int i;

    for ( i = 0; i < Lc->nrows; i++ )
    	osmmfree( List[i] );
}


/*****************************************************************
 plot_spec()
   description:
     plot the starting line (Ystart) of the calibration
     image (Wlc). If label = LABEL_SPEC, it plots additionally the
     lines found and the previous identifications.
   use:
     line identification.
     line adding for directed search.
*/
int plot_spec( label )
int label;
{
    int unit;		/* useless */
    int nulval, actval;	/* useless */
    int id;
    int naxis;

    if ( ! file_exists( Wlc, ".bdf" ) ) {
	SCTPUT( "*** Calibration image invalid ***" );
    	return(FALSE);
    }

    SCFOPN( Wlc, D_R4_FORMAT, 0, F_IMA_TYPE, &id );
    SCDRDI( id, "NAXIS", 1, 1, &actval, &naxis, &unit, &nulval );
    SCDRDI( id, "NPIX", 1, 2, &actval, Npix, &unit, &nulval );
    SCFCLO(id);
    if ( Ystart > Npix[1] && naxis > 1 ) {
	SCTPUT( "*** Starting line out of bound ***" );
    	return(FALSE);
    }

    Areadim(Wlc, Ystart, 0, FALSE);

    if ( label == LABEL_SPEC )
    	label_spectrum();

    return(TRUE);
}

void get_wlc_value()
{
    float cpx = 0.0, cpy = 0.0;
    int key, valpix;

    if ( NumWlcPos > NumLincatPos ) /* clicking twice in graph. win */
	NumWlcPos = NumLincatPos;

    XtSetSensitive((Widget) UxGetWidget(UxFindSwidget("menu3_p2")),FALSE);

    init_graphic(DEV_NO_ERASE);
    set_viewport();
    AG_VLOC( &cpx, &cpy, &key, &valpix );
    end_graphic();

    if ( key == LEFT_BUT )
	WlcPos[NumWlcPos++] = cpx;
    else
        XtSetSensitive((Widget) UxGetWidget(UxFindSwidget("menu3_p2")),TRUE);

}

void delete_idents()
{
    float curr_delta, delta, cpx = 0.0, cpy = 0.0;
    float wlimits[4], x[2], y[2];
    int i, id, colid, linepos, key, valpix;
    char str[MAXLINE];

    if ( TCTOPN( LineTable, F_IO_MODE, &id ) != 0 ) {
	SCTPUT( "Line table could not be opened." );
	return;
    }
    TCCSER( id, ":IDENT", &colid );

    linepos = -1;
    init_graphic(DEV_NO_ERASE);
    set_viewport();
    AG_SSET( BLUE_COLOR );
    AG_RGET( "WNDL", wlimits );
    y[0] = wlimits[YMIN];

    while ( 1 ) { /* forever */
    	AG_VLOC( &cpx, &cpy, &key, &valpix );
	if ( key == MID_BUT ) 
	    break;
        delta = MAXDELTA;
        for ( i = 0; i < NumLinePos; i++ ) {
    	    curr_delta = fabs((double)(LinePos[i] - cpx));
	    if ( curr_delta < delta ) {
	        linepos = i;
	        delta = curr_delta;
	    }
        }
        x[0] = x[1] = LinePos[linepos];
        y[1] = LinePeak[linepos];
    	AG_GPLL( x, y, 2 );
        sprintf( str, "Line removed : %.2f    Wavelength: %.2f",
		   LinePos[linepos], Ident[linepos] );
	SCTPUT(str);
        Ident[linepos] = Rnull;
        TCEWRR(id, LineRow[linepos], colid, &Rnull);
    }

    AG_SSET( DEF_COLOR );
    TCTCLO(id);
    end_graphic();
}

void show_current_idents()
{
    int i;
    char msg[MAXLINE];

    SCTPUT( " " );
    SCTPUT( "Current identifications" );
    SCTPUT( "Position     Wavelength" );
    SCTPUT( "-----------------------" );
    for ( i = 0; i < NumLinePos; i++ )
	if ( Ident[i] != Rnull ) {
	    sprintf( msg, "%7.1f    %9.2f", LinePos[i], Ident[i] );
    	    SCTPUT( msg );
	}
    SCTPUT( " " );
}

void ident_line()
{
    int i, linepos = 0;
    int id, colid;
    double delta = MAXDELTA, curr_delta;
    float val;
    float x[1], y[1];
    char str[MAXLINE];

    val = WlcPos[NumWlcPos-1];	/* last value picked */
    for ( i = 0; i < NumLinePos; i++ ) {
    	curr_delta = fabs((double)(LinePos[i] - val));
	if ( curr_delta < delta ) {
	    linepos = i;
	    delta = curr_delta;
	}
    }
    sprintf( str, "Line added   : %.2f    Wavelength: %.2f",
		   LinePos[linepos], LincatPos[NumWlcPos-1] );
    SCTPUT( str );

    if ( TCTOPN( LineTable, F_IO_MODE, &id ) != 0 ) {
	SCTPUT( "Line table could not be opened." );
	return;
    }
    TCCSER( id, ":IDENT", &colid );
    TCEWRR( id, LineRow[linepos], colid, &LincatPos[NumWlcPos-1] );
    TCTCLO( id );

    Ident[linepos] = LincatPos[NumWlcPos-1];

    x[0] = LinePos[linepos];
    y[0] = LinePeak[linepos];
    Alabelx(x, y, 1, GREEN_COLOR);
}


/****************************************************************************
 exist_descriptor(): checks if the descriptor 'desc' exists in 'image'.
*/
int exist_descriptor( image, desc )
char *image;	/* name of the image */
char *desc;	/* descriptor name */
{
    int nulval, id;
    char type;

    if ( file_exists( image, ".bdf" ) ) {
    	SCFOPN( image, D_R4_FORMAT, 0, F_IMA_TYPE, &id );
  	SCDFND( id, desc, &type, &nulval, &nulval );
	if ( type != ' ' ) {
    	    SCFCLO(id);
	    return(TRUE);
	}
    	SCFCLO(id);
    }
    return(FALSE);
}

/****************************************************************
 get_lines_to_add(): plot the spectrum and get x-coordinates of 
   the spectral lines to add from the graphics cursor.
*/
void get_lines_to_add( plot_label)
int plot_label; /* TRUE  --> plot spectrum with existent lines */
                /* FALSE --> plot spectrum without existent lines */
{
    float wlimits[4], x[2], y[2], cpx = 0.0, cpy = 0.0;
    int key, valpix;

    if ( !plot_spec(plot_label) ) /* plot 'WLC(Ystart)' */
        return;

    NToAdd = 0;

    init_graphic(DEV_NO_ERASE);
    set_viewport();

    AG_SSET( RED_COLOR );
    AG_RGET( "WNDL", wlimits );
    y[0] = wlimits[YMIN];
    y[1] = wlimits[YMAX];
    while( 1 ) {
    	AG_VLOC( &cpx, &cpy, &key, &valpix );
    	if ( key == MID_BUT )
	    break;
	LinesToAdd[NToAdd] = cpx;
    	x[0] = x[1] = cpx;
    	AG_GPLL( x, y, 2 );
	NToAdd++;
    }
    AG_SSET( DEF_COLOR );

    end_graphic();
}

/****************************************************************
 create_add_table(): save the x-coordinates of the spectral lines
  to add into the table LINEADD.tbl.
*/
void create_add_table( n, lines )
int n;
float lines[];
{
    int i, id, col;
    int unit;

    TCTINI( LINE_ADD_TABLE, F_TRANS, F_O_MODE, 3, 100, &id );
    TCCINI( id, D_R4_FORMAT, 1, "F10.2", "PIXEL", "X", &col );
    SCDWRI( id, "YSTART", &Ystart, 1, 1, &unit );

    for ( i = 0; i < n; i++ )
    	TCRWRR( id, i+1, 1, &col, lines+i );

    TCTCLO(id);
}

void label_spectrum()
{
    float x[1], y[1];
    int i;

    Alabelx( LinePos, LinePeak, NumLinePos, BLUE_COLOR );

    for ( i = 0; i < NumLinePos; i++ )
        if ( Ident[i] != Rnull ) {
            x[0] = LinePos[i];
            y[0] = LinePeak[i];
            Alabelx( x, y, 1, GREEN_COLOR );
        }
}

int check_plot()
{
    if ( !IdentBegin ) {
        read_params_long();
        if ( ! read_lincat_table() )
            return FALSE;
        if ( !read_line_x(IDCONT) ) {  /* :X of line table */
            SCTPUT( "*** Lines have not been searched ***" );
            return FALSE;
        }
	if ( !plot_spec(LABEL_SPEC) )
            return FALSE;
    }
    IdentBegin = TRUE;
    return TRUE;
}

void cutx_spectrum()
{
    if ( !check_plot() )
	return;
    while ( Acutx() != MID_BUT )
        label_spectrum();
}

void cuty_spectrum()
{
    if ( !check_plot() )
	return;
    while ( Acuty() != MID_BUT )
        label_spectrum();
}

void shift_spectrum()
{
    if ( !check_plot() )
	return;
    while ( Ashift() != MID_BUT )
        label_spectrum();
}

void unzoom_spectrum()
{
    if ( !check_plot() )
	return;
    Aunzoom();
    label_spectrum();
}

void getcur_spectrum()
{
    if ( !check_plot() )
	return;
    Agetcur();
}

void redraw_plot()
{
    init_graphic(DEV_NO_ERASE);
    set_viewport();
    redraw_spectrum();
    end_graphic();

    label_spectrum();
}
