/*
	Twilight Prophecy 3D/Multimedia SDK
	A multi-platform development system for virtual reality and multimedia.

	Copyright (C) 1997-2001 by Twilight 3D Finland Oy Ltd.

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	Please read the file LICENSE.TXT for additional details.


	source:
		ID Software Quake III Arena: bsp reader (.bsp)

	revision history:
		Aug/10/2000 - Rune Vendler - initial revision
		Feb/10/2001 - Jukka Liimatta - renaissance build
		Mar/19/2001 - Arnaud Linz - fixed a bug in lightmap size
*/
#include <primport/primport.hpp>

using namespace prcore;
using namespace primport;



//////////////////////////////////////////////////////
// reader declaration                              //
////////////////////////////////////////////////////

class ReaderQ3BSP
{
	public:

	ReaderQ3BSP(ImportQ3BSP& import, Stream& stream);
	~ReaderQ3BSP();

	private:

	struct	DirectoryEntry
	{
		int		offset;
		int		length;
	};
		
	struct	Header
	{
		#define	Q3BSP_LUMP_ENTITIES			(0)
		#define	Q3BSP_LUMP_TEXTURES			(1)
		#define	Q3BSP_LUMP_PLANES			(2)
		#define	Q3BSP_LUMP_NODES			(3)
		#define	Q3BSP_LUMP_LEAFS			(4)
		#define	Q3BSP_LUMP_LEAFFACES		(5)
		#define	Q3BSP_LUMP_LEAFBRUSHES		(6)
		#define	Q3BSP_LUMP_MODELS			(7)
		#define	Q3BSP_LUMP_BRUSHES			(8)
		#define	Q3BSP_LUMP_BRUSHSIDES		(9)
		#define	Q3BSP_LUMP_VERTICES			(10)
		#define	Q3BSP_LUMP_MESHVERTICES		(11)
		#define	Q3BSP_LUMP_EFFECTS			(12)
		#define	Q3BSP_LUMP_FACES			(13)
		#define	Q3BSP_LUMP_LIGHTMAPS		(14)
		#define	Q3BSP_LUMP_LIGHTVOLUMES		(15)
		#define	Q3BSP_LUMP_VISDATA			(16)
		#define	Q3BSP_NUM_LUMPS				(17)

		// we need the original struct-sizes to figure out the number of each type
		#define	Q3BSP_TEXTURE_SIZE			(72)
		#define Q3BSP_PLANE_SIZE			(16)
		#define Q3BSP_NODE_SIZE				(36)
		#define Q3BSP_LEAF_SIZE				(48)
		#define Q3BSP_LEAFFACE_SIZE			(4)
		#define Q3BSP_LEAFBRUSH_SIZE		(4)
		#define Q3BSP_MODEL_SIZE			(40)
		#define Q3BSP_BRUSH_SIZE			(12)
		#define Q3BSP_BRUSHSIDE_SIZE		(8)
		#define Q3BSP_VERTEX_SIZE			(44)
		#define Q3BSP_MESHVERTEX_SIZE		(4)
		#define Q3BSP_EFFECT_SIZE			(72)
		#define Q3BSP_FACE_SIZE				(104)
		#define Q3BSP_LIGHTMAP_SIZE			(49152)
		#define Q3BSP_LIGHTVOLUME_SIZE		(8)
			
		unsigned int	magicnumber;
		int				version;
		DirectoryEntry	directory[Q3BSP_NUM_LUMPS];
	};
		
	Header			header;
	ImportQ3BSP&	import;
	Stream&			stream;

	vec2f			ReadVector2();
	vec3f			ReadVector3();
	vec3f			Remap(const vec3f& v);
	Color32			ReadColor3();
	Color32			ReadColor4();
		
	void			ReadHeader(void);
	void			ReadEntities(void);
	void			ReadTextures(void);
	void			ReadPlanes(void);
	void			ReadNodes(void);
	void			ReadLeafs(void);
	void			ReadLeafFaces(void);
	void			ReadLeafBrushes(void);
	void			ReadModels(void);
	void			ReadBrushes(void);
	void			ReadBrushSides(void);
	void			ReadVertices(void);
	void			ReadMeshVertices(void);
	void			ReadEffects(void);
	void			ReadFaces(void);
	void			ReadLightmaps(void);
	void			ReadLightVolumes(void);
	void			ReadVisData(void);
};


//////////////////////////////////////////////////////
// reader implementation                           //
////////////////////////////////////////////////////

ReaderQ3BSP::ReaderQ3BSP(ImportQ3BSP& i, Stream& s)
: import(i), stream(s)
{
	// reset stream
	stream.Seek(0,Stream::START);

	// read header
	ReadHeader();
	
	// read lumps
	if(header.directory[Q3BSP_LUMP_ENTITIES].length>0) ReadEntities();
	if(header.directory[Q3BSP_LUMP_TEXTURES].length>0) ReadTextures();
	if(header.directory[Q3BSP_LUMP_PLANES].length>0) ReadPlanes();
	if(header.directory[Q3BSP_LUMP_NODES].length>0) ReadNodes();
	if(header.directory[Q3BSP_LUMP_LEAFS].length>0) ReadLeafs();
	if(header.directory[Q3BSP_LUMP_LEAFFACES].length>0) ReadLeafFaces();
	if(header.directory[Q3BSP_LUMP_LEAFBRUSHES].length>0) ReadLeafBrushes();
	if(header.directory[Q3BSP_LUMP_MODELS].length>0) ReadModels();
	if(header.directory[Q3BSP_LUMP_BRUSHES].length>0) ReadBrushes();
	if(header.directory[Q3BSP_LUMP_BRUSHSIDES].length>0) ReadBrushSides();
	if(header.directory[Q3BSP_LUMP_VERTICES].length>0) ReadVertices();
	if(header.directory[Q3BSP_LUMP_MESHVERTICES].length>0) ReadMeshVertices();
	if(header.directory[Q3BSP_LUMP_EFFECTS].length>0) ReadEffects();
	if(header.directory[Q3BSP_LUMP_FACES].length>0) ReadFaces();
	if(header.directory[Q3BSP_LUMP_LIGHTMAPS].length>0) ReadLightmaps();
	if(header.directory[Q3BSP_LUMP_LIGHTVOLUMES].length>0) ReadLightVolumes();
	if(header.directory[Q3BSP_LUMP_VISDATA].length>0) ReadVisData();
}


ReaderQ3BSP::~ReaderQ3BSP()
{
}


vec2f ReaderQ3BSP::ReadVector2()
{
	vec2f v;
	v.x = ReadLittleEndian<float>(stream);
	v.y = ReadLittleEndian<float>(stream);
	return v;
}


vec3f ReaderQ3BSP::ReadVector3()
{
	vec3f v;
	v.x = ReadLittleEndian<float>(stream);
	v.y = ReadLittleEndian<float>(stream);
	v.z = ReadLittleEndian<float>(stream);
	return v;
}


vec3f ReaderQ3BSP::Remap(const vec3f& v)
{
	return vec3f(-v.y, v.z, v.x);
}


Color32 ReaderQ3BSP::ReadColor3()
{
	Color32 c;
	c.r = ReadLittleEndian<uint8>(stream);
	c.g = ReadLittleEndian<uint8>(stream);
	c.b = ReadLittleEndian<uint8>(stream);
	c.a = 0xff;
	return c;
}


Color32	ReaderQ3BSP::ReadColor4()
{
	Color32 c;
	c.r = ReadLittleEndian<uint8>(stream);
	c.g = ReadLittleEndian<uint8>(stream);
	c.b = ReadLittleEndian<uint8>(stream);
	c.a = ReadLittleEndian<uint8>(stream);
	return c;
}


void	ReaderQ3BSP::ReadHeader(void)
{
	stream.Seek(0,Stream::START);
	
	// id
	header.magicnumber = ReadLittleEndian<uint32>(stream);
	if( header.magicnumber != PRCORE_CODE32('I','B','S','P') )
		PRCORE_EXCEPTION( "ImportQ3BSP()", "Incorrect header id.!!" );
		
	// version
	header.version = ReadLittleEndian<uint32>(stream);
	
	// directory
	for(int t=0;t<Q3BSP_NUM_LUMPS;t++)
	{
		header.directory[t].offset = ReadLittleEndian<uint32>(stream);
		header.directory[t].length = ReadLittleEndian<uint32>(stream);
	}
}
	
	
void	ReaderQ3BSP::ReadEntities(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_ENTITIES].offset, Stream::START);
	
	import.entities.descriptionlength=header.directory[Q3BSP_LUMP_ENTITIES].length;
	import.entities.entitydescriptions=new char[import.entities.descriptionlength];
	stream.Read(import.entities.entitydescriptions,import.entities.descriptionlength);
}


void	ReaderQ3BSP::ReadTextures(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_TEXTURES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_TEXTURES].length/Q3BSP_TEXTURE_SIZE;
	
	import.textures.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		char name[64];
		stream.Read(name,64);
		import.textures[t].name = name;
		import.textures[t].flags = ReadLittleEndian<uint32>(stream);
		import.textures[t].contents = ReadLittleEndian<uint32>(stream);
	}
}


void	ReaderQ3BSP::ReadPlanes(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_PLANES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_PLANES].length/Q3BSP_PLANE_SIZE;
	
	import.planes.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.planes[t].normal = Remap(ReadVector3());
		import.planes[t].dist = ReadLittleEndian<float>(stream);
	}
}


void	ReaderQ3BSP::ReadNodes(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_NODES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_NODES].length/Q3BSP_NODE_SIZE;
	
	import.nodes.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		vec3f v;
		import.nodes[t].planeindex = ReadLittleEndian<uint32>(stream);
		import.nodes[t].children[0] = ReadLittleEndian<uint32>(stream);
		import.nodes[t].children[1] = ReadLittleEndian<uint32>(stream);
		v.x = (float)ReadLittleEndian<uint32>(stream);
		v.y = (float)ReadLittleEndian<uint32>(stream);
		v.z = (float)ReadLittleEndian<uint32>(stream);
		import.nodes[t].boundingbox.min = Remap(v);
		v.x = (float)ReadLittleEndian<uint32>(stream);
		v.y = (float)ReadLittleEndian<uint32>(stream);
		v.z = (float)ReadLittleEndian<uint32>(stream);
		import.nodes[t].boundingbox.max = Remap(v);

		// switch x components because of remapping
		float x = import.nodes[t].boundingbox.min.x;
		import.nodes[t].boundingbox.min.x = import.nodes[t].boundingbox.max.x;
		import.nodes[t].boundingbox.max.x = x;
	}
}


void	ReaderQ3BSP::ReadLeafs(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_LEAFS].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_LEAFS].length/Q3BSP_LEAF_SIZE;
	
	import.leafs.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		vec3f v;
		import.leafs[t].cluster = ReadLittleEndian<int32>(stream);
		import.leafs[t].area = ReadLittleEndian<int32>(stream);
		v.x = (float)ReadLittleEndian<int32>(stream);
		v.y = (float)ReadLittleEndian<int32>(stream);
		v.z = (float)ReadLittleEndian<int32>(stream);
		import.leafs[t].boundingbox.min = Remap(v);
		v.x = (float)ReadLittleEndian<int32>(stream);
		v.y = (float)ReadLittleEndian<int32>(stream);
		v.z = (float)ReadLittleEndian<int32>(stream);
		import.leafs[t].boundingbox.max = Remap(v);
		import.leafs[t].leaffaceindex = ReadLittleEndian<int32>(stream);
		import.leafs[t].numleaffaces = ReadLittleEndian<int32>(stream);
		import.leafs[t].leafbrushindex = ReadLittleEndian<int32>(stream);
		import.leafs[t].numleafbrushes = ReadLittleEndian<int32>(stream);

		// switch x components because of remapping
		float x=import.leafs[t].boundingbox.min.x;
		import.leafs[t].boundingbox.min.x=import.leafs[t].boundingbox.max.x;
		import.leafs[t].boundingbox.max.x=x;
	}
}


void	ReaderQ3BSP::ReadLeafFaces(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_LEAFFACES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_LEAFFACES].length/Q3BSP_LEAFFACE_SIZE;
	
	import.leaffaces.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.leaffaces[t].faceindex = ReadLittleEndian<int32>(stream);
	}
}


void	ReaderQ3BSP::ReadLeafBrushes(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_LEAFBRUSHES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_LEAFBRUSHES].length/Q3BSP_LEAFBRUSH_SIZE;
	
	import.leafbrushes.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.leafbrushes[t].brushindex = ReadLittleEndian<int32>(stream);
	}
}


void	ReaderQ3BSP::ReadModels(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_MODELS].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_MODELS].length/Q3BSP_MODEL_SIZE;
	
	import.models.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.models[t].boundingbox.min = Remap(ReadVector3());
		import.models[t].boundingbox.max = Remap(ReadVector3());
		import.models[t].faceindex = ReadLittleEndian<int32>(stream);
		import.models[t].numfaces = ReadLittleEndian<int32>(stream);
		import.models[t].brushindex = ReadLittleEndian<int32>(stream);
		import.models[t].numbrushes = ReadLittleEndian<int32>(stream);

		// switch x components because of remapping
		float	x=import.models[t].boundingbox.min.x;
		import.models[t].boundingbox.min.x=import.models[t].boundingbox.max.x;
		import.models[t].boundingbox.max.x=x;
	}
}


void	ReaderQ3BSP::ReadBrushes(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_BRUSHES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_BRUSHES].length/Q3BSP_BRUSH_SIZE;
	
	import.brushes.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.brushes[t].brushsideindex = ReadLittleEndian<int32>(stream);
		import.brushes[t].numbrushsides = ReadLittleEndian<int32>(stream);
		import.brushes[t].textureindex = ReadLittleEndian<int32>(stream);
	}
}


void	ReaderQ3BSP::ReadBrushSides(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_BRUSHSIDES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_BRUSHSIDES].length/Q3BSP_BRUSHSIDE_SIZE;
	
	import.brushsides.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.brushsides[t].planeindex = ReadLittleEndian<int32>(stream);
		import.brushsides[t].textureindex = ReadLittleEndian<int32>(stream);
	}
}


void	ReaderQ3BSP::ReadVertices(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_VERTICES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_VERTICES].length/Q3BSP_VERTEX_SIZE;
	
	import.vertices.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.vertices[t].position = Remap(ReadVector3());
		import.vertices[t].texcoords = ReadVector2();
		import.vertices[t].lightmapcoords = ReadVector2();
		import.vertices[t].normal = Remap(ReadVector3());
		import.vertices[t].color = ReadColor4();
	}
}


void	ReaderQ3BSP::ReadMeshVertices(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_MESHVERTICES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_MESHVERTICES].length/Q3BSP_MESHVERTEX_SIZE;
	
	import.meshvertices.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.meshvertices[t].offset = ReadLittleEndian<int32>(stream);
	}
}


void	ReaderQ3BSP::ReadEffects(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_EFFECTS].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_EFFECTS].length/Q3BSP_EFFECT_SIZE;
	
	import.effects.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		char name[64];
		stream.Read(name,64);
		import.effects[t].name=name;
		import.effects[t].brushindex = ReadLittleEndian<int32>(stream);
		import.effects[t].unknown = ReadLittleEndian<int32>(stream);
	}
}


void	ReaderQ3BSP::ReadFaces(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_FACES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_FACES].length/Q3BSP_FACE_SIZE;
	
	import.faces.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.faces[t].textureindex = ReadLittleEndian<int32>(stream);
		import.faces[t].effectindex = ReadLittleEndian<int32>(stream);
		import.faces[t].type = ReadLittleEndian<int32>(stream);
		import.faces[t].vertexindex = ReadLittleEndian<int32>(stream);
		import.faces[t].numvertices = ReadLittleEndian<int32>(stream);
		import.faces[t].meshvertexindex = ReadLittleEndian<int32>(stream);
		import.faces[t].nummeshvertices = ReadLittleEndian<int32>(stream);
		import.faces[t].lightmapindex = ReadLittleEndian<int32>(stream);
		import.faces[t].lightmapcorner[0] = ReadLittleEndian<int32>(stream);
		import.faces[t].lightmapcorner[1] = ReadLittleEndian<int32>(stream);
		import.faces[t].lightmapsize[0] = ReadLittleEndian<int32>(stream);
		import.faces[t].lightmapsize[1] = ReadLittleEndian<int32>(stream);
		import.faces[t].lightmapworldspaceorigin = Remap(ReadVector3());
		import.faces[t].lightmapworldspaces = Remap(ReadVector3());
		import.faces[t].lightmapworldspacet = Remap(ReadVector3());
		import.faces[t].normal = Remap(ReadVector3());
		import.faces[t].patchsize[0] = ReadLittleEndian<int32>(stream);
		import.faces[t].patchsize[1] = ReadLittleEndian<int32>(stream);
	}
}


void	ReaderQ3BSP::ReadLightmaps(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_LIGHTMAPS].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_LIGHTMAPS].length/Q3BSP_LIGHTMAP_SIZE;
	
	import.lightmaps.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		stream.Read((char*)import.lightmaps[t].map,128*128*3);
	}
}


void	ReaderQ3BSP::ReadLightVolumes(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_LIGHTVOLUMES].offset, Stream::START);
	
	int	num=header.directory[Q3BSP_LUMP_LIGHTVOLUMES].length/Q3BSP_LIGHTVOLUME_SIZE;
	
	import.lightvolumes.SetSize(num);
	
	for(int t=0;t<num;t++)
	{
		import.lightvolumes[t].ambientcolor = ReadColor3();
		import.lightvolumes[t].directionalcolor = ReadColor3();
		import.lightvolumes[t].phi = ReadLittleEndian<uint8>(stream);
		import.lightvolumes[t].theta = ReadLittleEndian<uint8>(stream);
	}
}


void	ReaderQ3BSP::ReadVisData(void)
{
	stream.Seek(header.directory[Q3BSP_LUMP_VISDATA].offset, Stream::START);
	
	import.visdata.numvectors = ReadLittleEndian<int32>(stream);
	import.visdata.vectorsize = ReadLittleEndian<int32>(stream);
	import.visdata.data=new unsigned char[import.visdata.numvectors*import.visdata.vectorsize];
	stream.Read((char*)import.visdata.data,import.visdata.numvectors*import.visdata.vectorsize);
}


//////////////////////////////////////////////////////
// ImportQ3BSP                                     //
////////////////////////////////////////////////////

ImportQ3BSP::ImportQ3BSP()
{
}


ImportQ3BSP::ImportQ3BSP(Stream& stream)
{
	ReaderQ3BSP reader(*this,stream);
}


ImportQ3BSP::ImportQ3BSP(const char* filename)
{
	FileStream stream(filename);
	ReaderQ3BSP reader(*this,stream);
}


ImportQ3BSP::~ImportQ3BSP()
{
}
