#pragma once

struct SuperFile
{
	FILE *f;
	bool writing;
	int chunksize;
	int chunkstart;
	int inchunk;
	int eof;

	bool Eof()
	{
		return eof || feof(f);
	}
	bool Open(char *fname,bool forwrite);
	void Close();

	int ReadNextChunk(int &id, int &ver);
	int FindChunk(int id, int ver);
	int StartChunk(int id, int ver);
	void EndChunk();
	void Write(void *data, int size);
	void Read(void *data, int size);

	SuperFile()
	{
		eof=0;
		inchunk=0;
		f=NULL;
		writing=0;
		chunksize=chunkstart=0;	
	}

};


#define MESH_CHUNK 0x1234
#define VERT_CHUNK 0x2345
#define FACE_CHUNK 0x3456
#define EDGE_CHUNK 0x4567
#define CAM_CHUNK 0x6567
#define LGT_CHUNK 0x7567
void __cdecl Report3D(const char *pFormat,... );
__inline D3DTexture *LoadTexture(char *fname)
{
	D3DTexture *t=NULL;
	
	if (strnicmp(fname,"c:\\art\\",7)==0) fname+=7;
	if (strnicmp(fname,"c:/art/",7)==0) fname+=7;	
	Report3D("loading texture %s\n",fname);
	D3DXCreateTextureFromFile(d3ddev,fname,&t);
	return t;
}



struct MePrim
{
	D3DIBuf *ibuf;
	D3DIBuf *ebuf;
	D3DTexture *tex1,*tex2;
	ULONG diff;
	ULONG flags;
	float selfillum;
	int numi;

	
	MePrim()
	{
		ibuf=NULL;
		ebuf=NULL;
		tex1=NULL;
		tex2=NULL;
		diff=-1;
		flags=0;
		selfillum=0;
	}

	~MePrim()
	{
		/*if (ibuf) ibuf->Delete();
		if (ebuf) ebuf->Delete();
		if (tex1) tex1->Delete();
		if (tex2) tex2->Delete();
		*/
	}

	void Load(SuperFile &f)
	{
		char texname[256];
		char envname[256];
		if (f.StartChunk(FACE_CHUNK,1)<=0) return;
		f.Read(texname,256);
		f.Read(envname,256);
		tex1=texname[0]?LoadTexture(texname):NULL;
		tex2=envname[0]?LoadTexture(envname):NULL;
		f.Read(&diff,4);
		f.Read(&flags,4);
		f.Read(&selfillum,4);
		int i;
		f.Read(&i,4);
		//ibuf= BS3::IndexBuffer::Create(D3DPT_TRIANGLELIST,0);
		ibuf=NULL;
		numi=i*3;
		d3ddev->CreateIndexBuffer(i*3*2,D3DUSAGE_DYNAMIC,D3DFMT_INDEX16,D3DPOOL_DEFAULT,&ibuf);
		WORD *ww=NULL;
		ibuf->Lock(0,0,(unsigned char**)&ww,0);
		for (int j=0;j<i;j++)
		{
			int a,b,c;
			f.Read((void*)&a,4);				
			f.Read((void*)&b,4);				
			f.Read((void*)&c,4);				
			ww[j*3+0]=c;
			ww[j*3+1]=b;
			ww[j*3+2]=a;
		}
		ibuf->Unlock();
		f.EndChunk();
		if (f.StartChunk(EDGE_CHUNK,1)<=0) return;
		{
			f.Read(&i,4);
			//ebuf=BS3::IndexBuffer::Create(D3DPT_LINELIST,0);
			//BS3::WordList &wl = ebuf.GetWritable()->Lock();
			//wl.resize(i*2);
			ebuf=NULL;
			d3ddev->CreateIndexBuffer(i*2*2,D3DUSAGE_DYNAMIC,D3DFMT_INDEX16,D3DPOOL_DEFAULT,&ebuf);
			WORD *ww=NULL;
			ebuf->Lock(0,0,(unsigned char**)&ww,0);
			for (int j=0;j<i;j++)
			{
				int a,b;
				f.Read((void*)&a,4);				
				f.Read((void*)&b,4);				
				ww[j*2+0]=a;
				ww[j*2+1]=b;
			}
			ebuf->Unlock();
			f.EndChunk();
		}
	}

};

typedef std::vector<MePrim*> MePrimList;


struct MEVert
{
	v3 p,n;
	float u,v;
	DWORD vcol;
};



struct MeObj
{
	D3DVBuf *vbuf;
	MePrimList prim;
	m44 tmat;
	int numv;

	~MeObj()
	{
		//if (vbuf!=NULL) vbuf->Delete();
		while (!prim.empty())
		{
			delete prim.back();
			prim.pop_back();
		}
	}

	MeObj()
	{
		numv=0;
		vbuf=NULL;
		tmat.Identity();
	}


	void DrawToShadow(m44 mat, SimpleBuffer2D &b, bool actuallydraw)
	{
		mat = tmat*mat;
		d3ddev->SetTexture(0,NULL);		

		SimpleVert *sv;
		vbuf->Lock(0,0,(BYTE**)&sv,D3DLOCK_READONLY);
		
		for (int c1=0;c1<prim.size();c1++)
		{
			MePrim &p = *prim[c1];
			WORD *ii;
			p.ibuf->Lock(0,0,(BYTE**)&ii,D3DLOCK_READONLY);
			for (int f=0;f<p.numi/3;f++)
			{
				int vi[3];
				for (int i=0;i<3;i++)
				{
					v3 pp = sv[*ii++].p * mat;
					ShadowProject(pp,actuallydraw);
					if (actuallydraw) vi[i]=b.AddVertex(pp,0xff000000);
				}
				if (actuallydraw)
				{
					b.AddTri(vi[0],vi[1],vi[2]);				
					b.Flush();
				}
			}
			b.Draw();
			p.ibuf->Unlock();
		}

		vbuf->Unlock();
	}

	void DrawBlobs(m44 mat, SimpleBufferUV2 &b, float rad, DWORD col)
	{
		mat = tmat*mat;

		SetWorldMat();
		

		SimpleVert *sv;
		vbuf->Lock(0,0,(BYTE**)&sv,D3DLOCK_READONLY);

		std::vector<int> dv;
		dv.resize(numv,-1);
		
		for (int c1=0;c1<prim.size();c1++)
		{
			MePrim &p = *prim[c1];
			WORD *ii;
			p.ibuf->Lock(0,0,(BYTE**)&ii,D3DLOCK_READONLY);
			d3ddev->SetTexture(1,p.tex1);
			for (int f=0;f<p.numi/3;f++)
			{				
				for (int i=0;i<3;i++,ii++) if (rand()<300)
				{
					int v = *ii;

					if (dv[v]!=c1)
					{
						dv[v]=c1;
					

						v3 pp = sv[v].p * mat;
						b.AddBlob(pp,col,GaussRandom(0.8f,1.2f)*rad,Random(-PI,PI),0,0,1,1,sv[v].u,sv[v].v,sv[v].u,sv[v].v);
						b.Flush();			
					}
				}
			}
			b.Draw();
			d3ddev->SetTexture(1,NULL);
		}
		

		vbuf->Unlock();
	}

	void Draw(m44 mat)
	{
		SetWorldMat(tmat*mat);
		d3ddev->SetVertexShader(SimpleVertFVF);
		d3ddev->SetStreamSource(0,vbuf,sizeof(SimpleVert));
		for (int c1=0;c1<prim.size();c1++)
		{
			MePrim &p = *prim[c1];
			d3ddev->SetTexture(0,p.tex1);
			d3ddev->SetIndices(p.ibuf,0);
			d3ddev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,numv,0,p.numi/3);
		}
		d3ddev->SetTexture(0,NULL);
		mat.Identity();
		SetWorldMat(mat);
	}

	bool Load(SuperFile &f, float SCALEFAC, bool normals, int black=0, int alpha=255)
	{
		if (f.StartChunk(MESH_CHUNK,1)<=0) return false;
		float ff=0;
		f.Read(&tmat,16*4);

		m44 mm;
		mm.Identity();
		mm.m44[1][1]=0;
		mm.m44[1][2]=1;
		mm.m44[2][1]=1;
		mm.m44[2][2]=0;
		tmat=tmat*mm;

		tmat.SetT(tmat.GetT()*SCALEFAC);

		float det = D3DXMatrixfDeterminant((D3DXMATRIX*)&tmat);
		

		int numsub;
		f.Read(&numsub,4);				
		f.EndChunk();
		prim.clear();
		if (f.StartChunk(VERT_CHUNK,2)<=0) return false;		
		f.Read(&numv,4);
		//vbuf=BS3::VertexBuffer::Create(D3DFVF_XYZ|(normals?D3DFVF_NORMAL:0)|D3DFVF_DIFFUSE|D3DFVF_TEX1,0);
		//BS3::VertexIterator vit(vbuf,numv);
		vbuf=NULL;
		d3ddev->CreateVertexBuffer(numv*sizeof(SimpleVert),D3DUSAGE_DYNAMIC,SimpleVertFVF,D3DPOOL_DEFAULT,&vbuf);
		SimpleVert *ww=NULL;
		vbuf->Lock(0,0,(unsigned char**)&ww,0);
		for (int i=0;i<numv;i++)
		{
			//Report3D("vertex %d %0.5f, %0.5f, %0.5f normal %0.5f, %0.5f, %0.5f uv %0.5f %0.5f\n",idx,ii->x,ii->y,ii->z,ii->nx,ii->ny,ii->nz,ii->u,ii->v);
			MEVert mv;
			f.Read((void*)&(mv),36);
			mv.n=-mv.n;
			mv.vcol = (((mv.vcol>>16)&0xff)<<0) | (((mv.vcol>>8)&0xff)<<8) | (((mv.vcol>>0)&0xff)<<16) | (alpha<<24);

			ww->p=mv.p * SCALEFAC;
			//ww->n=Normalize(ww->p);
			//if (normals) ww->n=BS3::Normalize(mv.n);
			ww->u=mv.u;
			ww->v=1-mv.v;
			ww->col=black?0xff000000: mv.vcol;
			++ww;
		}
		//not needed vbuf=vit.myvb;
		f.EndChunk();

		vbuf->Unlock();

		for (int c1=0;c1<numsub;c1++)
		{
			prim.push_back(new MePrim());
			prim.back()->Load(f);
		}

		return true;
	}

};

typedef std::vector<MeObj*> MeObjList;

////////////////////////////////////////////////////////////////////// MeObj
