#include "Primitive.h"

namespace Brainstem
{
	Primitive::Primitive(IDirect3DDevice9* device) 
	{ 
		Device = device; 

		Mesh = NULL; 
		Effect = NULL;
		World.SetIdentity(); 

		SetTechnique("Default");
		SetDefaultProperties();
	}

	Primitive::~Primitive() { }

	void Primitive::SetEffect(ID3DXEffect* effect)
	{
		Effect = effect;
	}

	void Primitive::Update() 
	{ 
		Matrix4<float> scale;
		Matrix4<float> trans;
		Matrix4<float> rot;

		scale.SetScale(Scale);
		trans.SetTranslation(Position);
		rot.SetRotationRadians(Rotation);

		World = trans * rot * scale;
	}

	void Primitive::ApplyTexture(ITexture* texture)
	{
		Tex = texture;
	}
	
	ITexture* Primitive::GetTexture()
	{
		return Tex;
	}

	void Primitive::Render(ICamera* camera) 
	{ 
		Texture* textureData = (Texture*)((ITexture*)Tex);

		if (textureData != NULL)
			if (textureData->Data != NULL)
				Effect->SetTexture("colorMapTexture", textureData->Data);

		float textureScaling[2] = { TextureScaling.X, TextureScaling.Y };
		Effect->SetValue("textureScale", textureScaling, sizeof(textureScaling));

		Effect->CommitChanges();

		Mesh->Render(); 
	}

	void Primitive::Release() { }

	Teapot::Teapot(IDirect3DDevice9* device) : Primitive(device) { }
	Teapot::~Teapot() { Release(); }

	bool Teapot::Init() 
	{ 
		LPD3DXMESH initMesh;
		LPD3DXMESH texMesh;
		D3DXCreateTeapot(Device, &initMesh, NULL); 

		Mesh = new IMesh();

		initMesh->CloneMeshFVF(D3DXMESH_SYSTEMMEM, Vertex::FVF, Device, &texMesh);
		initMesh->Release();

		Vertex* pVerts;
		texMesh->LockVertexBuffer(0,(LPVOID*)&pVerts);

		int numVerts = texMesh->GetNumVertices();

		for (int i = 0;i < numVerts;i++) 
		{
			Mesh->Bounds.Set(Vector3<float>(pVerts->Position.x, 
				pVerts->Position.y, 
				pVerts->Position.z));
            
			pVerts++;
		}

		texMesh->UnlockVertexBuffer();

		Mesh->Mesh = texMesh;

		return true;
	}

	Plane::Plane(IDirect3DDevice9* device, float width, float height) : Primitive(device) 
	{ 
		Width = width;
		Height = height;
		CullMode = CULL_NONE;
	}
	Plane::~Plane() { Release(); }

	bool Plane::Init() 
	{ 
		LPD3DXMESH initMesh;
		D3DXCreateMeshFVF(2, 4, D3DXMESH_MANAGED, Vertex::FVF, Device, &initMesh);

		Mesh = new IMesh(initMesh);

		Vertex* v = 0;
		initMesh->LockVertexBuffer(0, (void**)&v);

		v[0] = Vertex(Vector3<float>(-1.0f, -1.0f, 0.0f), 0.0f, 1.0f);
		v[1] = Vertex(Vector3<float>(-1.0f,  1.0f, 0.0f), 0.0f, 0.0f);
		v[2] = Vertex(Vector3<float>(1.0f,  1.0f, 0.0f), 1.0f, 0.0f);
		v[3] = Vertex(Vector3<float>(1.0f, -1.0f, 0.0f), 1.0f, 1.0f);

		Mesh->Bounds.Min = Vector3<float>(-1.0f, -1.0f, 0.0f);
		Mesh->Bounds.Max = Vector3<float>(1.0f, 1.0f, 0.0f);

		initMesh->UnlockVertexBuffer();

		WORD* i = 0;
		initMesh->LockIndexBuffer(0, (void**)&i);

		i[0] = 0; i[1] = 1; i[2] = 2;
		i[3] = 0; i[4] = 2; i[5] = 3;

		initMesh->UnlockIndexBuffer();

		std::vector<DWORD> adjacencyBuffer(initMesh->GetNumFaces() * 3);
		initMesh->GenerateAdjacency(0, &adjacencyBuffer[0]);

		D3DXComputeNormals(initMesh, 0);
		D3DXComputeTangentFrame(initMesh, 0);

		return true;
	}

	Sphere::Sphere(IDirect3DDevice9* device, float radius, unsigned int slices, unsigned int stacks) : Primitive(device)
	{ 
		Radius = radius;
		Slices = slices;
		Stacks = stacks;
	}

	Sphere::~Sphere() { Release(); }

	bool Sphere::Init() 
	{ 
		LPD3DXMESH initMesh;
		LPD3DXMESH texMesh;

		Mesh = new IMesh();

		D3DXCreateSphere(Device, Radius, Slices, Stacks, &initMesh, NULL); 

		initMesh->CloneMeshFVF(D3DXMESH_SYSTEMMEM, Vertex::FVF, Device, &texMesh);
		initMesh->Release();

		Vertex* pVerts;
		texMesh->LockVertexBuffer(0,(LPVOID*)&pVerts);

		int numVerts = texMesh->GetNumVertices();

		for (int i = 0;i < numVerts;i++) {

			pVerts->tU = asinf(pVerts->Normal.x) / D3DX_PI + 0.5f;
			pVerts->tV = asinf(pVerts->Normal.y) / D3DX_PI + 0.5f;

			Mesh->Bounds.Set(Vector3<float>(pVerts->Position.x, 
				pVerts->Position.y, 
				pVerts->Position.z));
            
			pVerts++;
		}

		D3DXComputeTangentFrame(texMesh, 0);

		texMesh->UnlockVertexBuffer();

		Mesh->Mesh = texMesh;

		return true;
	}

	Box::Box(IDirect3DDevice9* device, float width, float height, float depth) : Primitive(device)
	{ 
		Width = width;
		Height = height;
		Depth = depth;
	}

	Box::~Box() { Release(); }

	bool Box::Init() 
	{ 
		LPD3DXMESH initMesh;
		LPD3DXMESH texMesh;

		Mesh = new IMesh();

		D3DXCreateBox(Device, Width, Height, Depth, &initMesh, NULL);

		initMesh->CloneMeshFVF(D3DXMESH_SYSTEMMEM, Vertex::FVF, Device, &texMesh);
		initMesh->Release();

		Vertex* pVerts;
		texMesh->LockVertexBuffer(0,(LPVOID*)&pVerts);

		const int numVerts = texMesh->GetNumVertices();

		Vector2<float> Coords[24] =
		{
			Vector2<float>(1.0f, 1.0f), 
			Vector2<float>(0.0f, 1.0f), 
			Vector2<float>(0.0f, 0.0f), 		
			Vector2<float>(1.0f, 0.0f), 

			Vector2<float>(1.0f, 1.0f), 
			Vector2<float>(0.0f, 1.0f), 
			Vector2<float>(0.0f, 0.0f),			
			Vector2<float>(1.0f, 0.0f), 

			Vector2<float>(0.0f, 0.0f), 
			Vector2<float>(1.0f, 0.0f),	
			Vector2<float>(1.0f, 1.0f),			
			Vector2<float>(0.0f, 1.0f), 

			Vector2<float>(0.0f, 0.0f),
			Vector2<float>(1.0f, 0.0f), 
			Vector2<float>(1.0f, 1.0f),	
			Vector2<float>(0.0f, 1.0f), 

			Vector2<float>(1.0f, 1.0f), 
			Vector2<float>(0.0f, 1.0f),			
			Vector2<float>(0.0f, 0.0f), 
			Vector2<float>(1.0f, 0.0f), 

			Vector2<float>(0.0f, 1.0f), 
			Vector2<float>(0.0f, 0.0f), 
			Vector2<float>(1.0f, 0.0f),			
			Vector2<float>(1.0f, 1.0f), 
		};

		for (int i = 0; i < numVerts; i++) {

			pVerts->tU = Coords[i].X;
			pVerts->tV = Coords[i].Y;

			Mesh->Bounds.Set(Vector3<float>(pVerts->Position.x, 
				pVerts->Position.y, 
				pVerts->Position.z));

			pVerts++;
		}

		D3DXComputeTangentFrame(texMesh, 0);
		texMesh->UnlockVertexBuffer();

		Mesh->Mesh = texMesh;

		return true;
	}
}