/* surface.c */

#include "surface.h"
#include "Memory.h"

#ifdef BETTER_MEMMAN
static STORAGE *surfaceStor = (STORAGE *)NULL;
#define NEWSURFACE()  	(SURFACE *)New(sizeof(SURFACE), &surfaceStor)
#define DISPOSESURFACE(ptr) Dispose((unsigned char *)(ptr), &surfaceStor)
#else /*BETTER_MEMMAN*/
#define NEWSURFACE()	(SURFACE *)Alloc(sizeof(SURFACE))
#define DISPOSESURFACE(ptr) Free((char *)ptr, sizeof(SURFACE))
#endif /*BETTER_MEMMAN*/


static int surfID = 0;

void SurfaceConnectFace(SURFACE *surf, PATCH *face)
{
	face->surface = surf;
}

SURFACE *SurfaceCreate(MATERIAL *material, 
		       POINTLIST *points, VECTORLIST *normals,
		       VERTEXLIST *vertices, PATCHLIST *faces)
{
	SURFACE *surf;

	surf = NEWSURFACE();
	surf->id = surfID++;
	surf->material = material;
	surf->points = points;
	surf->normals = normals;
	surf->vertices = vertices;
	surf->faces = faces;

	PatchListIterate1B(surf->faces, SurfaceConnectFace, surf);

	return surf;
}

void SurfaceDestroy(SURFACE *surf)
{
	PointListIterate(surf->points, PointDestroy);
	PointListDestroy(surf->points);

	VectorListIterate(surf->normals, VectorDestroy);
	VectorListDestroy(surf->normals);

	VertexListIterate(surf->vertices, VertexDestroy);
	VertexListDestroy(surf->vertices);

	PatchListIterate(surf->faces, PatchDestroy);
	PatchListDestroy(surf->faces);

	DISPOSESURFACE(surf);
}

POINT *SurfaceInstallPoint(SURFACE *surf, POINT *point)
{
	POINT *p;

	if ((p = PointListFind(surf->points, point)) == (POINT *)NULL) {
		p = PointCopy(point);
		surf->points = PointListAdd(surf->points, p);
	}

	return p;
}

VECTOR *SurfaceInstallNormal(SURFACE *surf, VECTOR *normal)
{
	VECTOR *n;

	if ((n = VectorListFind(surf->normals, normal)) == (VECTOR *)NULL) {
		n = VectorCopy(normal);
		surf->normals = VectorListAdd(surf->normals, n);
	}

	return n;
}

VERTEX *SurfaceInstallVertex(SURFACE *surf, POINT *point, VECTOR *normal, RGB *color)
{
	POINT *p;
	VECTOR *n;
	VERTEX *v;

	if ((v = VertexListFind(surf->vertices, point, normal)) == (VERTEX *)NULL) {
		p = SurfaceInstallPoint(surf, point);
		n = SurfaceInstallNormal(surf, normal);


		v = VertexCreate(p, n, PatchListCreate(), color);
		surf->vertices = VertexListAdd(surf->vertices, v);
	}

	return v;
}

void SurfacePrint(FILE *out, SURFACE *surface)
{
	fprintf(out, "Surface id %d: material %s, patches ID: ",
		     surface->id, surface->material->name);
	PatchListIterate1B(surface->faces, PatchPrintID, out);
	fprintf(out, "\n");

	PatchListIterate1B(surface->faces, PatchPrint, out);
}

float *SurfaceBounds(SURFACE *surf, float *boundingbox)
{
	return PatchListBounds(surf->faces, boundingbox);
}

PATCH	*SurfaceIntersect(SURFACE *surf, RAY *ray, float mindist, float *maxdist, int ShadowTesting)
{
	return PatchListIntersect(surf->faces, ray, mindist, maxdist, ShadowTesting);
}

PATCHLIST *SurfacePatchlist(SURFACE *surf)
{
	return surf->faces;
}

GEOM_METHODS surfaceMethods = {
	(float *(*)(void *, float *))SurfaceBounds,
	(void (*)(void *))SurfaceDestroy,
	(void (*)(FILE *, void *))SurfacePrint,
	(PATCH *(*)(void *, RAY *, float, float *, int))SurfaceIntersect,
	(GEOMLIST *(*)(void *))NULL,
	(PATCHLIST *(*)(void *))SurfacePatchlist,
	(void *(*)(void *))NULL
};

GEOM_METHODS *SurfaceMethods(void)
{
	return &surfaceMethods;
}



