#ifndef lint
static char *sccsid = "%W% (Steve Hill) %G%";
#endif

/* parse.c
 *
 * A parser for a simple input language to drive a ray tracer.
 *
 * It constructs forms for display directly without constructing
 * intermediate parse trees.
 */

#include <stdio.h>

#include "basetype.h"
#include "cartesian.h"
#include "error.h"
#include "malloc.h"
#include "matrix.h"
#include "vector.h"
#include "colour.h"
#include "point.h"
#include "indexlist.h"
#include "ray.h"
#include "surface.h"
#include "bitmap.h"
#include "pattern.h"
#include "patterns.h"
#include "quadric.h"
#include "solid.h"
#include "bbox.h"
#include "solidtrans.h"
#include "shapes.h"
#include "list.h"
#include "light.h"
#include "world.h"

#include "lex.h"
#include "assoclist.h"
#include "parse.h"

#include "sunbitmap.h"

/* surface_list, pattern_list, solid_list
 *
 */

assoc_list_t	*surface_list = AssocListNull,
		*pattern_list = AssocListNull,
		*solid_list   = AssocListNull;


void
ParseError(message)
char	*message;
{
	fprintf(stderr, "Error at line %d in file %s\n",
		line_number, lex_file_name);
	fprintf(stderr, "%s\n", message);
	if (TokenIs(EOF_TOKEN))
		fprintf(stderr, "Unexpected end of file\n");
	else
		fprintf(stderr, "Found '%s'\n", token);
	exit(1);
}

void
ParseWarn(message)
char	*message;
{
	fprintf(stderr, "Warning at line %d of file %s\n",
		line_number, lex_file_name);
	fprintf(stderr, "%s\n", message);
}

world_t *
Parse(file_name, file)
char	*file_name;
FILE	*file;
{
	world_t	*world = WorldNull;

	/*printf("Parse\n");*/

	include_level = 0;
	InitialiseLex(file_name, file);
	GetToken();

	while (TokenIsnt(EOF_TOKEN) || include_level != 0)
	{
		if (TokenIs(INCLUDE_TOKEN))
		{
			char	*name;
			FILE	*include_file;

			GetToken();
			name = ParseName1();
			StackFile();
			include_file = fopen(name, "r");
			if (include_file == NULL)
				ParseError("File missing or unreadable");
			InitialiseLex(name, include_file);
			GetToken();
		}
		else
		if (TokenIs(EOF_TOKEN))
		{
			PopFile();
			GetToken();
		}
		else
		if (TokenIs(SURFACE_TOKEN))
		{
			char	*name;

			GetToken();
			name = ParseName();
			surface_list
				= AssocListAdd(name, (address_t) ParseSurface(),
					       surface_list);
			if (surface_list == AssocListNull)
				ParseError("Surface defined twice");
		}
		else
		if (TokenIs(PATTERN_TOKEN))
		{
			char	*name;

			GetToken();
			name = ParseName();
			pattern_list
				= AssocListAdd(name, (address_t) ParsePattern(),
					       pattern_list);
			if (pattern_list == AssocListNull)
				ParseError("Pattern defined twice");
		}
		else
		if (TokenIs(SOLID_TOKEN))
		{
			char	*name;

			GetToken();
			name = ParseName();
			solid_list
				= AssocListAdd(name, (address_t) ParseSolid(),
					       solid_list);
			if (solid_list == AssocListNull)
				ParseError("Previous solid defined twice");
		}
		else
		if (TokenIs(WORLD_TOKEN))
		{
			GetToken();
			world = ParseWorld();
		}
		else
			ParseError("Expected definition.");
	}
	if (world == WorldNull)
		ParseError("No world definition");

	CameraVectors(world);

	return(world);
}

/* ParseSurface
 *
 * Parse a surface description.  All the parameters are optional.
 * If any are omitted, then they are set to default values.
 */

surface_t *
ParseSurface()
{
	surface_t	*surface;

	/*fprintf(stderr, "ParseSurface\n");*/

	if (TokenIsIdentifier())
	{
		surface = (surface_t *) AssocListGet(token, surface_list);
		if (surface == SurfaceNull)
			ParseError("Undefined surface");
		GetToken();
		return(SurfaceCopy(surface));
	}

	if (TokenIsnt(OPEN_CURLY_TOKEN))
		ParseError("Missing open curly bracket");

	GetToken();

	surface = SurfaceCopy(&default_surface);

	while (TokenIsnt(CLOSE_CURLY_TOKEN))
	{
		if (TokenIs(AMBIENT_TOKEN))
		{
			ColourFree(surface->ambient);
			GetToken();
			surface->ambient  = ParseAmbient();
		}
		else
		if (TokenIs(DIFFUSE_TOKEN))
		{
			ColourFree(surface->diffuse);
			GetToken();
			surface->diffuse  = ParseDiffuse();
		}
		else
		if (TokenIs(MIRROR_TOKEN))
		{
			ColourFree(surface->mirror);
			GetToken();
			surface->mirror   = ParseMirror();
		}
		else
		if (TokenIs(TRANSMIT_TOKEN))
		{
			ColourFree(surface->transmit);
			GetToken();
			surface->transmit = ParseTransmit();
		}
		else
		if (TokenIs(SPECULAR_TOKEN))
		{
			GetToken();
			surface->specular = ParseSpecular();
			surface->power    = ParsePower();
		}
		else
		if (TokenIs(INDEX_TOKEN))
		{
			GetToken();
			surface->index = ParseReal();
		}
		else
			ParseError("Expected surface parameter.");
	
	}
	GetToken();
	return(surface);
}

/* ParsePattern
 *
 * Patterns come in several different varieties.  The first
 * compulsory field determines which pattern we are considering.
 */

pattern_t *
ParsePattern()
{
	pattern_t	*pattern;

	/*fprintf(stderr, "ParsePattern\n");*/

	if (TokenIsIdentifier())
	{
		pattern = (pattern_t *) AssocListGet(token, pattern_list);
		if (pattern == PatternNull)
			ParseError("Undefined pattern");
		GetToken();
		return(PatternCopy(pattern));
	}

	if (TokenIsnt(OPEN_CURLY_TOKEN))
		ParseError("Missing open curly bracket");

	GetToken();

	if (TokenIs(CHEQUE_TOKEN))
	{
		GetToken();
		pattern = ParseCheque();
	}
	else
	if (TokenIs(LAYER_TOKEN))
	{
		GetToken();
		pattern = ParseLayer();
	}
	else
	if (TokenIs(BITMAP_TOKEN))
	{
		GetToken();
		pattern = ParseBitMap();
	}
	else
	{
		fprintf(stderr, BITMAP_TOKEN);
		ParseError("Illegal pattern type");
	}

	if (TokenIsnt(CLOSE_CURLY_TOKEN))
		ParseError("Missing close curly bracket");

	GetToken();

	return(pattern);
}

/* ParseSolid
 *
 */

solid_t *
ParseSolid()
{
	solid_t	*solid;

	/*fprintf(stderr, "ParseSolid\n");*/

	if (TokenIsIdentifier())
	{
		solid = (solid_t *) AssocListGet(token, solid_list);
		if (solid == SolidNull)
			ParseError("Undefined solid");
		GetToken();
		return(SolidCopy(solid));
	}

	if (TokenIsnt(OPEN_CURLY_TOKEN))
		ParseError("Expected open curly bracket");

	GetToken();

	if (TokenIs(ATOM_TOKEN))
	{
		ParseWarn("Obsolete keyword (ATOM)");
		GetToken();
	}

	if (TokenIs(JOIN_TOKEN))
	{
		GetToken();
		solid = ParseJoin();
	}
	else
	if (TokenIs(JOIN_LIST_TOKEN))
	{
		solid_t	*solid1;

		GetToken();
		if (TokenIsnt(OPEN_CURLY_TOKEN))
			ParseError("Expected open curly bracket");
		GetToken();
		solid1 = ParseSolid();
		solid  = ParseJoinList(solid1);
	}
	else
	if (TokenIs(MEET_TOKEN))
	{
		GetToken();
		solid = ParseMeet();
	}
	else
	if (TokenIs(MEET_LIST_TOKEN))
	{
		solid_t	*solid1;

		GetToken();
		if (TokenIsnt(OPEN_CURLY_TOKEN))
			ParseError("Expected open curly bracket");
		GetToken();
		solid1 = ParseSolid();
		solid  = ParseMeetList(solid1);
	}
	else
	if (TokenIs(SOLID_TOKEN))
	{
		GetToken();
		solid = ParseSolid();
	}
	else
		solid = ParseAtom();

	solid = ParseSolidOptions(solid);

	return(solid);
}

char *
ParseName()
{
	char	*name;

	/*fprintf(stderr, "ParseName\n");*/

	if (!TokenIsIdentifier())
		ParseError("Name expected");

	name = malloc(strlen(token) + 1);
	strcpy(name, token);
	GetToken();

	return(name);
}

char *
ParseName1()
{
	char	*name;

	if (!TokenIsIdentifier())
		ParseError("Name expected");

	name = malloc(strlen(token) + 1);
	strcpy(name, token);
	return(name);
}

colour_t *
ParseAmbient()
{
	/*fprintf(stderr, "ParseAmbient\n");*/

	return(ParseColour());
}

colour_t *
ParseDiffuse()
{
	/*fprintf(stderr, "ParseDiffuse\n");*/

	return(ParseColour());
}

colour_t *
ParseMirror()
{
	/*fprintf(stderr, "ParseMirror\n");*/

	return(ParseColour());
}

colour_t *
ParseTransmit()
{
	return(ParseColour());
}

colour_t *
ParseColour()
{
	real_t		rgb[3];
	int		i;

	/*fprintf(stderr, "ParseColour\n");*/

	for (i = 0; i < 3; i++)
		rgb[i] = ParseReal();

	return(Colour(rgb[0], rgb[1], rgb[2]));
}

real_t
ParseSpecular()
{
	/*fprintf(stderr, "ParseSpecular\n");*/

	return(ParseReal());
}

real_t
ParsePower()
{
	/*fprintf(stderr, "ParsePower\n");*/

	return(ParseReal());
}

real_t
ParseReal()
{
	real_t	r;

	/*fprintf(stderr, "ParseReal\n");*/

	if (!TokenIsNumber())
		ParseError("Expected real number");

	sscanf(token, "%lf", &r);
	GetToken();

	return(r);
}

real_t
ParseAngle()
{
	real_t	angle;

	angle = ParseReal();

	if (TokenIs(DEG_TOKEN))
	{
		angle = (angle * REAL_PI) / 180.0;
		GetToken();
	}
	return(angle);
}

int
ParseInteger()
{
	int	i;

	/*fprintf(stderr, "ParseInteger\n");*/

	if (!TokenIsNumber())
		ParseError("Expected integer");

	sscanf(token, "%d", &i);
	GetToken();

	return(i);
}

pattern_t *
ParseCheque()
{
	real_t		x_res, y_res, z_res;
	surface_t	*surface1, *surface2;
	pattern_t	*pattern;

	/*fprintf(stderr, "ParseCheque\n");*/

	if (TokenIsnt(RES_TOKEN))
		ParseError("Expected resolution of cheque pattern");
	GetToken();
	x_res = ParseReal();
	y_res = ParseReal();
	z_res = ParseReal();

	if (TokenIsnt(SURFACE1_TOKEN))
		ParseError("Expected first surface of cheque pattern");
	GetToken();
	surface1 = ParseSurface();

	if (TokenIsnt(SURFACE2_TOKEN))
		ParseError("Expected second surface of cheque pattern");
	GetToken();
	surface2 = ParseSurface();

	pattern = Cheque(x_res, y_res, z_res, surface1, surface2);
	return(pattern);
}

pattern_t *
ParseLayer()
{
	real_t		res;
	axis_t		axis;
	surface_t	*surface1, *surface2;
	pattern_t	*pattern;

	/*fprintf(stderr, "ParseLayer\n");*/

	if (TokenIsnt(RES_TOKEN))
		ParseError("Expected resolution of layer pattern");
	GetToken();
	res = ParseReal();

	axis = ParseAtomAxis();
	
	if (TokenIsnt(SURFACE1_TOKEN))
		ParseError("Expected first surface of layer pattern");
	GetToken();
	surface1 = ParseSurface();

	if (TokenIsnt(SURFACE2_TOKEN))
		ParseError("Expected second surface of layer pattern");
	GetToken();
	surface2 = ParseSurface();

	pattern = Layer(res, axis, surface1, surface2);
	return(pattern);
}

pattern_t *
ParseBitMap()
{
	real_t		x_size, z_size;
	surface_t	*surface;
	pattern_t	*pattern;
	char		*file;
	bm_t		*bm;

	if (TokenIsnt(RES_TOKEN))
		ParseError("Expected resolution of bitmap pattern");
	GetToken();
	x_size = ParseReal();
	z_size = ParseReal();

	if (TokenIsnt(SURFACE1_TOKEN))
		ParseError("Expected surface of bitmap pattern");
	GetToken();
	surface = ParseSurface();

	if (TokenIsnt(FILE_TOKEN))
		ParseError("Expected file for bitmap");
	GetToken();

	file = ParseName();
	
	read_sun_raster(file, &bm);

	pattern = BitMap(x_size, z_size, surface, bm);
	return(pattern);
}

axis_t
ParseAxis()
{
	axis_t	axis;

	/*fprintf(stderr, "ParseAxis\n");*/

	if (TokenIs(X_AXIS_TOKEN))
		axis = X_AXIS;
	else
	if (TokenIs(Y_AXIS_TOKEN))
		axis = Y_AXIS;
	else
	if (TokenIs(Z_AXIS_TOKEN))
		axis = Z_AXIS;
	else
		ParseError("Illegal axis type");

	GetToken();
	return(axis);
}

axis_t
ParseAtomAxis()
{
	/*fprintf(stderr, "ParseAtomAxis\n");*/

	if (TokenIs(AXIS_TOKEN))
	{
		ParseWarn("Obsolete keyword (AXIS)");
		GetToken();
	}
	return(ParseAxis());
}

solid_t *
ParseAtom()
{
	solid_t	*solid;

	/*fprintf(stderr, "ParseAtom\n");*/

	if (TokenIs(SPHERE_TOKEN))
	{
		solid = Sphere();
		GetToken();
	}
	else
	if (TokenIs(CYLINDER_TOKEN))
	{
		GetToken();
		solid = Cylinder(ParseAtomAxis());
	}
	else
	if (TokenIs(SEMI_TOKEN))
	{
		ParseWarn("Obsolete keyword (SEMI) - use PLANE");
		GetToken();
		solid = SemiSpace(ParseAtomAxis());
	}
	else
	if (TokenIs(PLANE_TOKEN))
	{
		GetToken();
		solid = SemiSpace(ParseAtomAxis());
	}
	else
	if (TokenIs(PLANE3_TOKEN))
	{
		GetToken();
		solid = ParsePlane3();
	}
	else
	if (TokenIs(PLANE4_TOKEN))
	{
		GetToken();
		solid = ParsePlane4();
	}
	else
	if (TokenIs(CONE_TOKEN))
	{
		GetToken();
		solid = Cone(ParseAtomAxis());
	}
	else
	if (TokenIs(QUADRIC_TOKEN))
	{
		real_t	a, b, c, d, e, f, g, h, i, j;

		GetToken();
		a = ParseReal(); b = ParseReal();
		c = ParseReal(); d = ParseReal();
		e = ParseReal(); f = ParseReal();
		g = ParseReal(); h = ParseReal();
		i = ParseReal(); j = ParseReal();
		solid = General(a, d, e, g, b, f, h, c, i, j);
	}
	else
		ParseError("Illegal solid type");

	return(solid);
}

solid_t *
ParseJoin()
{
	solid_t	*solid1, *solid2;

	/*fprintf(stderr, "ParseJoin\n");*/

	solid1 = ParseSolid();
	solid2 = ParseSolid();

	return(SolidJoin(solid1, solid2));
}

solid_t *
ParseJoinList(solid1)
solid_t	*solid1;
{
	solid_t	*solid2;

	if (TokenIs(CLOSE_CURLY_TOKEN))
	{
		GetToken();
		return(solid1);
	}

	solid2 = ParseSolid();
	return(SolidJoin(solid1, ParseJoinList(solid2)));
}

solid_t *
ParseMeet()
{
	solid_t	*solid1, *solid2;

	/*fprintf(stderr, "ParseMeet\n");*/

	solid1 = ParseSolid();
	solid2 = ParseSolid();

	return(SolidMeet(solid1, solid2));
}

solid_t *
ParseMeetList(solid1)
solid_t	*solid1;
{
	solid_t	*solid2;

	if (TokenIs(CLOSE_CURLY_TOKEN))
	{
		GetToken();
		return(solid1);
	}

	solid2 = ParseSolid();
	return(SolidMeet(solid1, ParseMeetList(solid2)));
}

solid_t *
ParseSolidOptions(solid)
solid_t	*solid;
{
	/*fprintf(stderr, "ParseSolidOptions\n");*/

	while (TokenIsnt(CLOSE_CURLY_TOKEN))
	{
		if (TokenIs(TRANSLATE_TOKEN))
		{
			GetToken();
			solid = ParseTranslate(solid);
		}
		else
		if (TokenIs(ROTATE_TOKEN))
		{
			GetToken();
			solid = ParseRotate(solid);
		}
		else
		if (TokenIs(SCALE_TOKEN))
		{
			GetToken();
			solid = ParseScale(solid);
		}
		else
		if (TokenIs(PATTERN_TOKEN))
		{
			GetToken();
			solid = SolidPattern(solid, ParsePattern());
		}
		else
		if (TokenIs(SURFACE_TOKEN))
		{
			GetToken();
			solid = SolidPaint(solid, ParseSurface());
		}
		else
		if (TokenIs(BBOX_TOKEN))
		{
			GetToken();
			solid->bounded = TRUE;
		}
		else
		if (TokenIs(NEGATE_TOKEN))
		{
			GetToken();
			solid = SolidNegate(solid);
		}
		else
			ParseError("Illegal solid operation");
	}
	GetToken();
	return(solid);
}

solid_t *
ParseTranslate(solid)
solid_t	*solid;
{
	real_t	dx, dy, dz;

	/*fprintf(stderr, "ParseTranslate\n");*/

	dx = ParseReal();
	dy = ParseReal();
	dz = ParseReal();

	return(SolidT(solid, dx, dy, dz));
}

solid_t *
ParseRotate(solid)
solid_t	*solid;
{
	axis_t	axis;
	real_t	angle;

	/*fprintf(stderr, "ParseRotate\n");*/

	axis = ParseAxis();
	angle = ParseAngle();

	switch (axis)
	{
	case X_AXIS:
		return(SolidRx(solid, angle));

	case Y_AXIS:
		return(SolidRy(solid, angle));

	case Z_AXIS:
		return(SolidRz(solid, angle));

	default:
		FatalError(__FILE__, __LINE__, "ParseRotate: bad axis");
	}

	return(solid);
}

solid_t *
ParseScale(solid)
solid_t	*solid;
{
	real_t	sx, sy, sz;

	/*fprintf(stderr, "ParseScale\n");*/

	sx = ParseReal();
	sy = ParseReal();
	sz = ParseReal();

	return(SolidS(solid, sx, sy, sz));
}

point_t *
ParsePoint()
{
	real_t	x, y, z;

	/*fprintf(stderr, "ParsePoint\n");*/

	x = ParseReal();
	y = ParseReal();
	z = ParseReal();

	return(Point(x, y, z));
}

vector_t *
ParseVector()
{
	real_t	x, y, z;

	/*fprintf(stderr, "ParseVector\n");*/

	x = ParseReal();
	y = ParseReal();
	z = ParseReal();

	return(Vector(x, y, z));
}

light_t *
ParseLight()
{
	point_t		*point;
	colour_t	*colour;

	/*fprintf(stderr, "ParseLight\n");*/

	point  = ParsePoint();
	colour = ParseColour();

	return(Light(point, colour));
}

world_t *
ParseWorld()
{
	world_t	*world = WorldNull;

	/*fprintf(stderr, "ParseWorld\n");*/

	world = World();

	if (TokenIsnt(OPEN_CURLY_TOKEN))
		ParseError("Expected open brace");
	GetToken();

	while (TokenIsnt(CLOSE_CURLY_TOKEN))
	{
		if (TokenIs(SOLID_TOKEN))
		{
			GetToken();
			WorldSolid(world, ParseSolid());
		}
		else
		if (TokenIs(LOCATION_TOKEN))
		{
			GetToken();
			WorldLocation(world, ParsePoint());
		}
		else
		if (TokenIs(LOOK_TOKEN))
		{
			vector_t	*v;
			GetToken();
			v = ParseVector();
			WorldLook(world, v);
		}
		else
		if (TokenIs(LOOKAT_TOKEN))
		{
			point_t		*p;
			vector_t	*v;

			GetToken();
			p = ParsePoint();
			if (world->view_point == PointNull)
				ParseError("LOOKAT only valid after LOCATION");

			v = VectorZero();
			MakeVector(v, world->view_point, p);
			WorldLook(world, v);
			PointFree(p);
		}
		else
		if (TokenIs(UP_TOKEN))
		{
			GetToken();
			WorldUp(world, ParseVector());
		}
		else
		if (TokenIs(LIGHT_TOKEN))
		{
			light_t	*light;

			GetToken();
			light = ParseLight();
			WorldLight(world, light);
		}
		else
		if (TokenIs(AMBIENT_TOKEN))
		{
			GetToken();
			WorldAmbient(world, ParseAmbient());
		}
		else
		if (TokenIs(FILE_TOKEN))
		{
			GetToken();
			WorldFileName(world, ParseName());
		}
		else
		if (TokenIs(RESOLUTION_TOKEN))
		{
			int	x, y;

			GetToken();
			x = ParseInteger();
			y = ParseInteger();
			WorldResolution(world, x, y);
		}
		else
		if (TokenIs(PPLANE_TOKEN))
		{
			real_t	x, y, d;

			GetToken();
			x = ParseReal();
			y = ParseReal();
			d = ParseReal();
			WorldPplane(world, x, y, d);
		}
		else
		if (TokenIs(SKY_TOKEN))
		{
			GetToken();
			WorldSky(world, ParseColour());
		}
	}
	GetToken();

	if (world->solid == SolidNull)
		ParseError("No solid specified");

	if (world->lights == LightListNull)
		ParseError("No lights specified");
	return(world);
}

solid_t *
ParsePlane3()
{
	point_t	*point1, *point2, *point3;
	solid_t	*solid;

	point1 = ParsePoint();
	point2 = ParsePoint();
	point3 = ParsePoint();

	solid = Plane3(point1, point2, point3);

	PointFree(point1);
	PointFree(point2);
	PointFree(point3);

	return(solid);
}

solid_t *
ParsePlane4()
{
	point_t	*point1, *point2, *point3, *point4;
	solid_t	*solid;

	point1 = ParsePoint();
	point2 = ParsePoint();
	point3 = ParsePoint();
	point4 = ParsePoint();

	solid = Plane4(point1, point2, point3, point4);

	PointFree(point1);
	PointFree(point2);
	PointFree(point3);
	PointFree(point4);

	return(solid);
}
