#include "SubdivisionAlgorithm.h"

SubdivisionAlgorithm::SubdivisionAlgorithm(MeshObject *mesh, const char* clothesFileName, IDirect3DDevice9 *pDev, LPD3DXMESH Body, LPD3DXMESH Clothes)
{
	pDevice = pDev;
	bodyModel = Body;
	clothes = Clothes;
	body = mesh;
	int a = bodyModel->GetNumVertices();
	int c = body->MeshData.pMesh->GetNumVertices();

	//Clone existing mesh
	animatedClothes = body->CloneMeshObject(clothesFileName);
	int b = animatedClothes->MeshData.pMesh->GetNumVertices();
	int d = clothes->GetNumVertices();
}


//Subdivide cloths mesh into 3 parts: skinny parts, more loose parts and very loose parts
void SubdivisionAlgorithm::Subdivision()
{
	avgDistance = minDistance = maxDistance = 0;
	LPDIRECT3DVERTEXBUFFER9 clothesVertexBuffer = NULL, bodyVertexBuffer = NULL;
	//Copy data from buffers
	bodyModel->GetVertexBuffer(&bodyVertexBuffer);
	clothes->GetVertexBuffer(&clothesVertexBuffer);
	const int size = 30;
	std::vector<int> *influencedVertices[size];
	std::vector<float> *influencedWeights[size];
	for (int i = 0; i < size; i++)
	{
		influencedVertices[i] = new std::vector<int>;
		influencedWeights[i] = new std::vector<float>;
	}

	int numOfBones = body->SkinInfo->GetNumBones();
	int numOfClothsVertices = clothes->GetNumVertices();
	int numOfBodyVertices = bodyModel->GetNumVertices();
	int numInfl = 0;
	//For each bone on clothes mesh	
	for (int i = 0; i < numOfClothsVertices; i++)
	{
		D3DXVECTOR3* verticesFromClothesBuffer;
		clothesVertexBuffer->Lock(0, 0, (VOID**)&verticesFromClothesBuffer, D3DLOCK_READONLY);
		D3DXVECTOR3 vertex = verticesFromClothesBuffer[i];
		clothesVertexBuffer->Unlock();
		D3DXVECTOR3* verticesFromBodyBuffer;
		bodyVertexBuffer->Lock(0, 0, (VOID**)&verticesFromBodyBuffer, D3DLOCK_READONLY);
		D3DXVECTOR3 vertexFirst = verticesFromBodyBuffer[0];

		float distance = sqrt(pow(vertexFirst.x - vertex.x, 2) + pow(vertexFirst.y - vertex.y, 2) + pow(vertexFirst.z - vertex.z, 2));
		bodyVertexBuffer->Unlock();
		minDistance = maxDistance = distance;
		int indexOfMinVert = 0;
		//Search nearest vertex on body mesh
		float dist;
		for (int j = 1; j < numOfBodyVertices; j++)
		{
			bodyVertexBuffer->Lock(0, 0, (VOID**)&verticesFromBodyBuffer, D3DLOCK_READONLY);
			D3DXVECTOR3 vertexNext = verticesFromBodyBuffer[j];			
			dist = sqrt(pow(vertexNext.x - vertex.x, 2) + pow(vertexNext.y - vertex.y, 2) + pow(vertexNext.z - vertex.z, 2));
			bodyVertexBuffer->Unlock();
			if (distance > dist)
			{
				distance = dist;
				indexOfMinVert = j;
			}
			if (dist > maxDistance)
			{
				maxDistance = dist;
			}
			if (dist < minDistance)
			{
				minDistance = dist;
			}
		}
		
		distancesToBody.push_back(distance);
		avgDistance += distance;

		//Change animation in vertex
		for (int bone = 0; bone < numOfBones; bone++)
		{
			DWORD pInfluenceIndex = NULL;
			FLOAT pWeights;
			DWORD pVertexNum;
			//If bone influences this point
			if ((body->SkinInfo->FindBoneVertexInfluenceIndex(bone, indexOfMinVert, &pInfluenceIndex)) == S_OK)
			{				
				HRESULT hr = body->SkinInfo->GetBoneVertexInfluence(bone, pInfluenceIndex, &pWeights, &pVertexNum);
				//Add info about vertices and weights
				influencedVertices[bone]->push_back(i);
				influencedWeights[bone]->push_back(pWeights);
				numInfl++;
			}
		}

	}

	avgDistance /= distancesToBody.size();
	std::vector<int> shortDistanceToBody, longDistanceToBody;

	int numOfshortDistanceToBody = 0, numOflongDistanceToBody = 0;
	int numl = 0;

	//Devide vertices into 2 groups
	for (int i = 0; i < 920; i++)
	{		
		if (distancesToBody[i] < avgDistance / 4)
		{
			numOfshortDistanceToBody++;
			shortDistanceToBody.push_back(i);
		}
		else
		{
			numOflongDistanceToBody++;
			longDistanceToBody.push_back(i);
			//Add this point as point for mass spring system
			pointsForMassSpringSystem.push_back(i);
		}
	}
	//Set new animation for each bone
	for (int bone = 0; bone < body->SkinInfo->GetNumBones(); bone++)
	{
		HRESULT hr;
		int numInfluences = influencedVertices[bone]->size();
		DWORD *Vertices = new DWORD[numInfluences];
		float *Weights = new float[numInfluences];
		int numInf = 0;
		//Fill array 
		for (int point = 0; point < influencedVertices[bone]->size(); point++)
		{
			Vertices[numInf] = influencedVertices[bone]->at(point);
			Weights[numInf] = influencedWeights[bone]->at(point);
			numInf++;
		}
		//Set influences and their weightrs for bone
		hr = animatedClothes->SkinInfo->SetBoneInfluence(bone, numInf, Vertices, Weights);
		for (int point = 0; point < numInf; point++)
		{
			//Set influences and their weightrs for point
			animatedClothes->SkinInfo->SetBoneVertexInfluence(bone, point, Weights[point]);
		}
	}

	//Update new information in model
	animatedClothes->UpdateMesh();
	animatedClothes->Map(animatedClothes->pSkinFrame);
	animatedClothes->SkinnedMeshPositioning();
}

void SubdivisionAlgorithm::DrawClothes()
{
	pDevice->Clear(NULL, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(0, 0, 64, 255), 1.0f, 0);
	if (SUCCEEDED(pDevice->BeginScene())) {

		// Render skinned mesh
		animatedClothes->DrawModel();

		// End the scene
		pDevice->EndScene();
	}

	// Present the scene to the user
	pDevice->Present(NULL, NULL, NULL, NULL);

}

std::vector<int> SubdivisionAlgorithm::GetPointsForMassSpringSystem()
{
	return pointsForMassSpringSystem;
}
