/* ************************
   Copyright Terrain Experts Inc.
   Terrain Experts Inc (TERREX) reserves all rights to this source code
   unless otherwise specified in writing by the President of TERREX.
   This copyright may be updated in the future, in which case that version
   supercedes this one.
   -------------------
   Terrex Experts Inc.
   4400 East Broadway #314
   Tucson, AZ  85711
   info@terrex.com
   Tel: (520) 323-7990
   ************************
   */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* trpage_scene.cpp
	This file implements a bunch of stuff, all of it optional.  See trpage_scene.h
	 for more information.
	Scene Graph nodes -
	 All the methods for the simple scene graph are here.
	trpgSceneGraphParser -
	 This is a subclass of trpgSceneParser.  It uses that utility class to keep track
	  of pushes and pops.  It also registers an interest in all the node types it
	  knows about (Geometry,Group,LOD,ModelRef).  When one of those is encountered
	  by the trpgr_Parser (which it's also a subclass of) it reads it into the
	  appropriate trpgRead* type.
	 Unless you're reading into the scene graph defined in trpage_scene.h, you won't
	  use this class directly.  Instead, copy it and use it as a template for how
	  to read into a scene graph.  You'll need to replace the helpers, primarily.
	*/

#include <trpage_read.h>
#include <trpage_scene.h>

/*  ****************
	MBR Calculation and handling
	****************
	*/
trpgMBR::trpgMBR()
{
	valid = false;
}
bool trpgMBR::isValid() const
{
	return valid;
}
void trpgMBR::Reset()
{
	valid = false;
}
trpg3dPoint trpgMBR::GetLL() const
{
	return ll;
}
trpg3dPoint trpgMBR::GetUR() const
{
	return ur;
}
void trpgMBR::AddPoint(const trpg3dPoint &pt)
{
	if (valid) {
		ll.x = MIN(pt.x,ll.x);
		ll.y = MIN(pt.y,ll.y);
		ll.z = MIN(pt.z,ll.z);
		ur.x = MAX(pt.x,ur.x);
		ur.y = MAX(pt.y,ur.y);
		ur.z = MAX(pt.z,ur.z);
	} else {
		valid = true;
		ll = ur = pt;
	}
}
void trpgMBR::AddPoint(double x,double y,double z)
{
	AddPoint(trpg3dPoint(x,y,z));
}
void trpgMBR::GetMBR(trpg3dPoint &oll,trpg3dPoint &our) const
{
	oll = ll;
	our = ur;
}
// Add the input MBR to this one
void trpgMBR::Union(const trpgMBR &in)
{
	if (valid) {
		if (in.isValid()) {
			AddPoint(in.GetLL());
			AddPoint(in.GetUR());
		}
	} else {
		valid = true;
		*this = in;
	}
}
// See if there's any overlap between the two MBRs
bool trpgMBR::Overlap(const trpg2dPoint &ill, const trpg2dPoint &iur) const
{
	if (!isValid()) return false;

	trpg2dPoint ilr = trpg2dPoint(iur.x,ill.y);
	trpg2dPoint iul = trpg2dPoint(ill.x,iur.y);

	// B MBR falls within A
	if (Within(ill) || Within(iur) || Within(ilr) || Within(iul))
		return true;

	// A MBR falls within B
	if ((inRange(ill.x,iur.x,ll.x) && inRange(ill.y,iur.y,ll.y)) ||
		(inRange(ill.x,iur.x,ur.x) && inRange(ill.y,iur.y,ll.y)) ||
		(inRange(ill.x,iur.x,ur.x) && inRange(ill.y,iur.y,ur.y)) ||
		(inRange(ill.x,iur.x,ll.x) && inRange(ill.y,iur.y,ur.y)))
		return true;

	if ((inRange(ll.x,ur.x,ill.x) && ill.y < ll.y && iur.y > ur.y) ||
		(inRange(ll.y,ur.y,ill.y) && ill.x < ll.x && iur.x > ur.x))
		return true;

	return false;
}
// Check if a given 2d point is within the MBR
bool trpgMBR::Within(const trpg2dPoint &pt) const
{
	if (inRange(ll.x,ur.x,pt.x) && inRange(ll.y,ur.y,pt.y))
		return true;
	return false;
}

/*	****************
	Read Group Base
	Base class for all group structures.
	****************
	*/

// Destructor
trpgReadGroupBase::~trpgReadGroupBase()
{
	DeleteChildren();
}

// Delete all children
void trpgReadGroupBase::DeleteChildren()
{
	for (unsigned int i=0;i<children.size();i++)
		if (children[i])
			delete children[i];
}

// Add a child to the list
void trpgReadGroupBase::AddChild(trpgReadNode *n)
{
	children.push_back(n);
}

// Unref a child (but don't delete it)
void trpgReadGroupBase::unRefChild(int id)
{
	if (id < 0 || id >= (int)children.size())
		return;
	children[id] = NULL;
}

// Unref all the children (they've probably been moved elsewhere)
void trpgReadGroupBase::unRefChildren()
{
	for (unsigned int i=0;i<children.size();i++)
		unRefChild(i);
}

// Calculate an MBR
trpgMBR trpgReadGroupBase::GetMBR() const
{
	if (mbr.isValid())
		return mbr;
	else {
		// Calculate and cache a new MBR
		trpgMBR *cmbr = const_cast<trpgMBR *>(&mbr);
		trpgMBR kmbr;
		// Ask the kids
		for (unsigned int i=0;i<children.size();i++) {
			kmbr = children[i]->GetMBR();
			cmbr->Union(kmbr);
		}
		return *cmbr;
	}
}

/*  ****************
	Read Geometry
	****************
	*/
// Calculate an MBR
trpgMBR trpgReadGeometry::GetMBR() const
{
	if (mbr.isValid())
		return mbr;

	trpgMBR *pmbr = const_cast<trpgMBR *>(&mbr);

	int numVert,i;
	trpg3dPoint pt;
	data.GetNumVertex(numVert);
	numVert /= 3;
	for (i=0;i<numVert;i++) {
		data.GetVertex(i,pt);
		pmbr->AddPoint(pt);
	}

	return mbr;
}

/*	****************
	Scene Graph Parser
	****************
	*/

/* Scene Graph Parser Helpers
	Each of these classes reads a certain kind of data (e.g. a group)
	 and creates the appropriate trpgrRead* form and returns that.
	 */

/* This is a helper registered by trpgSceneGraphParser that readers trpgGeometry
	nodes and adds them to the current scene graph.  trpgGeometry nodes are
	always leaves so there should be no pushes after this node.  The Parse method
	also adds the new node as a child to any existing (e.g. top) group.
	{group:Demonstration Scene Graph}
 */
class trpgReadGeometryHelper : public trpgr_Callback {
public:
	trpgReadGeometryHelper(trpgSceneGraphParser *in_parse) { parse = in_parse;}
	void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
		trpgReadGeometry *geom = new trpgReadGeometry();
		trpgGeometry *data = geom->GetData();
		if (!data->Read(buf)) {
			delete geom;
			return NULL;
		}
		trpgReadGroupBase *top = parse->GetCurrTop();
		if (top)
			top->AddChild(geom);
		else
			delete geom;

		return geom;
	}
protected:
	trpgSceneGraphParser *parse;
};

/* This helper is registered by trpgSceneGraphParser.  It reads a trpgGroup
	from the trpgReadBuffer.  It then adds it to our current scene graph.
	It also adds an index corresponding to the group's group ID in our group
	mapping in trpgSceneGraphParser.  The new group becomes the top one
	after returning from the Parse call.
	{group:Demonstration Scene Graph}
 */
class trpgReadGroupHelper : public trpgr_Callback {
public:
	trpgReadGroupHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
	void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
		trpgReadGroup *group = new trpgReadGroup();
		trpgGroup *data = group->GetData();
		if (!data->Read(buf)) {
			delete group;
			return NULL;
		}
		trpgReadGroupBase *top = parse->GetCurrTop();
		if (top)
			top->AddChild(group);
		else
			delete group;
		// Add to the group map
		int id;
		data->GetID(id);
		trpgSceneGraphParser::GroupMap *gmap = parse->GetGroupMap();
		(*gmap)[id] = group;
		return group;
	}
protected:
	trpgSceneGraphParser *parse;
};
class trpgReadBillboardHelper : public trpgr_Callback {
public:
	trpgReadBillboardHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
	void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
		trpgReadBillboard *group = new trpgReadBillboard();
		trpgBillboard *data = group->GetData();
		if (!data->Read(buf)) {
			delete group;
			return NULL;
		}
		trpgReadGroupBase *top = parse->GetCurrTop();
		if (top)
			top->AddChild(group);
		else
			delete group;
		// Add to the group map
		int id;
		data->GetID(id);
		trpgSceneGraphParser::GroupMap *gmap = parse->GetGroupMap();
		(*gmap)[id] = group;
		return group;
	}
protected:
	trpgSceneGraphParser *parse;
};
class trpgReadAttachHelper : public trpgr_Callback {
public:
	trpgReadAttachHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
	void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
		trpgReadAttach *attach = new trpgReadAttach();
		trpgAttach *data = attach->GetData();
		if (!data->Read(buf)) {
			delete attach;
			return NULL;
		}
		trpgReadGroupBase *top = parse->GetCurrTop();
		if (top)
			top->AddChild(attach);
		else
			delete attach;
		// Add to the group map
		int id;
		data->GetID(id);
		trpgSceneGraphParser::GroupMap *gmap = parse->GetGroupMap();
		(*gmap)[id] = attach;
		return attach;
	}
protected:
	trpgSceneGraphParser *parse;
};
class trpgReadLodHelper : public trpgr_Callback {
public:
	trpgReadLodHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
	void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
		trpgReadLod *lod = new trpgReadLod();
		trpgLod *data = lod->GetData();
		if (!data->Read(buf)) {
			delete lod;
			return NULL;
		}
		trpgReadGroupBase *top = parse->GetCurrTop();
		if (top)
			top->AddChild(lod);
		else
			delete lod;
		// Add to the group map
		int id;
		data->GetID(id);
		trpgSceneGraphParser::GroupMap *gmap = parse->GetGroupMap();
		(*gmap)[id] = lod;
		return lod;
	}
protected:
	trpgSceneGraphParser *parse;
};
class trpgReadModelRefHelper : public trpgr_Callback {
public:
	trpgReadModelRefHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
	void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
		trpgReadModelRef *mod = new trpgReadModelRef();
		trpgModelRef *data = mod->GetData();
		if (!data->Read(buf)) {
			delete mod;
			return NULL;
		}
		trpgReadGroupBase *top = parse->GetCurrTop();
		if (top)
			top->AddChild(mod);
		else
			delete mod;
		return mod;
	}
protected:
	trpgSceneGraphParser *parse;
};
class trpgReadTileHeaderHelper : public trpgr_Callback {
public:
	trpgReadTileHeaderHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
	void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
		trpgReadTileHeader *th = parse->GetTileHeaderRef();
		trpgTileHeader *data = th->GetData();
		if (!data->Read(buf))
			return NULL;
		return th;
	}
protected:
	trpgSceneGraphParser *parse;
};

/* The Scene Graph Parser constructor does two things.  First, it sets
	up any internal variables like a normal constructor.  Then it registers
	an interest in all the node types it knows how to parse.  It does this
	by calling AddCallback, which is a method of its parent.  It passes in
	a token representing the node type (see trpg_io.h) and an object that
	is capable of parsing the given type.

	The objects we pass in here are called helpers.  They parse specific
	objects and add them to the user defined scene graph.  Examples include
	trpgReadGeometryHelper, trpgReadGroupHelper, trpgReadAttachHelper,
	trpgReadBillboardHelper, trpgReadLodHelper, trpgReadModelRefHelper,
	trpgReadTileHeaderHelper.  These are all derived from trpgr_Callback.
	You should not use any of these yourself. Instead look at these classes
	as examples of how to implement your own subclass of trpgSceneParser.
	*/
trpgSceneGraphParser::trpgSceneGraphParser()
{
	top = currTop = NULL;

	// Register the readers
	AddCallback(TRPG_GEOMETRY,new trpgReadGeometryHelper(this));
	AddCallback(TRPG_GROUP,new trpgReadGroupHelper(this));
	AddCallback(TRPG_ATTACH,new trpgReadAttachHelper(this));
	AddCallback(TRPG_BILLBOARD,new trpgReadBillboardHelper(this));
	AddCallback(TRPG_LOD,new trpgReadLodHelper(this));
//	AddCallback(TRPG_TRANSFORM,new trpgReadTransformHelper(this));
	AddCallback(TRPG_MODELREF,new trpgReadModelRefHelper(this));
//	AddCallback(TRPG_LAYER,new trpgReadLayerHelper(this));
	AddCallback(TRPGTILEHEADER,new trpgReadTileHeaderHelper(this));
}

// Get Current Top node
trpgReadGroupBase *trpgSceneGraphParser::GetCurrTop()
{
	if (!currTop)
		return NULL;
	if (currTop->isGroupType())
		return (trpgReadGroupBase *)currTop;

	return NULL;
}

// Return a pointer to the tile header record
trpgReadTileHeader *trpgSceneGraphParser::GetTileHeaderRef()
{
	return &tileHead;
}

// Parse Scene
// Parse a buffer and return the resulting scene graph
trpgReadNode *trpgSceneGraphParser::ParseScene(trpgReadBuffer &buf,GroupMap &inGmap)
{
	gmap = &inGmap;
	trpgTileHeader *data = tileHead.GetData();
	data->Reset();

	// Always put a group up top, since there might be more than
	//  one node at the top level in the file.
	top = currTop = new trpgReadGroup();

	// All the setup for tokens is handled in the constructor
	// Just call parse
	if (!Parse(buf)) {
		// Failed to parse correctly.  Give up.
		delete top;
		return NULL;
	}

	return top;
}

// Start Children
// This is called when the parser hits a push.
// We'll want to make the node it's handing us the "top" node
bool trpgSceneGraphParser::StartChildren(void *in_node)
{
	trpgReadNode *node = (trpgReadNode *)in_node;

	if (!node || !node->isGroupType()) {
		// Looks like there's a push in the wrong place
		// Make the current "top" NULL.
		// This will drop all node until we pop back above
		currTop = NULL;
	} else {
		// This node is our new "top"
		currTop = node;
	}

	return true;
}

/* This is called whent he parser hits a pop.
   We'll want to look on the parent list (in trpgSceneParser)
    for the parent above the current one.
   If there isn't one, we'll just stick things in our top group.
   */
bool trpgSceneGraphParser::EndChildren(void * /*in_node*/)
{
	// We don't need it here, but this is the node we just
	//  finished putting children under.  If you need to close
	//  it out in some way, do that here
	//trpgReadNode *node = (trpgReadNode *)in_node;

	// Get the parent above the current one
	int pos = parents.size()-2;
	if (pos < 0)
		// Nothing above the current one.  Fall back on our top group
		currTop = top;
	else
		currTop = (trpgReadNode *)parents[pos];

	return true;
}

// Return group map (for use by helpers)
trpgSceneGraphParser::GroupMap *trpgSceneGraphParser::GetGroupMap()
{
	return gmap;
}

/*  ***********
	Test functions
	***********
	*/

// Test all the tiles in an archive
bool trpgTestArchive(trpgr_Archive &archive)
{
	int numLod;
	trpg2iPoint tileSize;
	trpgSceneGraphParser parse;
	trpgReadNode *scene;
	trpgSceneGraphParser::GroupMap gmap;

	if (!archive.isValid()) return false;

	const trpgHeader *head = archive.GetHeader();
	head->GetNumLods(numLod);

	// Iterate over the lods
	int nl,x,y;
	trpgMemReadBuffer buf(archive.GetEndian());
	trpg3dPoint ll,ur;
	for (nl = 0;nl < numLod;nl++) {
		head->GetLodSize(nl,tileSize);
		// Iterate over the tiles within those
		for (x = 0; x < tileSize.x; x++)
			for (y = 0; y < tileSize.y; y++) {
				archive.trpgGetTileMBR(x,y,nl,ll,ur);
				if (archive.ReadTile(x,y,nl,buf)) {
					// Parse it
					scene = parse.ParseScene(buf,gmap);
					if (scene)
						delete scene;
				}
			}
	}

	return true;
}
