/*********************/
/*** Elem Stuph ***/
/*********************/

#include	"stdafx.h"
#include	<Vector3.h>
#include	<gl\gl.h>
#include	<gl\glu.h>
#include	"GLEnabledView.h"
#include	<Vector>
#include	<GFName.hpp>

#include	"Core.h"
#include	"TexCache.h"
#include	"Elem.h"
#include	"GinTex.h"
#include	"utils.h"

#include	"MapEdit.h"
#include	"MapEditDoc.h"
#include	"MapEditView.h"
#include	"MainFrm.h"

#include	"GUITileBank.h"


const Vector3	DefOfs(+0.5f,0,+4.0f);

/*****************************************************************************/
// All Elems MUST run from 0,0 ->

/*****************************************************************************/
const float	ElemBrowserGap=0.2f;
const float	ElemBrowserX0=0-ElemBrowserGap/2;
const float	ElemBrowserX1=1+ElemBrowserGap/2;
const float	ElemBrowserY0=0-ElemBrowserGap/2;
const float	ElemBrowserY1=1+ElemBrowserGap/2;

/*****************************************************************************/
const float	XFlipMtx[]=
{
-1, 0, 0, 0,
 0, 1, 0, 0,
 0, 0, 1, 0,
 0, 0, 0, 1,
};

const float	YFlipMtx[]=
{
 1, 0, 0, 0,
 0,-1, 0, 0,
 0, 0, 1, 0,
 0, 0, 0, 1,
};

/*****************************************************************************/
CElem::CElem(int Width,int Height)
{
		ElemWidth=Width;
		ElemHeight=Height;
		UnitWidth=ElemWidth/UnitSize;
		UnitHeight=ElemHeight/UnitSize;
		ElemID=-1;
		ElemRGB=(u8*)malloc(ElemWidth*ElemHeight*3);
		memset(ElemRGB,0,ElemWidth*ElemHeight*3);

		Type=ElemType2d;
		TexXOfs=0;
		TexYOfs=0;
		CreateBlankTileGfx();
}

/*****************************************************************************/
// 3d Elem (From Gin File)
CElem::CElem(CCore *Core,const char *Filename,CScene &ThisScene,int Node)
{
CNode		&ThisNode=ThisScene.GetNode(Node);
CTexCache	&TexCache=Core->GetTexCache();
GFName		Path=Filename;

			SetPath=Path.Drive();
			SetPath+=Path.Dir();
			SetPath.Append('\\');

			Type=ElemType3d;
			TexXOfs=-1;
			TexYOfs=-1;
			Ofs.Zero();
			Build3dElem(TexCache,ThisScene,Node);
			Calc3dSize();
			Build3dDrawList(TexCache,DrawList[ElemType3d]);
			Ofs.Zero();
			Create2dTexture(TexCache,Path.File(),Node);
			Build2dDrawList(TexCache,DrawList[ElemType2d]);
			if (!ValidFlag)	CreateInvalidTileGfx();

}

/*****************************************************************************/
// 2d Elem (From Bmp File)
CElem::CElem(CCore *Core,const char *Filename,int TexID,int XOfs,int YOfs,int Width,int Height,bool Centre)
{
CTexCache	&TexCache=Core->GetTexCache();
GFName		Path=Filename;

			ElemWidth=Width;
			ElemHeight=Height;
			UnitWidth=ElemWidth/UnitSize;
			UnitHeight=ElemHeight/UnitSize;

			Type=ElemType2d;
			TexXOfs=XOfs;
			TexYOfs=YOfs;
			Ofs.Zero();
			if (Centre)
			{
				Ofs.x=0.5-UnitWidth/2;
//				Ofs.y=0.5-UnitHeight/2;
				Ofs.y=-1.0f;
			}
			
			Build2dElem(Core,Path.File(),TexID);
			Build3dDrawList(TexCache,DrawList[ElemType3d]);
			Create2dTexture(TexCache,Path.File(),TexID);
			Build2dDrawList(TexCache,DrawList[ElemType2d]);
			if (!ValidFlag)	CreateInvalidTileGfx();
}

/*****************************************************************************/
void	CElem::CleanUp()
{
		free(ElemRGB);
}

/*****************************************************************************/
void	CElem::Build2dElem(CCore *Core,const char *Filename,int TexID)
{
float		X0=0;
float		X1=+UnitWidth;
float		Y0=0;
float		Y1=+UnitHeight;

Vector3		P0(X0,Y0,0);
Vector3		P1(X1,Y0,0);
Vector3		P2(X0,Y1,0);
Vector3		P3(X1,Y1,0);

int			ListSize=TriList.size();
			TriList.resize(ListSize+2);
sTriFace	&Tri0=TriList[ListSize+0];
sTriFace	&Tri1=TriList[ListSize+1];

CTexCache	&TexCache=Core->GetTexCache();
sTex		&ThisTex=TexCache.GetTex(TexID);

float		dU=1.0/((float)ThisTex.TexWidth/(float)ElemWidth);
float		dV=1.0/((float)ThisTex.TexHeight/(float)ElemHeight);

float		u0=(TexXOfs*dU);
float		u1=u0+dU;

float		v1=1.0-(TexYOfs*dV);
float		v0=v1-dV;

			Tri0.Mat=TexID;

			Tri0.vtx[0]=P0;
			Tri0.vtx[1]=P1;
			Tri0.vtx[2]=P2;
			Tri0.uvs[0].u=u0; Tri0.uvs[0].v=v0;
			Tri0.uvs[1].u=u1; Tri0.uvs[1].v=v0;
			Tri0.uvs[2].u=u0; Tri0.uvs[2].v=v1;

			Tri1.Mat=TexID;
			Tri1.vtx[0]=P1;
			Tri1.vtx[1]=P2;
			Tri1.vtx[2]=P3;
			Tri1.uvs[0].u=u1; Tri1.uvs[0].v=v0;
			Tri1.uvs[1].u=u0; Tri1.uvs[1].v=v1;
			Tri1.uvs[2].u=u1; Tri1.uvs[2].v=v1;
}

/*****************************************************************************/
void	CElem::Build3dElem(CTexCache &TexCache,CScene &ThisScene,int Node)
{
CNode						&ThisNode=ThisScene.GetNode(Node);
CNode						&ParentNode=ThisScene.GetNode(ThisNode.ParentIdx);
int							ChildCount=ThisNode.GetPruneChildCount();
std::vector<sGinTri> const	&NodeTriList=ThisNode.GetTris();
std::vector<Vector3>const	&NodeVtxList=ThisNode.GetPts();
std::vector<sUVTri>	const	&NodeUVList=ThisNode.GetUVTris();
std::vector<int>	const	&NodeTriMat=ThisNode.GetTriMaterial();

std::vector<GString> const	&SceneTexList=ThisScene.GetTexList();
std::vector<int> const		&SceneUsedMatList=ThisScene.GetUsedMaterialIdx();

int			TriCount=NodeTriList.size();
int			ListSize=TriList.size();

			TriList.resize(ListSize+TriCount);
			for (int T=0; T<TriCount; T++)
			{
				sGinTri	const	&ThisTri=NodeTriList[T];
				sUVTri	const	&ThisUV=NodeUVList[T];
				sTriFace		&Tri=TriList[ListSize+T];
				int				ThisMat=NodeTriMat[T];
				int				TexID;
				

// Sort Textures - Only add the ones nthat are used :o)
				TexID=SceneUsedMatList[ThisMat];

				if (TexID!=-1)
					{
					GString	ThisName;

					ThisName=SetPath+SceneTexList[TexID];
					TRACE2("%i !%s!\n",TexID,ThisName);
					TexID=TexCache.ProcessTexture(ThisName);
					}
// Sort Rest of Tri info
Matrix4x4		TransMtx;
				TransMtx.Identity();
				
				if (ParentNode.GetTris().size() || !ThisNode.ParentIdx)
				{
					TransMtx=ThisNode.Mtx;
					TransMtx.Invert();
				}

				for (int p=0; p<3; p++)
				{
					Tri.vtx[p]=TransMtx*NodeVtxList[ThisTri.p[p]];
					Tri.uvs[p].u=ThisUV.p[p].u;
					Tri.uvs[p].v=ThisUV.p[p].v;
					Tri.Mat=TexID;
				}
			}
		for (int Child=0; Child<ChildCount; Child++) Build3dElem(TexCache,ThisScene,ThisNode.PruneChildList[Child]);

}

/*****************************************************************************/
void	CElem::Calc3dSize()
{
int			i,ListSize=TriList.size();
Vector3		Min(+32000,+32000,+32000);
Vector3		Max(-32000,-32000,-32000);
Vector3		Mid;

		for (i=0; i<ListSize; i++)
		{
			sTriFace		&Tri=TriList[i];
			for (int p=0; p<3; p++)
			{
				if (Min.x>Tri.vtx[p].x) Min.x=Tri.vtx[p].x;
				if (Min.y>Tri.vtx[p].y) Min.y=Tri.vtx[p].y;
				if (Min.z>Tri.vtx[p].z) Min.z=Tri.vtx[p].z;
				if (Max.x<Tri.vtx[p].x) Max.x=Tri.vtx[p].x;
				if (Max.y<Tri.vtx[p].y) Max.y=Tri.vtx[p].y;
				if (Max.z<Tri.vtx[p].z) Max.z=Tri.vtx[p].z;
			}
		}

		UnitWidth=round(Max.x-Min.x);
		UnitHeight=round(Max.y-Min.y);
		if (UnitWidth<1) UnitWidth=1;
		if (UnitHeight<1) UnitHeight=1;
		ElemWidth=UnitWidth*UnitSize;
		ElemHeight=UnitHeight*UnitSize;
		Min.z=0;
		
		Ofs=DefOfs;
//		Ofs.x=+0.5f; Ofs.y=0; Ofs.z=+4.0f;
}

/*****************************************************************************/
void	CElem::Build2dDrawList(CTexCache &TexCache,GLint &List)
{
float	X0=Ofs.x;
float	X1=Ofs.x+UnitWidth;
float	Y0=Ofs.y;
float	Y1=Ofs.y+UnitHeight;
float	Z=Ofs.z;

			List=glGenLists(1);
			glNewList(List,GL_COMPILE);

			glBindTexture(GL_TEXTURE_2D, TexCache.GetTexGLId(ElemID));
			glBegin (GL_QUADS);
				glTexCoord2f(0.0f,0.0f);
				glVertex3f(X0,Y0,Z);

				glTexCoord2f(1.0f,0.0f);
				glVertex3f(X1,Y0,Z);

				glTexCoord2f(1.0f,1.0f);
				glVertex3f(X1,Y1,Z);

				glTexCoord2f(0.0f,1.0f);
				glVertex3f(X0,Y1,Z);
			glEnd();
			glEndList();

}

/*****************************************************************************/
// Build OpenGL display lists, sorted by material (luckily by gin!!)
void	CElem::Build3dDrawList(CTexCache &TexCache,GLint &List)
{
int			TriCount=TriList.size();
int			LastMat=-1,ThisMat;
float		ScaleU,ScaleV;

			List=glGenLists(1);
			glNewList(List,GL_COMPILE);

			for (int T=0; T<TriCount; T++)
			{
				sTriFace	&ThisTri=TriList[T];
				ThisMat=ThisTri.Mat;
				
				if (!T || ThisMat!=LastMat)	// First Tri or new material
				{
					if (T) glEnd();	// Not first tri, so end previous mat set
					glBindTexture(GL_TEXTURE_2D, TexCache.GetTexGLId(ThisMat));
					glBegin (GL_TRIANGLES);
					LastMat=ThisMat;
					sTex &Tex=TexCache.GetTex(ThisMat);
					int	AW=TexCache.AlignSize(Tex.TexWidth);
					int	AH=TexCache.AlignSize(Tex.TexHeight);
					if (AW!=Tex.TexWidth || AH!=Tex.TexHeight)
					{
						ScaleU=(float)Tex.TexWidth/(float)AW;
						ScaleV=(float)Tex.TexHeight/(float)AH;
					}
					else
					{
						ScaleU=ScaleV=1;
					}
				}
		
				for (int p=0; p<3; p++)
				{
					Vector3	&ThisVtx=ThisTri.vtx[p];
					float	u=ThisTri.uvs[p].u*ScaleU;
					float	v=ThisTri.uvs[p].v*ScaleV;
					glTexCoord2f(u,v);
					glVertex3f( (ThisVtx.x+Ofs.x), (ThisVtx.y+Ofs.y), -(ThisVtx.z+Ofs.z));		// Neg Z (cos openGL)
				}
			}

			glEnd();
			glEndList();

}

/*****************************************************************************/
void	CElem::Render(int Flags,bool Render3d)
{
		glPushMatrix();

		if (Flags & PC_TILE_FLAG_MIRROR_X)
		{
			glTranslatef(1,0,0);
			glMultMatrixf(XFlipMtx);
		}
		if (Flags & PC_TILE_FLAG_MIRROR_Y)
		{
			glTranslatef(0,1,0);
			glMultMatrixf(YFlipMtx);
		}

int		ColFlags=Flags >> PC_TILE_FLAG_COLLISION_SHIFT;

		if (ColFlags)
		{
			switch (ColFlags)
			{
			case PC_TILE_COLLISION_NORMAL:		glColor4f(1.0f,1.0f,1.0f,0.5); break;
			case PC_TILE_COLLISION_DAMAGE:		glColor4f(1.0f,0.0f,0.0f,0.5); break;
			case PC_TILE_COLLISION_SLIPPERY:	glColor4f(0.0f,1.0f,0.0f,0.5); break;
			case PC_TILE_COLLISION_ELECTRIC:	glColor4f(1.0f,0.0f,1.0f,0.5); break;
			case PC_TILE_COLLISION_STICKY:		glColor4f(1.0f,1.0f,0.0f,0.5); break;
			case PC_TILE_COLLISION_WATER:		glColor4f(0.0f,0.0f,1.0f,0.5); break;
			case PC_TILE_COLLISION_SOLID:		glColor4f(0.0f,1.0f,1.0f,0.5); break;
			case PC_TILE_COLLISION_DEATH:		glColor4f(1.0f,0.5f,0.5f,0.5); break;
			}
		}

		glEnable(GL_TEXTURE_2D);
		if (Render3d) 
		{
			glEnable(GL_DEPTH_TEST);
			glCallList(DrawList[ElemType3d]);
			glDisable(GL_DEPTH_TEST);
		}
		else
		{
			glCallList(DrawList[ElemType2d]);
		}
		glDisable(GL_TEXTURE_2D);
		glPopMatrix();
}

/*****************************************************************************/
void	CElem::RenderInvalid()
{
float	X0=0;
float	X1=UnitWidth;
float	Y0=0;
float	Y1=UnitHeight;

		glBindTexture(GL_TEXTURE_2D, 0);
		glBegin(GL_LINES); 
			glColor4f(1,1,1,1);
			
			glVertex3f( X0,Y0,0);
			glVertex3f( X1,Y1,0);

			glVertex3f( X1,Y0,0);
			glVertex3f( X0,Y1,0);
		glEnd();
}

/*****************************************************************************/
void	CElem::Purge()
{
		for (int i=0; i<ElemTypeMax; i++)
			glDeleteLists(DrawList[i],1);
		TriList.clear();
}

/*****************************************************************************/
// Only created for the tile browser, should NEVER appear in main view
void	CElem::CreateBlankTileGfx()
{
float	X0=0;
float	X1=UnitWidth;
float	Y0=0;
float	Y1=UnitHeight;

		Purge();
		for (int i=0; i<ElemTypeMax; i++)
		{
			DrawList[i]=glGenLists(1);
			glNewList(DrawList[i],GL_COMPILE);
			glBindTexture(GL_TEXTURE_2D, 0);
			glBegin(GL_LINES); 
				glColor4f(1,1,1,1);
		
				glVertex3f( X0,Y0,0);
				glVertex3f( X1,Y0,0);

				glVertex3f( X0,Y1,0);
				glVertex3f( X1,Y1,0);

				glVertex3f( X0,Y0,0);
				glVertex3f( X0,Y1,0);

				glVertex3f( X1,Y0,0);
				glVertex3f( X1,Y1,0);
			glEnd();
			glEndList();
	}
}

/*****************************************************************************/
void	CElem::CreateInvalidTileGfx()
{
float	X0=0;
float	X1=UnitWidth;
float	Y0=0;
float	Y1=UnitHeight;

		Purge();
		for (int i=0; i<ElemTypeMax; i++)
		{
			DrawList[i]=glGenLists(1);
			glNewList(DrawList[i],GL_COMPILE);
			glBindTexture(GL_TEXTURE_2D, 0);
			glBegin(GL_LINES); 
				glColor4f(1,1,1,1);
			
				glVertex3f( X0,Y0,0);
				glVertex3f( X1,Y1,0);

				glVertex3f( X1,Y0,0);
				glVertex3f( X0,Y1,0);
			glEnd();
			glEndList();
	}
}

/*****************************************************************************/
void	CElem::RenderElem4Texture(sRGBData &RGBData)
{
float	W=UnitWidth;
float	H=UnitHeight;

		glClearColor(1,0,1,1 );
//		glClearColor(1,1,1,1 );
		glPushAttrib(GL_VIEWPORT_BIT);
		glViewport(0,0,RGBData.Width,RGBData.Height);

		glMatrixMode(GL_PROJECTION);
 		glPushMatrix();

		glLoadIdentity();
		glOrtho(0, W, 0, H, -16.0f, +16.0f);

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
		glTranslatef(-Ofs.x,-Ofs.y,-Ofs.z);

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen

		glColor3f(1,1,1);
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_DEPTH_TEST);
		glCallList(DrawList[ElemType3d]);
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_TEXTURE_2D);

		glPopMatrix();
		glMatrixMode(GL_PROJECTION);
 		glPopMatrix();
		glPopAttrib();

		glReadPixels(0,0,RGBData.Width,RGBData.Height,GL_RGB, GL_UNSIGNED_BYTE,RGBData.RGB);
		glClearColor(0,0,0,1 );
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen
}

/*****************************************************************************/
// ID used to create unique textures within one set :o)
void	CElem::Create2dTexture(CTexCache &TexCache,const char *Filename,int ID)
{
sRGBData	RGBData;
char		TexName[256];

		sprintf(TexName,"_2dPc_%s_%03d",Filename,ID);
		ElemRGB=(u8*)malloc(ElemWidth*ElemHeight*3);

		RGBData.Width=ElemWidth;
		RGBData.Height=ElemHeight;
		RGBData.RGB=ElemRGB;
		RenderElem4Texture(RGBData);
		ElemID=TexCache.ProcessTexture(TexName,&RGBData);
		ValidFlag=CheckHasData(RGBData);

#ifdef _DEBUG
		if (0)
		{
char	Filename[256];
		sprintf(Filename,"/x/%s.Tga",TexName);
		SaveTGA(Filename,ElemWidth,ElemHeight,ElemRGB);
		}

#endif

}

/*****************************************************************************/
bool	CElem::CheckHasData(sRGBData &RGBData)
{
u8		*Src=RGBData.RGB;
int		Size=RGBData.Width*RGBData.Height;

		for (int i=0; i<Size; i++)
		{
			u8	R=*Src++;
			u8	G=*Src++;
			u8	B=*Src++;
			if (R!=255 || G!=0 || B!=255) return(TRUE);

		}
	return(false);
}

/*****************************************************************************/
/*****************************************************************************/
/*** Elem Set ****************************************************************/
/*****************************************************************************/
/*****************************************************************************/
CElemSet::CElemSet(const char *_Filename,int Idx,int Width,int Height,bool CreateBlank,bool Centre)
{
GFName	FName=_Filename;

		Filename=_Filename;
		Name=FName.File();
		
		MaxWidth=Width;
		MaxHeight=Height;
		CentreFlag=Centre;
		Loaded=FALSE;
		SetNumber=Idx;

		if (CreateBlank)
		{
			ElemList.push_back(CElem(MaxWidth,MaxHeight));	// Insert Blank		
		}

}

/*****************************************************************************/
CElemSet::~CElemSet()
{
}

/*****************************************************************************/
void	CElemSet::CleanUp()
{
int	ListSize=ElemList.size();

		for (int i=0; i<ListSize; i++)
		{
			ElemList[i].CleanUp();
		}
}

/*****************************************************************************/
void	CElemSet::Load(CCore *Core)
{
GFName	FName=Filename;
GString	Ext=FName.Ext();
		Ext.Upper();

		if (Ext=="GIN")
			Load3d(Core);
		else
			Load2d(Core);
		Loaded=TRUE;
}

/*****************************************************************************/
void	CElemSet::Load2d(CCore *Core)
{
CTexCache	&TexCache=Core->GetTexCache();
int			TexID=TexCache.ProcessTexture(Filename);
sTex		&ThisTex=TexCache.GetTex(TexID);
int			Width,Height;

		if (MaxWidth==-1) MaxWidth=ThisTex.TexWidth;
		if (MaxHeight==-1) MaxHeight=ThisTex.TexHeight;

		Width=ThisTex.TexWidth/MaxWidth;
		Height=ThisTex.TexHeight/MaxHeight;


		for (int Y=0; Y<Height; Y++)
		{
			for (int X=0; X<Width; X++)
			{
				ElemList.push_back(CElem(Core,Filename,TexID,X,Y,MaxWidth,MaxHeight,CentreFlag));
			}
		}
		ElemBrowserWidth=Width;
}

/*****************************************************************************/
void	CElemSet::Load3d(CCore *Core)
{
CScene	Scene;

		Scene.Load(Filename);

CNode	&ThisNode=Scene.GetSceneNode(0);
int		ChildCount=ThisNode.GetPruneChildCount();
		
		for (int Child=0; Child<ChildCount; Child++) 
		{
			ElemList.push_back(CElem(Core,Filename,Scene,ThisNode.PruneChildList[Child]));
		}
		ElemBrowserWidth=DEF_ELEMBROWSWEWIDTH;
}

/*****************************************************************************/
void	CElemSet::Purge()
{
int	ListSize=ElemList.size();
	
		for (int i=0; i<ListSize; i++)
		{
			ElemList[i].Purge();
		}
		ElemList.clear();
		Loaded=FALSE;
}

/*****************************************************************************/
CPoint	CElemSet::GetElemPos(int ID)
{
	if (ID==0)
		return(CPoint(-1,-1));
	else
		return(IDToPoint(ID-1,ElemBrowserWidth));
}

/*****************************************************************************/
bool	CElemSet::IsValid(int No)			
{
int		ListSize=ElemList.size();
		if (No>ListSize) return(false);
		return(ElemList[No].IsValid());
}

/*****************************************************************************/
/*****************************************************************************/
/*** Elem Bank ***************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
CElemBank::CElemBank(int W,int H,bool Blank,bool Centre)
{
		MaxWidth=W; 
		MaxHeight=H; 
		BlankFlag=Blank;
		CentreFlag=Centre;

		LoadFlag=false;
		CurrentSet=0;
		VisibleFlag=true;
}

/*****************************************************************************/
CElemBank::~CElemBank()
{
}

/*****************************************************************************/
void	CElemBank::CleanUp()
{
int	ListSize=SetList.size();

		for (int i=0; i<ListSize; i++)
		{
			SetList[i].CleanUp();
		}
}

/*****************************************************************************/
void	CElemBank::Load(CFile *File,int Version)
{
int		ListSize;
GFName	RootPath=File->GetFilePath();
GString	FilePath;
char	FixPath[1024];

		FilePath=RootPath.Drive();
		FilePath+=RootPath.Dir();
		FilePath.Append('\\');
		FilePath.Upper();
	
		if (Version>=5)
		{
			File->Read(&CurrentSet,sizeof(int));
		}
		File->Read(&ListSize,sizeof(int));

// New Style rel storage
		for (int i=0;i<ListSize;i++)
		{
			char	c=1;
			GString	FullName;//=FilePath;

			while (c)
			{
				File->Read(&c,1);
				FullName.Append(c);
			}
			FullName.Upper();
			GFName::makeabsolute(FilePath,FullName,FixPath);
			FullName=FixPath;
			_fullpath( FixPath, FullName, 1024);
			for (int z=0; z<strlen(FixPath); z++) 
			{// Invalidate any long name short cackness
				if (FixPath[z]=='~') FixPath[z]='_'; 
			}
			FullName=FixPath;

			CheckFilename(FullName);
			FullName.Upper();
			AddSet(FullName);
		}
}

/*****************************************************************************/
void	CElemBank::Save(CFile *File)
{
int		ListSize=SetList.size();
GFName	RootPath=File->GetFilePath();
GString	SavePath;

		SavePath=RootPath.Drive();
		SavePath+=RootPath.Dir();
		SavePath.Append('\\');

		SavePath.Upper();

		File->Write(&CurrentSet,sizeof(int));
		File->Write(&ListSize,sizeof(int));

		for (int i=0; i<ListSize; i++)
		{
			char		Filename[256+64];
			RootPath.makerelative(SavePath,SetList[i].GetFilename(),Filename);	
			File->Write(Filename,strlen(Filename)+1);		
		}

}

/*****************************************************************************/
void	CElemBank::AddSet(const char *Filename)
{
int			ListSize=SetList.size();
CElemSet	NewSet(Filename,ListSize,MaxWidth,MaxHeight,BlankFlag,CentreFlag);

			SetList.Add(NewSet);
			if (SetList.size()!=ListSize) LoadFlag=TRUE;
}

/*****************************************************************************/
void	CElemBank::LoadAllSets(CCore *Core)
{
int		ListSize=SetList.size();
	
		if (!LoadFlag) return;

		for (int i=0;i<ListSize;i++)
		{
			CElemSet	&ThisSet=SetList[i];

			if (!ThisSet.IsLoaded()) ThisSet.Load(Core);
		}
		LoadFlag=false;
}

/*****************************************************************************/
void	CElemBank::LoadNewSet(CCore *Core)
{
char		BASED_CODE GinFilter[]= "All Tile Files (*.Gin; *.Bmp)|*.gin;*.Bmp|3d Tile Files (*.Gin)|*.Gin|2d Tile Files (*.Bmp)|*.Bmp|All Files (*.*)|*.*||";
CFileDialog	Dlg(TRUE,"Gin",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,GinFilter);

		if (Dlg.DoModal()!=IDOK) return;

char	Filename[256];
		sprintf(Filename,"%s",Dlg.GetPathName());
		AddSet(Filename);
}

/*****************************************************************************/
void	CElemBank::ReloadAllSets()
{
int		ListSize=SetList.size();

		for (int i=0; i<ListSize; i++)
		{
			SetList[i].Purge();
		}

		LoadFlag=TRUE;
}

/*****************************************************************************/
void	CElemBank::DeleteSet(int Set)
{
		SetList.erase(Set);
}

/*****************************************************************************/
bool	CElemBank::IsValid(int Set,int Elem)
{
 		if (Set<0 || Elem<0) return(false);
		if (Elem==0) return(true);
 		if (Set>=SetList.size()) return(false);
		ASSERT(Set<SetList.size());
		return(SetList[Set].IsValid(Elem));
}


/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
CPoint	CElemBank::GetElemPos(int ID,int Width)
{
	if (ID==0)
		return(CPoint(-1,-1));
	else
		return(IDToPoint(ID-1,Width));
}

/*****************************************************************************/
void	CElemBank::RenderElem(int Set,int Elem,int Flags,bool Is3d)
{
		if (IsValid(Set,Elem))
		{
			SetList[Set].RenderElem(Elem,Flags,Is3d);
		}
		else
		{
			SetList[0].RenderInvalid();
		}
		
}

/*****************************************************************************/
void	CElemBank::RenderGrid(CCore *Core,Vector3 &CamPos,bool Active)
{
		if (!GetSetCount()) return;
CElemSet	&ThisSet=SetList[CurrentSet];
int			ListSize=ThisSet.GetCount();
int			BrowserWidth=ThisSet.GetBrowserWidth();
int			TileID=1;	// Dont bother with blank, its sorted
float		Scale=CamPos.z/(float)BrowserWidth/2.0;
		
		if (!ListSize) return;
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();

		while(TileID!=ListSize)
		{
			CPoint	Pos=GetElemPos(TileID,BrowserWidth);
			float	XPos=(float)Pos.x*(1+ElemBrowserGap);
			float	YPos=(float)Pos.y*(1+ElemBrowserGap);

			glLoadIdentity();
			glScalef(Scale,Scale,Scale);
			glTranslatef(-CamPos.x+XPos,CamPos.y-YPos,0);
			
			glBegin(GL_LINES); 
				glColor3f(1,1,1);
			
				glVertex3f( ElemBrowserX0,ElemBrowserY0,0);
				glVertex3f( ElemBrowserX1,ElemBrowserY0,0);

				glVertex3f( ElemBrowserX0,ElemBrowserY1,0);
				glVertex3f( ElemBrowserX1,ElemBrowserY1,0);

				glVertex3f( ElemBrowserX0,ElemBrowserY0,0);
				glVertex3f( ElemBrowserX0,ElemBrowserY1,0);

				glVertex3f( ElemBrowserX1,ElemBrowserY0,0);
				glVertex3f( ElemBrowserX1,ElemBrowserY1,0);

			glEnd();

			TileID++;
		}
		glPopMatrix();
}

/*****************************************************************************/
void	CElemBank::FindCursorPos(CCore *Core,Vector3 &CamPos,CPoint &MousePos)
{
		if (!GetSetCount()) return;
CElemSet	&ThisSet=SetList[CurrentSet];
int			ListSize=ThisSet.GetCount();
int			BrowserWidth=ThisSet.GetBrowserWidth();
GLint		Viewport[4];
GLuint		SelectBuffer[SELECT_BUFFER_SIZE];
int			HitCount;
int			TileID=0;
float		Scale=CamPos.z/(float)BrowserWidth/2.0;
		
		if (!ListSize) return;
		glGetIntegerv(GL_VIEWPORT, Viewport);
		glSelectBuffer (SELECT_BUFFER_SIZE, SelectBuffer );
		glRenderMode (GL_SELECT);

	    glInitNames();
		glPushName(-1);

		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		gluPickMatrix( MousePos.x ,(Viewport[3]-MousePos.y),5.0,5.0,Viewport);
		Core->GetView()->SetupPersMatrix();

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();

		while(TileID!=ListSize)
		{
			CPoint	Pos=GetElemPos(TileID,BrowserWidth);
			float	XPos=(float)Pos.x*(1+ElemBrowserGap);
			float	YPos=(float)Pos.y*(1+ElemBrowserGap);

			glLoadIdentity();
			glScalef(Scale,Scale,Scale);
			glTranslatef(-CamPos.x+XPos,CamPos.y-YPos,0);

			glLoadName (TileID);
			glBegin (GL_QUADS); 
				BuildGLQuad(ElemBrowserX0,ElemBrowserX1,ElemBrowserY0,ElemBrowserY1,0);
			glEnd();
			TileID++;
		}

		HitCount= glRenderMode (GL_RENDER);
		glPopMatrix();
		glMatrixMode(GL_PROJECTION);
		glPopMatrix();

// Process hits

GLuint	*HitPtr=SelectBuffer;

		TileID=-2;
		if (HitCount)	// Just take 1st		
		{
			TileID=HitPtr[3];
		}
		glMatrixMode(GL_MODELVIEW);	// <-- Prevent arse GL assert

		CursorPos=TileID;
}

/*****************************************************************************/
/*** Gui *********************************************************************/
/*****************************************************************************/
void	CElemBank::GUIInit(CCore *Core)
{
		Core->GUIAdd(GUIElemList,IDD_ELEMLIST);
}

/*****************************************************************************/
void	CElemBank::GUIKill(CCore *Core)
{
		Core->GUIRemove(GUIElemList,IDD_ELEMLIST);
}

/*****************************************************************************/
void	CElemBank::GUIUpdate(CCore *Core)
{
int			ListSize=GetSetCount();
bool		IsSubView=Core->IsSubView();

			if (GUIElemList.m_List)
			{
				GUIElemList.m_List.ResetContent();
				if (ListSize)
				{
					for (int i=0; i<ListSize; i++)
					{
						GUIElemList.m_List.AddString(GetSetName(i));
					}
					GUIElemList.m_List.SetCurSel(CurrentSet);
				}
				else
				{
					IsSubView=FALSE;
				}
				GUIElemList.m_List.EnableWindow(IsSubView);
			}

}

/*****************************************************************************/
void	CElemBank::GUIChanged(CCore *Core)
{
}

/*****************************************************************************/