/* Panorama_Tools	-	Generate, Edit and Convert Panoramic Images
   Copyright (C) 1998,1999 - Helmut Dersch  der@fh-furtwangen.de
   
   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, 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 Mass Ave, Cambridge, MA 02139, USA.  */

/*------------------------------------------------------------*/


#include "filter.h"
#include <locale.h>

static int 		ReadControlPoint	( controlPoint * cptr, char *line);
static int 		ReadImageDescription( Image *imPtr, stBuf *sPtr, char *line );
static int 		ReadModeDescription ( sPrefs *sP, char *line );
static int 		ReadCoordinates( 	CoordInfo	*cp, char *line );


#define MY_SSCANF( str, format, ptr )		if( sscanf( str, format, ptr ) != 1 )   \
											{										\
												PrintError( 						\
												"Syntax error in script: Line %d\nCould not assign variable",\
												lineNum);							\
												return -1;							\
											}										\


#define READ_VAR(format, ptr )		nextWord( buf, &li );			\
									MY_SSCANF( buf, format, ptr );
									
								
								
#define READ_OPT_VAR(var)		nextWord( buf, &li );			\
								MY_SSCANF( buf, "%ld", &k);		\
								if( k<0 || k>= numIm )			\
								{								\
									PrintError("Syntax error in script: Line %d", lineNum);	\
									return -1;					\
								}								\
								if( gl->opt[k].var )			\
								{								\
									PrintError("Conflict in script: Line %d\nMultiple Instances of Variable", lineNum);	\
									return -1;					\
								}								\
								gl->opt[k].var	 = 1;			\
								n++;							\
											
											
// Optimizer Script parser; fill global info structure

int ParseScript( char* script, AlignInfo *gl )
{
	Image				*im;
	optVars				*opt;
	CoordInfo			*ci;
	
	// Variables used by parser
	
	char 				*li, line[256], *ch ,*lineStart, buf[256];
	int 				lineNum = 0;
	int 				i,k;


	int 				n=0; // Number of parameters to optimize
	int					numIm,numPts,nt;

	setlocale(LC_ALL, "C");

	gl->im 	= NULL;
	gl->opt = NULL;
	gl->cpt	= NULL;
	gl->t 	= NULL;
	gl->cim = NULL;

	// Determine number of images and control points


	gl->numIm 	= numLines( script, 'i' );
	gl->numPts 	= numLines( script, 'c' );
	gl->nt 		= numLines( script, 't' );



	// Allocate Space for Pointers to images, preferences and control points
	
	gl->im 		= (Image*)		malloc( gl->numIm 	* sizeof(Image) );
	gl->opt		= (optVars*)		malloc( gl->numIm 	* sizeof(optVars) );
	gl->cpt		= (controlPoint*) 	malloc( gl->numPts 	* sizeof( controlPoint ));			
	gl->t		= (triangle*) 		malloc( gl->nt 		* sizeof( triangle ));	
	gl->cim		= (CoordInfo*)		malloc( gl->numIm 	* sizeof(CoordInfo) );
	
	if( gl->im == NULL || gl->opt == NULL || gl->cpt == NULL || gl->t == NULL || gl->cim == NULL )
	{
		PrintError("Not enough memory");
		return -1;
	}
	
	SetImageDefaults(&(gl->pano));	
	SetStitchDefaults(&(gl->st)); strcpy( gl->st.srcName, "buf" ); // Default: Use buffer 'buf' for stitching
	for(i=0; i<gl->numIm; i++)
	{
		SetImageDefaults( &(gl->im[i]) );
		SetOptDefaults	( &(gl->opt[i]));
		SetCoordDefaults( &(gl->cim[i]), i);
	}
		
	
	numIm =0; numPts = 0; nt = 0; // reused as indices

	// Parse script	
	
	ch = script;
	
	while( *ch != 0 )
	{
		lineNum++;

		while(*ch == '\n')
			ch++;
		lineStart = ch;

		nextLine( line, &ch );

		// parse line; use only if first character is i,p,v,c,m
	

		switch( line[0] )
		{
		case 'i':		// Image description

					im	= &(gl->im[numIm]);		// This image is being set 
					opt = &(gl->opt[numIm]);
					ci	= &(gl->cim[numIm]);
						
					li = &(line[1]);
					while( *li != 0)
					{
						switch(*li)
						{
							case 'w': 	READ_VAR( "%d", &(im->width) ); break;
							case 'h': 	READ_VAR( "%d", &(im->height)); break;
							case 'v': 	if( *(li+1) == '=' ){
									  		li++;
											READ_VAR( "%d", &(opt->hfov));
											opt->hfov += 2;
										}else{
											READ_VAR(  "%lf", &(im->hfov));
										}
										break;
							case 'a':	if( *(li+1) == '=' ){
									  		li++;
											READ_VAR( "%d", &(opt->a));
											opt->a += 2;
										}else{
											READ_VAR( "%lf", &(im->cP.radial_params[0][3]));
										}
										im->cP.radial 	= TRUE;
										break;
							case 'b':	if( *(li+1) == '=' ){
									  		li++;
											READ_VAR( "%d", &(opt->b));
											opt->b += 2;
										}else{
											READ_VAR( "%lf", &(im->cP.radial_params[0][2]));
										}
										im->cP.radial 	= TRUE;
										break;
							case 'c':	if( *(li+1) == '=' ){
									  		li++;
											READ_VAR(  "%d", &(opt->c));
											opt->c += 2;
										}else{
											READ_VAR( "%lf", &(im->cP.radial_params[0][1]));
										}
										im->cP.radial 	= TRUE;
										break;
							case 'f':
										READ_VAR( "%d", &k );
										switch (k)
										{
											case 0:   im->format = _rectilinear;	break;
											case 1:   im->format = _panorama;
											  			im->cP.correction_mode |= correction_mode_vertical;
									          			break;
											case 2:   im->format = _fisheye_circ;	break;
											case 3:   im->format = _fisheye_ff;		break;
											case 4:   im->format = _equirectangular;
											  			im->cP.correction_mode |= correction_mode_vertical;
														break;
											case 8:	  im->format = _orthographic; break;
											default:  PrintError("Syntax error in script: Line %d", lineNum);
											  			return -1;
											  			break;
										}
										break;
							case 'o':	li++;
										im->cP.correction_mode |=  correction_mode_morph;
										break;
							case 'y': 	if( *(li+1) == '=' ){
									  		li++;
											READ_VAR( "%d", &(opt->yaw));
											opt->yaw += 2;
										}else{
											READ_VAR( "%lf", &(im->yaw));
										}
										break;
							case 'p': 	if( *(li+1) == '=' ){
									  		li++;
											READ_VAR( "%d", &(opt->pitch));
											opt->pitch += 2;
										}else{
											READ_VAR("%lf", &(im->pitch));
										}
										break;
							case 'r': 	if( *(li+1) == '=' ){
									  		li++;
											READ_VAR( "%d", &(opt->roll));
											opt->roll += 2;
										}else{
											READ_VAR("%lf", &(im->roll));
										}
										break;
							case 'd' :	if( *(li+1) == '=' ){
									  		li++;
											READ_VAR( "%d", &(opt->d));
											opt->d += 2;
										}else{
											READ_VAR( "%lf", &(im->cP.horizontal_params[0]));
								      		im->cP.horizontal= TRUE;
										}
										break;	
							case 'e':	if( *(li+1) == '=' ){
									  		li++;
											READ_VAR( "%d", &(opt->e));
											opt->e += 2;
										}else{
											READ_VAR("%lf", &(im->cP.vertical_params[0]));
											im->cP.vertical	= TRUE;
										}
										break;
							case 'n':			// Set filename
										nextWord( buf, &li );
										sprintf( im->name, "%s", buf );
										break;
							case 'm':  // Frame
								li++;
								switch( *li )
								{
									case 'x':
											READ_VAR("%d", &(im->cP.fwidth) );
											im->cP.cutFrame = TRUE;
											break;
									case 'y':
											READ_VAR("%d", &(im->cP.fheight) );
											im->cP.cutFrame = TRUE;
											break;
									default: 
											li--;
											READ_VAR("%d", &(im->cP.frame) );
											im->cP.cutFrame = TRUE;											
											break;
								}
								break;	
							case 'X':	READ_VAR( "%lf", &ci->x[0] );
										break;
							case 'Y':	READ_VAR( "%lf", &ci->x[1] );
										break;
							case 'Z':	READ_VAR( "%lf", &ci->x[2] );
										break;
							case 'S':	nextWord( buf, &li );		
									sscanf( buf, "%d,%d,%d,%d", &im->selection.left, &im->selection.right, &im->selection.top, &im->selection.bottom );
									break;
							case 'C':	nextWord( buf, &li );		
									sscanf( buf, "%d,%d,%d,%d", &im->selection.left, &im->selection.right, &im->selection.top, &im->selection.bottom );
									im->cP.cutFrame = TRUE;
									break;
							default: 
										li++;
										break;
						}
					}

					numIm++;
					break;	
		case 't':       // Triangle
					li = &(line[1]);
					i = 0;
					while( *li != 0)
					{
						switch(*li)
						{
							case ' ' : 
							case '\t': li++; break;
							case 'i' : READ_VAR( "%ld", &(gl->t[nt].nIm)); 	break;
							default  : if(i<3)
										{
											li--;
											READ_VAR( "%d", &(gl->t[nt].vert[i]) ); 
											i++;
										}
										else
											li++;
										break;
						}
					}
					nt++;
					break;
		case 'c':		// Control Points
					
					gl->cpt[numPts].type = 0;	// default : optimize r
					if(  ReadControlPoint( &(gl->cpt[numPts]), &(line[1]) ) != 0 )
					{
						PrintError("Syntax error in script: Line %d", lineNum);
						return -1;
					}
					numPts++;
					break;

		case 'm':		// Mode description
					if( ReadModeDescription( &gl->sP, &(line[1]) ) != 0 )
					{
						PrintError( "Syntax error in line %d" , lineNum);
						free( script );
						return -1;
					}
					break;
								

		case 'v':		// Variables to optimize
					li = &(line[1]);
					while( *li != 0)
					{
						switch(*li)
						{
							case 'y':	READ_OPT_VAR(yaw);
										break;
							case 'p':	READ_OPT_VAR(pitch);
										break;
							case 'r':	READ_OPT_VAR(roll);
										break;
							case 'v':	READ_OPT_VAR(hfov);
										break;
							case 'a':	READ_OPT_VAR(a);
										break;
							case 'b':	READ_OPT_VAR(b);
										break;
							case 'c':	READ_OPT_VAR(c);
										break;
							case 'd':	READ_OPT_VAR(d);
										break;
							case 'e':	READ_OPT_VAR(e);
										break;
							case 'X':	READ_VAR( "%d", &k );
										if( k>=0 && k<gl->numIm )
											gl->cim[k].set[0] = FALSE;
										break;
							case 'Y':	READ_VAR( "%d", &k );
										if( k>=0 && k<gl->numIm )
											gl->cim[k].set[1] = FALSE;
										break;
							case 'Z':	READ_VAR( "%d", &k );
										if( k>=0 && k<gl->numIm )
											gl->cim[k].set[2] = FALSE;
										break;
							default:
								li++;
								break;
						}
						
					}
					break;
		case 'p':		// panorama 
					gl->pano.format 	= 2; // _equirectangular by default
					gl->pano.hfov		= 360.0;
					if( ReadImageDescription( &(gl->pano), &(gl->st), &(line[1]) ) != 0 )
					{
						PrintError( "Syntax error in line %d" , lineNum);
						return -1;
					}
					// Convert format; rectilinear and panorama ok
					
					if( gl->pano.format == 2 )
						gl->pano.format = _equirectangular;
						
						
					if( gl->pano.format == _rectilinear && gl->pano.hfov >= 180.0 )
					{
						PrintError( "Destination image must have FOV < 180" );
						return -1;
					}
					break;
		case '*':	// End of script-data
					*lineStart = 0; *ch = 0;
					break;
		 default: break;
		}
	}

	// Set up Panorama description
	
	if( gl->pano.width == 0 && gl->im[0].hfov != 0.0)  // Set default for panorama width based on first image
	{
		gl->pano.width = ( gl->pano.hfov / gl->im[0].hfov ) * gl->im[0].width;
		gl->pano.width /= 10; gl->pano.width *= 10; // Round to multiple of 10
	}

	if( gl->pano.height == 0 )
		gl->pano.height = gl->pano.width/2;
		

//	Set up global information structure
	gl->numParam 	= n;
	gl->data		= NULL;

// Set initial values for linked variables
	for(i=0; i<gl->numIm; i++){
		k = gl->opt[i].yaw - 2;
		if( k >= 0 ) gl->im[i].yaw = gl->im[ k ].yaw;

		k = gl->opt[i].pitch - 2;
		if( k >= 0 ) gl->im[i].pitch = gl->im[ k ].pitch;

		k = gl->opt[i].roll - 2;
		if( k >= 0 ) gl->im[i].roll = gl->im[ k ].roll;

		k = gl->opt[i].hfov - 2;
		if( k >= 0 ) gl->im[i].hfov = gl->im[ k ].hfov;

		k = gl->opt[i].a - 2;
		if( k >= 0 ) gl->im[i].cP.radial_params[0][3] = gl->im[ k ].cP.radial_params[0][3];

		k = gl->opt[i].b - 2;
		if( k >= 0 ) gl->im[i].cP.radial_params[0][2] = gl->im[ k ].cP.radial_params[0][2];

		k = gl->opt[i].c - 2;
		if( k >= 0 ) gl->im[i].cP.radial_params[0][1] = gl->im[ k ].cP.radial_params[0][1];

		k = gl->opt[i].d - 2;
		if( k >= 0 ) gl->im[i].cP.horizontal_params[0] = gl->im[ k ].cP.horizontal_params[0];

		k = gl->opt[i].e - 2;
		if( k >= 0 ) gl->im[i].cP.vertical_params[0] = gl->im[ k ].cP.vertical_params[0];

		gl->im[i].cP.radial_params[0][0] = 1.0 - ( gl->im[i].cP.radial_params[0][3]
														+ gl->im[i].cP.radial_params[0][2]
														+ gl->im[i].cP.radial_params[0][1] ) ;
														
		SetEquColor( &(gl->im[i].cP) );
		
	}


	return 0;
}


// Report Results 

void WriteResults( char* script, fullPath *sfile,  AlignInfo *g, double ds( int i), int launch)
{
	char 		*res, **hres, line[1024], cmd[256];
	int 		optHfov;		 // Has hfov being optimized?
	int			opta,optb,optc;  // same for a,b,c - the polynomial corrections
	int         format;
	int i;

	setlocale(LC_ALL, "C");
		
	hres = (char**) mymalloc( strlen(script) + g->numIm * 600 + g->numPts * 200 + 10000 ); // Do we ever need more?
	if( hres == NULL )
	{
		PrintError("Not enough memory to create resultfile");
		return;
	}
	res = *hres;
	*res = 0;
	strcat( res, script );
	sprintf( line, "\n*\n\n" ); strcat( res, line );
	sprintf( line, "# ====================================================================\n");strcat( res, line );
	sprintf( line, "# Output  generated by Panorama Tools\n\n" );strcat( res, line );
	if( g->data != NULL )
		strcat( res, (char*) g->data );
	
		
	sprintf( line, "\n# Panorama description\n" );strcat( res, line );
	switch( g->pano.format )
	{
		case _equirectangular:		format = 2; break;
		case _rectilinear:			format = 0; break;
		case _panorama:				format = 1; break;
	}
		
	sprintf( line, "# p f%d w%ld h%ld v%g n\"%s\"\n\n", format, g->pano.width, g->pano.height, g->pano.hfov, g->pano.name );strcat( res, line );
	
	
	
	sprintf( line, "# Parameters for Each Input Image:\n" );strcat( res, line );
	sprintf( line, "# (*) - optimized         (p) - preset \n\n");strcat( res, line );
			
	for( i=0; i<g->numIm; i++ )
	{
		switch( g->im[i].format )
		{
			case _rectilinear 		: format = 0; break;
			case _panorama    		: format = 1; break;
			case _fisheye_circ		: format = 2; break;
			case _fisheye_ff  		: format = 3; break;
			case _equirectangular 	: format = 4; break;
			default: break;
		}
		sprintf( cmd, "o f%d ", format );

		if( g->opt[i].hfov == 1 ||
			( g->opt[i].hfov > 1 &&  g->opt[g->opt[i].hfov-2].hfov == 1 ))
			optHfov = 1;
		else
			optHfov = 0;

		if( g->opt[i].a == 1 ||
			( g->opt[i].a > 1 &&  g->opt[g->opt[i].a-2].a == 1 ))
			opta = 1;
		else
			opta = 0;
					
		if( g->opt[i].b == 1 ||
			( g->opt[i].b > 1 &&  g->opt[g->opt[i].b-2].b == 1 ))
			optb = 1;
		else
			optb = 0;
					
		if( g->opt[i].c == 1 ||
			( g->opt[i].c > 1 &&  g->opt[g->opt[i].c-2].c == 1 ))
			optc = 1;
		else
			optc = 0;

		sprintf( line, "# Image No %d:\n", i );strcat( res, line );
		sprintf( line, "# Yaw:	%g deg (%c)	Pitch:	%g deg (%c) \n# Roll:	%g deg (%c)	HFov:	%g deg (%c)\n# Polynomial Coefficients: a   %f (%c); b   %f (%c); c   %f (%c)\n# Horizontal Shift: %f (%c)   Vertical Shift:  %f (%c)\n",
					g->im[i].yaw, ( g->opt[i].yaw ? '*' : 'p' ),
					g->im[i].pitch,  ( g->opt[i].pitch  ? '*' : 'p' ),
					g->im[i].roll, ( g->opt[i].roll ? '*' : 'p' ),
					g->im[i].hfov,  (              optHfov  ? '*' : 'p' ),
					g->im[i].cP.radial_params[0][3], (opta ? '*':'p'),
					g->im[i].cP.radial_params[0][2], (optb ? '*':'p'),
					g->im[i].cP.radial_params[0][1], (optc ? '*':'p'),
					g->im[i].cP.horizontal_params[0], (g->opt[i].d ? '*':'p'),
					g->im[i].cP.vertical_params[0], (g->opt[i].e ? '*':'p')
					);
		strcat( res, line );
		sprintf( line, "r%g p%g y%g v%g ", g->im[i].roll,g->im[i].pitch,g->im[i].yaw,g->im[i].hfov);
		strcat( cmd, line );
		if( g->im[i].cP.radial )
		{
			sprintf( line, "a%f b%f c%f ", g->im[i].cP.radial_params[0][3], g->im[i].cP.radial_params[0][2], g->im[i].cP.radial_params[0][1]);
			strcat( cmd, line );
		}
		if( g->im[i].cP.cutFrame &&
		 !( g->im[i].selection.bottom != 0 || g->im[i].selection.right != 0 )) // g->im[i].format != _fisheye_circ && g->im[i].cP.cutFrame )
		{
			if( g->im[i].cP.frame != 0 )
			{
				sprintf( line, "m%ld ",g->im[i].cP.frame );
			}
			else
			{
				sprintf( line, "mx%ld my%d ",g->im[i].cP.fwidth, g->im[i].cP.fheight );
			}
			strcat( cmd, line );
		}

//		sprintf( line, "d%f e%f ", g->prefs[i]->c_prefs.horizontal_params[0], g->prefs[i]->c_prefs.vertical_params[0]);
//		strcat( cmd, line );
#if 0
		if( g->prefs[i]->colCorrect )
		{
			sprintf( line, "t%f,%f,%f,%f,%f,%f ", g->prefs[i]->ColCoeff[0][0],
														g->prefs[i]->ColCoeff[0][1],
														g->prefs[i]->ColCoeff[1][0],
														g->prefs[i]->ColCoeff[1][1],
														g->prefs[i]->ColCoeff[2][0],
														g->prefs[i]->ColCoeff[2][1]);
			strcat( cmd, line );
		}
#endif
		if( g->im[i].cP.horizontal )
		{
			sprintf( line, "d%f ",g->im[i].cP.horizontal_params[0]); 
			strcat( cmd, line );
		}
		if( g->im[i].cP.vertical )
		{
			sprintf( line, "e%f ",g->im[i].cP.vertical_params[0]);
			strcat( cmd, line );
		}
		if( g->im[i].cP.correction_mode & correction_mode_morph )
		{
			strcat( cmd, "o " );
		}
		if( g->im[i].selection.bottom != 0 || g->im[i].selection.right != 0 ){
			if( g->im[i].cP.cutFrame ){
				sprintf( line, " C%d,%d,%d,%d ",g->im[i].selection.left, g->im[i].selection.right,
						       g->im[i].selection.top, g->im[i].selection.bottom );
			}else{
				sprintf( line, " S%d,%d,%d,%d ",g->im[i].selection.left, g->im[i].selection.right,
						       g->im[i].selection.top, g->im[i].selection.bottom );
			}
			strcat( cmd, line );
		}


		sprintf(line, "u%d ", g->st.feather);
		strcat( cmd, line );
#if 0
		// Print filename
		if( g->im[i].name != NULL && strlen( g->im[i].name != 0 ) ){
			sprintf(line, " n\"%s\" ", g->im[i].name);
			strcat( cmd, line );
		}
	
#endif
		// Add command for stitcher, depending on g->st
		// If '-' option has been set (destName != 0), do nothing
		// else add stitch commands
		if( !*(g->st.destName) )
		{
			
			if( i== 0 )
			{
				sprintf(line, "-%s ", g->st.srcName);
				strcat( cmd, line );
			}
			else if( i == g->numIm-1 )
			{
				sprintf(line, "+%s ", g->st.srcName);
				strcat( cmd, line );
			}
			else
			{
				sprintf(line, "+%s -%s ", g->st.srcName, g->st.srcName);
				strcat( cmd, line );
			}
			// Print Feather commands
		}
		
				
		
		if( opta || optb || optc )
		{
			sprintf(line,"# 4th polynomial coefficient: %g\n", 1.0 - ( g->im[i].cP.radial_params[0][3]
																 + g->im[i].cP.radial_params[0][2]
																 + g->im[i].cP.radial_params[0][1] ) );
			strcat( res, line );
		}
		sprintf(line, "# Command for Panorama Creation: \n%s\n", cmd );
		strcat( res, line );
		sprintf(line, "\n");strcat( res, line );
	} // numIm

	if( g->numPts > 0 ) // Display Controlpoint distances
	{
		sprintf(line, "\n");strcat( res, line );
		sprintf( line, "# ====================================================================\n");strcat( res, line );
		sprintf( line, "# Control Points: Distance between desired and fitted Position (in Pixels)\n\n");strcat( res, line );
			
		for( i=0; i<g->numPts; i++ )
		{
			sprintf( line, "# Control Point No %d:  %g\n", i , sqrt ( ds(i)) );strcat( res, line );
		}
		// Print optimum positions for points in panorama
		for( i=0; i<g->numPts; i++ )
		{
			double x[2],y[2],xd,yd;
			
			if(g->cpt[i].num[0] != g->cpt[i].num[1])
			{
				GetControlPointCoordinates(i, x, y, g );
				// Write only points inside image
				if( x[0] >= 0.0 && x[0] < g->pano.width && x[1] >= 0.0 && x[1] < g->pano.width &&
					y[0] >= 0.0 && y[0] < g->pano.height && y[1] >= 0.0 && y[1] < g->pano.height)
				{
					xd = (x[0]+x[1]) / 2.0;
					yd = (y[0]+y[1]) / 2.0;
					sprintf( line, "C i%d  x%g y%g X%g Y%g\n", g->cpt[i].num[0] , x[0], y[0], xd, yd );strcat( res, line );
					sprintf( line, "C i%d  x%g y%g X%g Y%g\n", g->cpt[i].num[1] , x[1], y[1], xd, yd );strcat( res, line );
				}
			}
		}
	}
	if( WriteScript( res, sfile, launch ) != 0 )
	{
		PrintError("Could not write results to scriptfile");
		free( res );
		return;
	}
	if( hres ) myfree( (void**)hres );
	return;
}



// Reads 'adjust' parameters from script 'sfile'
// return 0 on success/ -1 if failed

int readAdjust( aPrefs *p,  fullPath* sfile, int insert, sPrefs *sP )
{
	char*				script;
	// Variables used by parser
	
	char 				line[256], *ch;
	int 				lineNum = 0;
	int 				seto;
	

	setlocale(LC_ALL, "C");


	// Set prefs and sBuf to defaults

	SetAdjustDefaults( p );




	script = LoadScript( sfile );
	if( script == NULL )
			return -1;


	// Parse script	
	
	ch = script;
	seto = FALSE;

	while( *ch != 0 )
	{
		lineNum++;

		while(*ch == '\n')
			ch++;

		// read a line of text into line[];

		nextLine( line, &ch );
		// parse line; use only if first character is p,o,m
	
		switch( line[0] )
		{
		case 'o':	// Image description
					if( !seto ) // Read only _one_ image
					{
						if( ReadImageDescription( &(p->im), &(p->sBuf), &(line[1]) ) != 0 )
						{
							PrintError( "Syntax error in line %d" , lineNum);
							free( script );
							return -1;
						}
						seto = TRUE;
					}
					break;
		case 'm':		// Mode description
					if( ReadModeDescription( sP, &(line[1]) ) != 0 )
					{
						PrintError( "Syntax error in line %d" , lineNum);
						free( script );
						return -1;
					}
					break;
		case 'p':		// panorama 
					p->pano.format 	= 2; // _equirectangular by default
					p->pano.hfov	= 360.0;
					if( ReadImageDescription( &(p->pano), &(p->sBuf), &(line[1]) ) != 0 )
					{
						PrintError( "Syntax error in line %d" , lineNum);
						free( script );
						return -1;
					}
					// Convert format; rectilinear and panorama ok
					
					if( p->pano.format == 2 )
						p->pano.format = _equirectangular;
						
					if( p->pano.height == 0 )
						p->pano.height = p->pano.width/2;
						
					if( p->pano.format == _rectilinear && p->pano.hfov >= 180.0 )
					{
						PrintError( "Destination image must have FOV < 180" );
						free( script );
						return -1;
					}
					break;

		 default: break;
		}
	}
	
	if( !seto  )
	{
		PrintError( "Syntax error in scriptfile" );
		free( script );
		return -1;
	}
	
	// Create and Write changed scriptfile if inserting

	if( insert )
	{
		seto = FALSE; ch = script;


		while( *ch != 0 && !seto )
		{
			while(*ch == '\n')
				ch++;
			if( *ch == 'o' )
				seto = TRUE;
			else
			{
				while(*ch != '\n' && *ch != 0)
					ch++;
			}
		}
		if( *ch == 'o' )  *ch = '!';
		
		// If this is the last image to convert: recover script file

		seto = FALSE; ch = script;

		while( *ch != 0 && !seto )
		{
			while(*ch == '\n')
				ch++;
			if( *ch == 'o' )
				seto = TRUE;
			else
			{
				while(*ch != '\n' && *ch != 0)
					ch++;
			}
		}
		if( seto == FALSE ) // No more images to convert
		{
			ch = script;

			while( *ch != 0  )
			{
				while(*ch == '\n')
					ch++;
				if( *ch == '!' )
					*ch = 'o';
				else
				{
					while(*ch != '\n' && *ch != 0)
						ch++;
				}
			}
		}
			

		if( WriteScript( script, sfile, 0 ) != 0 )
		{
			PrintError("Could not write scriptfile");
			free( script );
			return -1;
		}
	}

	free( script);
	return 0;
}


void readControlPoints(char* script, controlPoint *cp )
{
	struct controlPoint defCn;

	// Variables used by parser
	
	char 				line[256], *ch ,*lineStart;
	int 				lineNum = 0;
	int 				i;
	int 				numPts;


	int 				n=0; // Number of parameters to optimize

	setlocale(LC_ALL, "C");

	defCn.num[0] 	= 	defCn.num[1] = -1;
	defCn.type		=	0;
	defCn.x[0] 	= 	defCn.x[1] = 	defCn.y[0] 	= 	defCn.y[1] = 0;

	for(i=0; i< NUMPTS; i++)
		memcpy( &(cp[i]), &defCn, sizeof( struct controlPoint ) );
	
	numPts = 0; // reused as indices


	// Parse script	
	
	ch = script;
	
	while( *ch != 0 )
	{
		lineNum++;

		while(*ch == '\n')
			ch++;

		lineStart = ch;

		// read a line of text into line[];

		nextLine( line, &ch );

		// parse line; use only if first character is c
	
		switch( line[0] )
		{
			case 'c':		// Control Points
				defCn.num[0] 	= 	defCn.num[1] = -1;
				defCn.type		=	0;
				defCn.x[0] 	= 	defCn.x[1] = 	defCn.y[0] 	= 	defCn.y[1] = 0;
				
				if( ReadControlPoint( &defCn, &(line[1]) ) != 0 )
				{
					PrintError("Error in line %d", lineNum);
					return;
				}
				if( defCn.num[1] == -1 ) 	// We found a partial controlpoint
				{
					*lineStart = 0; 		// script ends here
					memcpy( &(cp[numPts]), &defCn, sizeof( struct controlPoint ) );
					numPts++;
				}
				break;
			case '*':	// End of script-data
				*lineStart = 0; *ch = 0;
				break;
		 	default: 
				break;
		}
	}
}


// Fill 'word' with word starting at (*ch). Advance *ch

void nextWord( register char* word, char** ch )
{
	register char *c;
	c = *ch;
	
	c++;
	if( *c == '\"' )
	{
		c++;
		while( *c != '\"' && *c != 0 )
			*word++ = *c++;
	}
	else
	{
		while( !isspace(*c) && *c != 0 )
		{
			*word++ = *c++;
		}
	}
	*word = 0;
	*ch  = c;
}

// Fill 'line' with line starting at (*ch). Advance *ch

void nextLine( register char* line, char** ch )
{
	register char *c;
	register int i;
	
	c = *ch;

	while(*c == '\n')
		c++;

	// read a line of text into line[];
		
	i=0;
	
	while( *c != 0 && *c != '\n' && i++<255)
		*line++ = *c++;
	*line = 0;
	*ch = c;
}

// Number of lines in script starting with character 'first' 

int numLines( char* script, char first )
{
	register char *ch;
	int result = 0;
	
	ch = script;
	
	while( *ch != 0 )
	{
		// Advance to linestart
		while(*ch == '\n')
			ch++;
		if( *ch == first )
			result++;

		while(*ch != '\n' && *ch != 0)
			ch++;
	}
	return result;
}

#undef  MY_SSCANF
#define MY_SSCANF( str, format, ptr )		if( sscanf( str, format, ptr ) != 1 )   \
												return -1;							\

#undef READ_VAR
#define READ_VAR(format, ptr )		nextWord( buf, &ch );			\
									MY_SSCANF( buf, format, ptr );


// Parse a line describing a single Controlpoint

static int ReadControlPoint( controlPoint * cptr, char *line)
{
	controlPoint		cp;
	char *ch	= line;
	int setn, setN, setx, setX, sety, setY; 
	char buf[256];
	
	
	setn = setN = setx= setX = sety = setY = FALSE;
	
	memcpy(  &cp, cptr, sizeof(controlPoint) );
	
	while( *ch != 0)
	{
		switch(*ch)
		{
			case 't':	READ_VAR("%ld", &(cp.type)); 	
						break;
			case 'n':	READ_VAR("%ld", &(cp.num[0]));
						setn = TRUE;
						break;
			case 'N':	READ_VAR("%ld", &(cp.num[1]));
						setN = TRUE;
						break;
			case 'x':	READ_VAR("%lf", &(cp.x[0]));
						setx = TRUE;
						break;
			case 'X':	READ_VAR("%lf", &(cp.x[1]));
						setX = TRUE;
						break;
			case 'y':	READ_VAR("%lf", &(cp.y[0]));
						sety = TRUE;
						break;
			case 'Y':	READ_VAR("%lf", &(cp.y[1]));
						setY = TRUE;
						break;
			case 'i':	READ_VAR("%ld", &(cp.num[0]));
						cp.num[1] = cp.num[0];
						setn = TRUE;
						setN = TRUE;
			default: 	ch++;
						break;
		}
	}
	
	// Check values
	
	if( setn	== FALSE || setN	== FALSE || setx == FALSE || setX == FALSE ||
		sety	== FALSE || setY 	== FALSE )
	{
			PrintError("Missing Control Point Parameter");
			return -1;
	}
	else if( cp.type != 0 && cp.type != 1 && cp.type != 2 )
	{
			PrintError("Control Point Type must be 0,1, or 2");
			return -1;
	}
	else if( cp.x[0] < 0 || cp.y[0] < 0 || cp.x[1] < 0 || cp.y[1] < 0)
	{
			PrintError("Pixel Coordinates must be positive");
			return -1;
	}
	else // looks ok so far
	{
		memcpy( cptr, &cp, sizeof(controlPoint) );
		return 0;
	}
}


// Parse a line describing a single image

static int ReadImageDescription( Image *imPtr, stBuf *sPtr, char *line )
{
	Image im;
	stBuf sBuf;
	char *ch = line;
	char buf[256];
	int	 i;

	
	memcpy( &im, 	imPtr,	 sizeof(Image) );
	memcpy( &sBuf, 	sPtr,	 sizeof(stBuf ));
	
	while( *ch != 0)
	{
		switch(*ch)
		{
			case 'v':	READ_VAR( "%lf", &im.hfov );
						break;
			case 'a':	READ_VAR( "%lf", &(im.cP.radial_params[0][3]));
						im.cP.radial 	= TRUE;
						break;
			case 'b':	READ_VAR("%lf", &(im.cP.radial_params[0][2]));
						im.cP.radial 	= TRUE;
						break;
			case 'c':	READ_VAR("%lf", &(im.cP.radial_params[0][1]));
						im.cP.radial	= TRUE;
						break;
			case 'f':	READ_VAR( "%d", &im.format );
						if( im.format == _panorama || im.format == _equirectangular )
							im.cP.correction_mode |= correction_mode_vertical;
						break;
			case 'y':	READ_VAR( "%lf", &im.yaw);
						break;
			case 'p':	READ_VAR( "%lf", &im.pitch);
						break;
			case 'r':	READ_VAR( "%lf", &im.roll);
						break;
			case 'd':	READ_VAR("%lf", &(im.cP.horizontal_params[0]));
						im.cP.horizontal 	= TRUE;
						break;
			case 'e':	READ_VAR("%lf", &(im.cP.vertical_params[0]));
						im.cP.vertical = TRUE;
						break;
			case '+':	nextWord( buf, &ch );
						sprintf( sBuf.srcName, "%s", buf);
						break;
			case '-':	nextWord( buf, &ch );
						sprintf( sBuf.destName, "%s", buf );
						break;
			case 'u':	READ_VAR( "%d", &(sBuf.feather) );
						break;
			case 's':	READ_VAR( "%d", &sBuf.seam );
						{
							if(sBuf.seam != _dest)
								sBuf.seam = _middle;
						}
						break;
			case 'w':	READ_VAR( "%d", &im.width );
						break;
			case 'h':	READ_VAR("%d", &im.height);
						break;
			case 'o':	ch++;
						im.cP.correction_mode |= correction_mode_morph;
						break;
			case 'k':  // Colour correction
						READ_VAR( "%d", &i );
						// i &= 3;
						sBuf.colcorrect |= i&3;
						sBuf.colcorrect += (i+1)*4;
						break;	
			case 'm':  // Frame
						ch++;
						switch( *ch )
						{
							case 'x':
								READ_VAR( "%d", &im.cP.fwidth );
								im.cP.cutFrame = TRUE;
								break;
							case 'y':
								READ_VAR( "%d", &im.cP.fheight );
								im.cP.cutFrame = TRUE;
								break;
							default:
								ch--;
								READ_VAR( "%d", &(im.cP.frame) );
								im.cP.cutFrame = TRUE;											
								break;
						}
						break;
			case 'n':  // Name string (used for panorama format)
						nextWord( buf, &ch );
						strcpy( im.name, buf );
						break;	
			case 'S':  nextWord( buf, &ch );		
				   sscanf( buf, "%d,%d,%d,%d", &im.selection.left, &im.selection.right, &im.selection.top, &im.selection.bottom );
				   break;
			case 'C':  nextWord( buf, &ch );		
				   sscanf( buf, "%d,%d,%d,%d", &im.selection.left, &im.selection.right, &im.selection.top, &im.selection.bottom );
				   im.cP.cutFrame = TRUE;
				   break;
			default: 	ch++;
						break;
		}
	}
	
	// Set 4th polynomial parameter
					
	im.cP.radial_params[0][0] = 1.0 - ( im.cP.radial_params[0][3] + im.cP.radial_params[0][2]
																  + im.cP.radial_params[0][1] ) ;
	
	SetEquColor( &im.cP );
	SetCorrectionRadius( &im.cP );	
	// Do checks
	
	// appears ok
	
	memcpy( imPtr,	&im, sizeof(Image) );
	memcpy( sPtr,	&sBuf, sizeof(stBuf ) );
	return 0;
}

// Parse a line describing modes

static int ReadModeDescription( sPrefs *sP, char *line )
{
	sPrefs theSprefs;
	char *ch = line;
	char buf[256];

	setlocale(LC_ALL, "C");
	memcpy( &theSprefs, 	sP,	 sizeof(sPrefs) );
	
	while( *ch != 0)
	{
		switch(*ch)
		{
			case 'g':	READ_VAR( "%lf", &theSprefs.gamma );
						if( theSprefs.gamma <= 0.0 )
							return -1;
						break;
			case 'i':	READ_VAR( "%d", &theSprefs.interpolator );
						if( theSprefs.interpolator < 0 ||  theSprefs.interpolator > 7 )
							theSprefs.interpolator = 0;
						break;
			case 'p':	READ_VAR( "%d", &theSprefs.optCreatePano );
						if(theSprefs.optCreatePano != 0)
							theSprefs.optCreatePano = TRUE;
						break;
			default: 	ch++;
						break;
		}
	}
	
	// appears ok
	
	memcpy( sP,	 &theSprefs, 	sizeof(sPrefs) );
	return 0;
}

// Parse a string desscribing VRPanoOptions

int	getVRPanoOptions( VRPanoOptions *v, char *line )
{
	char 			*ch = line;
	char 			buf[256];
	VRPanoOptions 	VRopt;

	setlocale(LC_ALL, "C");
	memcpy( &VRopt, v, sizeof( VRPanoOptions ) );
		
	while( *ch != 0)
	{
		switch(*ch)
		{
			case 'w':	READ_VAR("%d", &VRopt.width); 
						break;
			case 'h':	READ_VAR("%d", &VRopt.height);
						break;
			case 'p':	READ_VAR("%lf", &VRopt.pan);
						break;
			case 't':	READ_VAR("%lf", &VRopt.tilt);
						break;
			case 'v':	READ_VAR("%lf", &VRopt.fov);
						break;
			case 'c':	READ_VAR("%d", &VRopt.codec);
						break;
			case 'q':	READ_VAR("%ld", &VRopt.cquality);
						break;
			case 'g':	READ_VAR("%ld", &VRopt.progressive);
						break;
			default: 	ch++;
						break;
		}
	}
	memcpy( v, &VRopt, sizeof( VRPanoOptions ) );
	return 0;
}



// Read coordinates of positions

int readPositions( char* script, transformCoord *tP )
{
	// Variables used by parser
	
	char 			    line[256], *ch ;
	int 				lineNum = 0;
	int 				nr=0,np=0;


	setlocale(LC_ALL, "C");
	// Determine number of images and control points


	tP->nump 	= numLines( script, 'P' );
	tP->numr 	= numLines( script, 'R' );

	// Allocate Space for Pointers to images, preferences and control points
	
	tP->r 		= (CoordInfo*)		malloc( tP->numr 	* sizeof(CoordInfo) );
	tP->p		= (CoordInfo*)		malloc( tP->nump 	* sizeof(CoordInfo) );

	if( tP->r == NULL || tP->p == NULL )
	{
		PrintError("Not enough memory");
		return -1;
	}


	// Parse script	
	
	ch = script;

	while( *ch != 0 )
	{
		lineNum++;

		while(*ch == '\n')
			ch++;

		// read a line of text into line[];

		nextLine( line, &ch );
		// parse line; use only if first character is p,o,m
	
		switch( line[0] )
		{
		case 'P':	// Coordinates as is
					if( ReadCoordinates( &tP->p[np++], &(line[1]) ) != 0 )
					{
						PrintError( "Syntax error in line %d" , lineNum);
						free( script );
						return -1;
					}
					break;
		case 'R':	// Coordinate values requested
					if( ReadCoordinates( &tP->r[nr++], &(line[1]) ) != 0 )
					{
						PrintError( "Syntax error in line %d" , lineNum);
						free( script );
						return -1;
					}
					break;
		 default: break;
		}
	}
	return 0;
}


static int ReadCoordinates( 	CoordInfo	*cp, char *line )
{
	CoordInfo	ci;
	char *ch = line;
	char buf[256];
	
	ci.num = ci.set[0] = ci.set[1] = ci.set[2] = 0;
	ci.x[0] = ci.x[1] = ci.x[2] = 1.0;

	
	while( *ch != 0)
	{
		switch(*ch)
		{
			case 'c':	READ_VAR( "%d", &ci.num );
						break;
			case 'i':	READ_VAR( "%d", &ci.num );
						ci.num -= 2;
						break;
			case 'X':	READ_VAR( "%lf", &ci.x[0] );
						ci.set[0] = TRUE;
						break;
			case 'Y':	READ_VAR( "%lf", &ci.x[1] );
						ci.set[1] = TRUE;
						break;
			case 'Z':	READ_VAR( "%lf", &ci.x[2] );
						ci.set[2] = TRUE;
						break;
			default: 	ch++;
						break;
		}
	}
	
	// appears ok
	
	memcpy( cp,	 &ci, 	sizeof(CoordInfo) );
	return 0;
}

int ReadMorphPoints( char *script, AlignInfo *gl, int nIm )
{
	// Variables used by parser
	
	char 			    line[256], *ch ;
	int 				lineNum = 0;
	int					np = 0;
	controlPoint		cp;
	void 				*tmp;


	setlocale(LC_ALL, "C");
	
	// Determine number of morph control points


	gl->numPts 	= numLines( script, 'C' );
	if( gl->numPts == 0 )
		return 0;

	// Allocate Space for Pointers to images, preferences and control points
	
	gl->cpt 	= (controlPoint*)	malloc( gl->numPts 	* sizeof(controlPoint) );

	if( gl->cpt == NULL )
	{
		PrintError("Not enough memory");
		return -1;
	}


	// Parse script	
	
	ch = script;

	while( *ch != 0 )
	{
		lineNum++;

		while(*ch == '\n')
			ch++;

		// read a line of text into line[];

		nextLine( line, &ch );
		// parse line; use only if first character is p,o,m
	
		switch( line[0] )
		{
		case 'C':	// Coordinates as is
					cp.type = 0;
					if(  ReadControlPoint( &cp, &(line[1]) ) != 0 )
					{
						PrintError( "Syntax error in line %d" , lineNum);
						free( script );
						return -1;
					}
					if( cp.num[0] == nIm )
					{
						cp.num[0] = 0;
						cp.num[1] = 1;
						memcpy( &gl->cpt[np], &cp, sizeof( controlPoint ));
						np++;
					}
		 default: break;
		}
	}
	

	tmp =  realloc( gl->cpt, np * sizeof( controlPoint ) );
	if( tmp == NULL )	return -1;
	gl->numPts=np; gl->cpt = (controlPoint*)tmp; 

	return np;

}

void SetCoordDefaults( CoordInfo *c, int num )
{
		c->num	= num;
		c->x[0] = (double) num;
		c->x[1] = c->x[2] = 0.0;
		c->set[0] = c->set[1] = c->set[2] = TRUE;
}


aPrefs* readAdjustLine( fullPath *theScript ){	
	sPrefs sP;
	aPrefs *aP = (aPrefs*)malloc(sizeof(aPrefs));
	if(aP==NULL) return NULL;
	SetAdjustDefaults( aP );
	SetSizeDefaults( &sP );

	if( readAdjust( aP, theScript, 1, &sP ) != 0 ){
		PrintError("Error processing script file" );
		return NULL;
	}

	// Use modevalues read from script
	aP->interpolator = sP.interpolator;
	aP->gamma	= sP.gamma;
				
	// Parse script again, now reading triangles if morphing requested
	if( aP->im.cP.correction_mode & correction_mode_morph ){
		char*				script;
		AlignInfo			ainf;
		int				nIm, nPts; // Number of image being processed
		Image				im[2];
					
		script = LoadScript( theScript) ;
		if( script != NULL ){	
			nIm = numLines( script, '!' ) - 1;
						
			if( nIm < 0)
			nIm = numLines( script, 'o' ) - 1;
					
			// Set ainf
			ainf.nt 	= 0;
			ainf.t		= NULL;
			ainf.numIm 	= 2;
			ainf.im		= im;
			memcpy( &ainf.pano, &aP->pano, sizeof( Image ));
			memcpy( &ainf.im[0], &aP->pano, sizeof( Image ));
			memcpy( &ainf.im[1], &aP->pano, sizeof( Image ));
						
			nPts = ReadMorphPoints( script, &ainf, nIm );
			if(nPts > 0){
				AddEdgePoints( &ainf );
				TriangulatePoints( &ainf, 1 );
				aP->nt = ainf.nt;
				if(aP->nt > 0){
					SortControlPoints( &ainf, 1 );
					SetSourceTriangles( &ainf, 1, &aP->td  );
					SetDestTriangles( &ainf, 1, &aP->ts  );
				}
			}
			if(ainf.numPts > 0) free(ainf.cpt);
			free( script );
		}
	}
	return aP;
}	


