#include <math.h>
#include "face.h"
#include "device.h"

/*for debugging*/
#define DRAW

#define SQR(x)          ((x)*(x))
#define SQRT(x)         ((REAL)sqrt((double)x))

/*vectors*/
#define NORMVector(v)           (SQRT(SQR((v)[0])+SQR((v)[1])+SQR((v)[2])))
#define COPYVector(a,b)         {a[0]=b[0];a[1]=b[1];a[2]=b[2];}
#define POINTByVector(p,v)      (X(p)*v[0] + Y(p)*v[1] + Z(p)*v[2])
#define POINTAsVector(p,v)      {v[0]=X(p); v[1]=Y(p); v[2]=Z(p);}
#define MAKEVector(v,x,y,z)     {v[0]=x; v[1]=y; v[2]=z;}
#define PRODUCTVector(u,v)      ((u)[0]*(v)[0]+(u)[1]*(v)[1]+(u)[2]*(v)[2])

/*views*/
#define VM(view)                        (view->M)

#define Project(view,p)         { /*p is a Point3DPtr*/       \
				  Point4D q; \
				  point3DTo4DView(view,p,&q)      \
				  p->x = q.x/q.w;\
				  p->y = q.y/q.w;\
				  p->z = q.z/q.w;\
				 }

#define PROJECTION(q,s) {       \
	s.x = q.x/q.w;                  \
	s.y = q.y/q.w;                  \
	s.z = q.z/q.w;                  \
}
#define COPOf(view)                     (view->COP)
#define VRPOf(view)                     (view->VRP)
#define U_Axis(view)            (view->u)
#define V_Axis(view)            (view->v)
#define N_Axis(view)            (view->n)

#define WC_COP_X(v)                     (X(COPOf(v))*U_Axis(v)[0] +     \
							 Y(COPOf(v))*V_Axis(v)[0] +      \
							 Z(COPOf(v))*N_Axis(v)[0] + X(VRPOf(v)))
#define WC_COP_Y(v)                     (X(COPOf(v))*U_Axis(v)[1] +     \
							 Y(COPOf(v))*V_Axis(v)[1] +      \
							 Z(COPOf(v))*N_Axis(v)[1] + Y(VRPOf(v)))
#define WC_COP_Z(v)                     (X(COPOf(v))*U_Axis(v)[2] +     \
							 Y(COPOf(v))*V_Axis(v)[2] +      \
							 Z(COPOf(v))*N_Axis(v)[2] + Z(VRPOf(v)))

#define SETSurfaceOfView(view,surface)  {view->surface = surface;}
#define GETSurfaceOfView(view)                  (view->surface)         

/*surfaces*/
#define VXMIN(surface)          (surface->x1)
#define VXMAX(surface)          (surface->x2)
#define VYMIN(surface)          (surface->y1)
#define VYMAX(surface)          (surface->y2)
#define DVX(surface)            (surface->dvx)
#define DVY(surface)            (surface->dvy)
#define HEIGHT(surface)         (surface->height)
#define SCREEN_X(s,x)           (VXMIN(s) + DVX(s)*((x) + 1.0))
#define SCREEN_Y(s,y)           (HEIGHT(s)-1-(VYMIN(s) + DVY(s)*((y) + 1.0)))
#define GETDrawableOfSurface(surface)   (surface->drawable)

/*matrices*/
#define MATRIX(m)                               (m->TM)
#define TRANSLATION(m,tx,ty,tz) setTranslationMatrix(m.TM,(tx),(ty),(tz))
#define IDENTITY(m)                             setIdentityMatrix(m.TM)
#define ZROTATION(m,a)                  setZRotationMatrix(m.TM,(a))
#define SCALING(m,sx,sy,sz)             setScalingMatrix(m.TM,(sx),(sy),(sz))


typedef REAL Vector[3];
typedef REAL Matrix[4][4];

typedef struct{
	REAL x,y,z,w;
} Point4D;

#define MAX_POLYGON     10

typedef struct{
	int posx,posy;          /*bottom left corner - if relevant*/
	int width,height;   /*of the surface*/
	REAL x1,x2,y1,y2;       /*viewport within the surface*/
	REAL dvx,dvy;           /*viewport x2-x1,y2-y1 each over 2*/
	DRAWABLE drawable;
	Point3D polygon[MAX_POLYGON];
	int polygonSize;
} Surface, *SurfacePtr;


typedef struct{
	Vector VPN,VUV;
	Point3D VRP,COP;
	REAL VPD,FCP,BCP;
	REAL Umin,Umax,Vmin,Vmax;

	Vector u,v,n;
	REAL D,dx,dy,px,py,Qu,Qv,Qn,Dmin,Dmax,Dz;

	Matrix M;

	Surface *surface; /*on which the view is displayed*/
} View;

#define NOOBJECT                                ((Object *)0)
#define FACEListOfObject(obj)   (obj->facelist)
#define FACESOfObject(obj)              (obj->face)
#define NUMFacesOfObject(obj)   (obj->numFaces)
#define LTMOfObject(obj)                (obj->L.TM)
#define GTMOfObject(obj)                (obj->G.TM)
#define CTMOfObject(obj)                (obj->C.TM)
#define CHILDOfObject(obj)              (obj->child)
#define MAXChilrenOfObject(obj) (obj->maxChildren)
#define LASTChildOfObject(obj)  (obj->n)
#define VERTEXOfObject(O) (VERTEXOfFace(atFaceList(O->facelist,INDEX(O->face))))
#define FLATTENObject(obj)      traverseObject(obj,&transformVerticesOfObject)

typedef struct{
	Matrix TM;
} TMatrix;

typedef struct object{
	FaceList *facelist;
	Index *face;                            /*list of indices of faces*/
	int numFaces;                           /*number of faces in the object*/
	TMatrix L,G,C;          /*local, global and current tm*/
	struct object **child;  /*dynamic array of pointers to objects*/
	int maxChildren;                        /*max number of children*/
	int n;                                          /*number of children*/
} Object, *ObjectPtr;



#define REDLight(light)                                 (light->intensity.red)
#define GREENLight(light)                               (light->intensity.green)
#define BLUELight(light)                                (light->intensity.blue)
#define POSITIONOfLight(light)                  (light->position)

#define NOOfLightsIn(lighting)                  (lighting->noOfLights)
#define AMBIENT(lighting)                               (lighting->ambient)
#define AMBIENTIntensity(lighting)              (lighting->ambient.intensity)
#define REDAmbient(lighting)                    (lighting->ambient.intensity.red)
#define GREENAmbient(lighting)                  (lighting->ambient.intensity.green)
#define BLUEAmbient(lighting)                   (lighting->ambient.intensity.blue)
#define REDLighting(lighting,i)                 (lighting->light[i].intensity.red)
#define GREENLighting(lighting,i)               (lighting->light[i].intensity.green)
#define BLUELighting(lighting,i)                (lighting->light[i].intensity.blue)
#define DIRECTIONOf(lighting,i)                 (lighting->light[i].direction)
#define POSITIONOf(lighting,i)                  (lighting->light[i].position)
#define LIGHTOf(lighting,i)                             (lighting->light[i])

typedef struct{
	Vector direction; /*for lights at infinity only*/
	Point3D position;
	Colour intensity;
} Light;

typedef struct{
	Light ambient;
	int noOfLights;
	Light light[MAXLIGHT];
} Lighting;

/*view.c*/
extern View *newView();
extern void setVPNView();
extern void setVPN();
extern void setVUVView();
extern void setVUV();
extern void setVRPView();
extern void setVRP();
extern void setCOPView();
extern void setCOP();
extern void setVPDView();
extern void setVPWindowView();
extern void setClipView();
extern void setView();
extern View *newView();
extern void displayFaceOnView();
extern void displayTreeOnView();

/*surface.c*/
extern Surface *newSurface();
extern void newPolygonOnSurface();
extern void addToPolygonOnSurface();
extern void displayPolygonOnSurface();

/*drawable.c*/
extern void initialiseDrawables();
extern DRAWABLE newDrawable();
extern void newPolygonOnDrawable();
extern void startPolygonOnDrawable();
extern void addToPolygonOnDrawable();
extern void setColourOnDrawable();
extern void displayPolygonOnDrawable();
extern void clearDrawable();
extern void setDrawable();

/*matrix.c*/
extern void setIdentityMatrix();
extern void multiplyMatrix();
extern void setTranslationMatrix();
extern void setZRotationMatrix();

/*object.c*/
extern Object *newObject();
extern void addFaceToObject();
extern void addChildToObject();
extern void setGlobalMatrixOfObject();
extern void setLocalMatrixOfObject();
extern void transformVerticesOfObject();
extern Object *cloneObject();

/*clip.c*/
extern void constructClipPolygon();
extern void doClipPolygon();

/*light.c*/
extern Shading *newShading();
extern Lighting *newLighting();
extern void changeAmbientLighting();
extern void addLightToLighting();
extern void setDirectionOfLight();
extern void setIntensityOfLight();
extern void setPositionOfLight();
