#include "Model.h"

Model::Model()
{
	_kdRed = 1.0f;
	_kdGreen = 1.0f;
	_kdBlue = 1.0f;

	_active = false;
	_drawMethod = DrawMethod::Wireframe;

	_modelMD2Filename = "";
	_modelTextureFilename = "";
}


Model::Model(string MD2Filename, string textureFilename)
{
	_kdRed = 1.0f;
	_kdGreen = 1.0f;
	_kdBlue = 1.0f;

	_active = false;
	_drawMethod = DrawMethod::Wireframe;

	_modelMD2Filename = MD2Filename;
	_modelTextureFilename = textureFilename;
}

Model::~Model()
{
	_vertices.~vector();
	_transformedVertices.~vector();
	_polygons.~vector();
	_pendingTransforms.~vector();
	_uvCoords.~vector();	
}

string Model::GetModelFilename() const
{
	return _modelMD2Filename;
}

const char* Model::CGetModelFilename() const
{
	return _modelMD2Filename.c_str();
}

string Model::GetTextureFilename() const
{
	return _modelTextureFilename;
}

const char* Model::CGetTextureFilename() const
{
	return _modelTextureFilename.c_str();
}

void Model::SetActive(bool value)
{
	_active = value;
}

void Model::ToggleActive()
{
	_active = !_active;
}

bool Model::GetActive() const
{
	return _active;
}

void Model::SetDrawMethod(DrawMethod method)
{
	_drawMethod = method;
}

DrawMethod Model::GetDrawMethod() const
{
	return _drawMethod;
}

vector<Polygon3D>& Model::GetPolygons()
{
	return _polygons;
}

vector<Vertex>& Model::GetVertices()
{
	return _vertices;
}

vector<UVCoord>& Model::GetUVCoords()
{
	return _uvCoords;
}

const vector<Matrix>& Model::GetPendingTransforms()
{
	return _pendingTransforms;
}

size_t Model::GetPolygonCount()
{
	return _polygons.size();
}

size_t Model::GetVerticesCount()
{
	return _vertices.size();
}

size_t Model::GetUVCoordCount()
{
	return _uvCoords.size();
}

size_t Model::GetPendingTransformCount()
{
	return _pendingTransforms.size();
}

void Model::AddVertex(float x, float y, float z)
{
	_vertices.push_back(Vertex(x, y, z));
	_transformedVertices.push_back(Vertex(x, y, z));
}

void Model::AddPolygon(int index0, int index1, int index2, int uvIndex0, int uvIndex1, int uvIndex2)
{
	_polygons.push_back(Polygon3D(index0, index1, index2, uvIndex0, uvIndex1, uvIndex2));
}

void Model::AddTextureUV(float u, float v)
{
	_uvCoords.push_back(UVCoord(u, v));
}

void Model::EnqueueTransform(Matrix transform)
{
	_pendingTransforms.push_back(transform);
}

void Model::ClearPendingTransforms()
{
	_pendingTransforms.clear();
}

Texture& Model::GetTexture()
{
	return _texture;
}

const Polygon3D& Model::GetPolygon(int index) const
{
	return _polygons[index];
}

const Vertex& Model::GetVertex(int polygonIndex, int vertexPolygonIndex) const
{
	return GetVertex(GetPolygon(polygonIndex).GetIndex(vertexPolygonIndex));          
}

const Vertex& Model::GetVertex(int index) const
{
	return _transformedVertices[index];
}

const UVCoord& Model::GetUVCoord(int index) const
{
	return _uvCoords[index];
}

vector<Vertex> Model::GetPolygonVertexArray(int index) const
{
	vector<Vertex> polygonVerticies = {};

	for (int i = 0; i < GetPolygon(index).GetPolygonVertexCount(); i++)
	{
		polygonVerticies.push_back(GetVertex(index, i));
	}
	return polygonVerticies;
}

void Model::SetPolygonColor(int index, COLORREF color)
{
	_polygons[index].SetColor(color);
}

void Model::SetVertexColor(int polyIndex, int vertIndex, COLORREF color)
{
	_transformedVertices[GetPolygon(polyIndex).GetIndex(vertIndex)].SetColor(color);
}

void Model::SetReflectionCoefficient(float red, float green, float blue)
{
	_kdRed = BoundsCheck(0.0f, 1.0f, red);
	_kdGreen = BoundsCheck(0.0f, 1.0f, green);
	_kdBlue = BoundsCheck(0.0f, 1.0f, blue);
}

void Model::SetReflectionCoefficient(ColRef color, float value)
{
	float valueChecked = BoundsCheck(0.0f, 1.0f, value);	

	switch (color)
	{
	default:
	case ColRef::Red:
		_kdRed = valueChecked;
		break;
	case ColRef::Green:
		_kdGreen = valueChecked;
		break;
	case ColRef::Blue:
		_kdBlue = valueChecked;
		break;	
	}
}

void Model::SetRedReflectionCoefficient(float value)
{
	SetReflectionCoefficient(ColRef::Red, value);
}

void Model::SetGreenReflectionCoefficient(float value)
{
	SetReflectionCoefficient(ColRef::Green, value);
}

void Model::SetBlueReflectionCoefficient(float value)
{
	SetReflectionCoefficient(ColRef::Blue, value);
}

float Model::GetReflectionCoefficient(ColRef color) const
{
	float result;
	switch (color)
	{
	default:
	case ColRef::Red:
		result = _kdRed;
		break;
	case ColRef::Green:
		result = _kdGreen;
		break;
	case ColRef::Blue:
		result = _kdBlue;
		break;
	}

	return result;
}

float Model::GetRedReflectionCoefficient() const
{
	return GetReflectionCoefficient(ColRef::Red);
}

float Model::GetGreenReflectionCoefficient() const
{
	return GetReflectionCoefficient(ColRef::Green);
}

float Model::GetBlueReflectionCoefficient() const
{
	return GetReflectionCoefficient(ColRef::Blue);
}

void Model::ApplyTransformToLocalVertices(const Matrix& transform)
{
	for (int i = 0; i < GetVerticesCount(); i++)
	{
		_transformedVertices[i] = transform * _vertices[i];
	}
}

void Model::ApplyTransformToTransformedVertices(const Matrix& transform)
{
	for (int i = 0; i < GetVerticesCount(); i++)
	{
		_transformedVertices[i] = transform * _transformedVertices[i];
	}
}

void Model::DehomogenizeAllVertices()
{
	for (Vertex& currentVertex : _transformedVertices)
	{
		currentVertex.Dehomogenize();
	}
}

void Model::CalculateBackfaces(Camera& currentCamera)
{
	for (Polygon3D& currentPolygon : _polygons)
	{
		Vector3D vectorA = _transformedVertices[currentPolygon.GetIndex(1)] - _transformedVertices[currentPolygon.GetIndex(0)];
		Vector3D vectorB = _transformedVertices[currentPolygon.GetIndex(2)] - _transformedVertices[currentPolygon.GetIndex(0)];
		currentPolygon.SetNormal(Vector3D::CrossProduct(vectorA, vectorB));		
		currentPolygon.NormalizeNormal();
		Vector3D eyeVector = _transformedVertices[currentPolygon.GetIndex(0)] - currentCamera.GetCameraPosition();
		eyeVector.Normalize();

		float dotProduct = Vector3D::DotProduct(currentPolygon.GetNormal(), eyeVector);
		if (dotProduct < 0)
		{
			currentPolygon.SetCulled(true);
		}
		else
		{
			currentPolygon.SetCulled(false);
		}
	}
}

void Model::CalculateVertexNormals()
{
	for (Vertex& currentVertex : _transformedVertices)
	{
		currentVertex.ResetNormal(true);
	}

	for (int pi = 0; pi < GetPolygonCount(); pi++)
	{
		for (int i = 0; i < _polygons[pi].GetPolygonVertexCount(); i++)
		{
			_transformedVertices[_polygons[pi].GetIndex(i)].SetNormal(_transformedVertices[_polygons[pi].GetIndex(i)].GetNormal() + _polygons[pi].GetNormal());
			_transformedVertices[_polygons[pi].GetIndex(i)].IncrementContributeCount();
			_transformedVertices[_polygons[pi].GetIndex(i)].SetUVIndex(_polygons[pi].GetUVIndex(i));
		}
	}

	for (Vertex& currentVertex : _transformedVertices)
	{
		
			currentVertex.SetNormal(currentVertex.GetNormal() / currentVertex.GetContributeCount());
			currentVertex.NormalizeNormal();
		
	}
}

bool DepthCompare(Polygon3D& a, Polygon3D& b)
{
	return a.GetDepth() > b.GetDepth();
}

void Model::Sort()
{
	for (Polygon3D& currentPolygon : _polygons)
	{
		float zTotal = 0.0f;
		for (int i = 0; i < currentPolygon.GetPolygonVertexCount(); i++)
		{
			zTotal += _transformedVertices[currentPolygon.GetIndex(i)].GetZ();
		}
		currentPolygon.SetDepth(zTotal / currentPolygon.GetPolygonVertexCount());
	}

	sort(_polygons.begin(), _polygons.end(), DepthCompare);
}