/* yacc.y */
%{
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include "error.h"

#include "readrad.h"
#include "color.h"
#include "point.h"
#include "pointlist.h"
#include "vector.h"
#include "vectorlist.h"
#include "vertex.h"
#include "vertexlist.h"
#include "patch.h"
#include "patchlist.h"
#include "material.h"
#include "materiallist.h"
#include "geom.h"
#include "geomlist.h"
#include "surface.h"
#include "compound.h"
#include "camera.h"

extern int yylex();
char yyfilename[200];

extern void CanvasSetSize(int hres, int vres);	/* in main.c */

static POINTLIST *currentPointList;
static VECTORLIST *currentNormalList;
static VERTEXLIST *currentVertexList;
static MATERIAL currentMaterial, *usedMaterial;

static RGB Black = {0., 0., 0.};
%}
%union { /* stack type */
	char	*string;
	float	val;
	int	ival;
	CAMERA 	*camera;
	GEOM	*geom;
	GEOMLIST *geomlist;
	MATERIAL *material;
	MATERIALLIST *materiallist;
	POINTLIST *pointlist;
	POINT	*point;
	VECTORLIST *vectorlist;
	VECTOR 	*vector;
	VERTEX 	*vertex;
	PATCH	*patch;
	PATCHLIST *patchlist;
	COLOR	color;
}	
%token 		tNEWMTL tKa tKd tKs tNs tEd tUSEMTL
%token		tEYEP tLOOKP tUP tFOV tSCREEN
%token          tLIST tEND tSURFACE tVERTEX tNORMAL tFACE 
%token	<val> 	tNUMBER
%token  <string> tSTRING
%type	<camera> cameradesclist cameradesc
%type	<geomlist> scene geomlist
%type   <materiallist> materiallist materiallib
%type	<material> material matproplist matprop materialname usematerial
%type 	<geom> geom aggregate list primitive surface newsurface
%type	<pointlist> points
%type	<point> point
%type 	<vectorlist> normals
%type	<vector> normal
%type	<patchlist> faces
%type	<patch> face
%type	<vertex> vertex
%type 	<color>	color
%type 	<val>	expr ParenExpr MExpr
%type	<ival>	iexpr
%left	'+' '-'
%left 	'*' '/' '%'
%left	UMINUS
%right	'^'
%%
scene:	 cameradesclist materiallib geomlist
        { $$ = World = $3; }
	;
cameradesclist: cameradesc cameradesclist
        |
        { $$ = &Camera; }
        ;
cameradesc: tEYEP expr expr expr
        { $$ = CameraSetEyep(&Camera, $2, $3, $4); }
        | tLOOKP expr expr expr
        { $$ = CameraSetLookp(&Camera, $2, $3, $4); }
        | tUP expr expr expr
        { $$ = CameraSetUpdir(&Camera, $2, $3, $4); }
        | tFOV expr
        { $$ = CameraSetFov(&Camera, $2); }
        | tSCREEN iexpr iexpr
        { CanvasSetSize($2, $3); $$ = &Camera; }
        ;
materiallib: materiallist
        { $$ = MaterialLib = $1; }
        ;
materiallist: material materiallist
        { $$ = MaterialListAdd($2, $1); }
        | material
        { $$ = MaterialListAdd(MaterialListCreate(), $1); }
        ;
material: tNEWMTL tSTRING matproplist
        { $$ = usedMaterial = MaterialCreate($2, &currentMaterial.Ka,
					     &currentMaterial.Kd,
					     &currentMaterial.Ks,
					     &currentMaterial.Ed,
					     currentMaterial.Ns); }
        ;
matproplist: matproplist matprop 
        { $$ = &currentMaterial; }
        | 
        { currentMaterial = defaultMaterial; $$ = &currentMaterial; }
        ;
matprop: tKd color
        { currentMaterial.Kd = $2; $$ = &currentMaterial; }
        | tEd color
        { currentMaterial.Ed = $2; $$ = &currentMaterial; }
        | tKa color
        { currentMaterial.Ka = $2; $$ = &currentMaterial; }
        | tKs color
        { currentMaterial.Ks = $2; $$ = &currentMaterial; }
        | tNs expr
        { currentMaterial.Ns = $2; $$ = &currentMaterial; }
        ;
usematerial: tUSEMTL tSTRING
        {
		if (!(usedMaterial = MaterialLookup(MaterialLib, $2))) {
			yyerror("Undefined material");
			usedMaterial = &defaultMaterial;
		}
		$$ = usedMaterial;
	}
        | material usematerial
        { $$ = $2; }
        |
        { $$ = usedMaterial; }
        ;
geomlist: usematerial geom geomlist
	{ $$ = GeomListAdd($3, $2); }
	| usematerial geom
        { $$ = GeomListAdd(GeomListCreate(), $2); }
	;
geom:	  primitive
	| aggregate
	;
aggregate: list
	;
list:	  tLIST geomlist tEND
	{ $$ = GeomCreate((void *)CompoundCreate($2), CompoundMethods()); }
	;
primitive: surface
        ;
surface: newsurface materialname points normals faces
	{ $$ = GeomCreate((void *)SurfaceCreate($2, $3, $4, currentVertexList, $5), SurfaceMethods()); }
	;
newsurface: tSURFACE
        {
		currentPointList = PointListCreate();
		currentNormalList = VectorListCreate();
		currentVertexList = VertexListCreate();
		$$ = (GEOM *)NULL;
        }
        ;
materialname: tSTRING
        { 
		if (!($$ = MaterialLookup(MaterialLib, $1))) {
			yyerror("Undefined material");
			$$ = MaterialDuplicate(&defaultMaterial);
		}
	}
        |
        {
		$$ = MaterialDuplicate(usedMaterial);
	}
        ;
points:  point points
        { $$ = currentPointList = PointListAdd(currentPointList, $1); }
        | 
	{ $$ = currentPointList; }
        ;
point:   tVERTEX expr expr expr
        { $$ = PointCreate($2, $3, $4); }
        ;
normals: normal normals
        { $$ = currentNormalList = VectorListAdd(currentNormalList, $1); }
        |
	{ $$ = currentNormalList; }
        ;
normal:  tNORMAL expr expr expr
        { $$ = VectorCreate($2, $3, $4); }
        ;
faces:  faces face
        { $$ = PatchListAdd($1, $2); }
        | 
        { $$ = PatchListCreate(); }
        ;
face:     tFACE vertex vertex vertex
        { $$ = PatchCreate(3, $2, $3, $4, (VERTEX *)NULL, FALSE); }
        | tFACE vertex vertex vertex vertex
        { $$ = PatchCreate(4, $2, $3, $4, $5, FALSE); }
        ; 
vertex:  iexpr '/' iexpr
        { 
		POINT *p; VECTOR *norm;
		
		if ((p = PointListGet(currentPointList, $1-1)) == (POINT *)NULL ||
		    (norm = VectorListGet(currentNormalList, $3-1)) == (VECTOR *)NULL) {
			Error(NULL, "Ongeldige index in lijn %d\n", lineno);
			$$ = (VERTEX *)NULL;
		
/* zowel hoekpunt als normaal gegeven: vertex kan gedeeld worden door meerdere
 * facetten */
		} else if (($$ = VertexListFind(currentVertexList, p, norm)) == (VERTEX *)NULL) {
			$$ = VertexCreate(p, norm, PatchListCreate(), &Black);
			currentVertexList = VertexListAdd(currentVertexList, $$);
		}
        }
        | iexpr
        { 
		POINT *p; 
		
		if ((p = PointListGet(currentPointList, $1-1)) == (POINT *)NULL) {
			Error(NULL, "Ongeldige index in lijn %d\n", lineno);
			$$ = (VERTEX *)NULL;
		} else {
/* enkel hoekpunt gegeven: vertex wordt niet gedeeld - normaal wordt berekend in
 * PatchCreate() */
			$$ = VertexCreate(p, (VECTOR *)NULL, PatchListCreate(), &Black);
			currentVertexList = VertexListAdd(currentVertexList, $$);
		}
	}
        ;
color:	  expr expr expr
	{ $$.r = $1; $$.g = $2; $$.b = $3; }
	;
iexpr:	  expr
	{ $$ = (int)$1; }
	;
expr:	  tNUMBER 
        | '+' tNUMBER		{ $$ = $2; }
        | '-' tNUMBER		{ $$ = - $2; }
        | ParenExpr
	;
ParenExpr: '(' MExpr ')'
        { $$ = $2; }
        ;
MExpr:    expr
        | MExpr '+' MExpr	{ $$ = $1 + $3; }
        | MExpr '-' MExpr	{ $$ = $1 - $3; }
        | MExpr '*' MExpr	{ $$ = $1 * $3; }
        | MExpr '/' MExpr	{ $$ = $1 / $3; }
        | MExpr '%' MExpr	{ $$ = (float)((int)$1 % (int)$3); }
        | MExpr '^' MExpr	{ $$ = (float)pow((double)$1, (double)$3); }
        | '-' MExpr %prec UMINUS  { $$ = - $2; }
        ;
%%
void yyerror(char *s)
{
	Error(NULL, "%s near line %d", s, lineno);
}




