#include "OBJ.h"

bool OBJ::LoadFromFile(std::string filename)
{
	m_Path = boost::filesystem::path(filename);

	// http://paulbourke.net/dataformats/obj/
	std::ifstream file(m_Path.string());
	if (!file.is_open()) {
		LOG_ERROR("Failed to open .obj \"%s\"", m_Path.string().c_str());
		return false;
	}

	LOG_INFO("Parsing .obj \"%s\"", m_Path.string().c_str());
	
	std::string line;
	while (std::getline(file, line)) {
		if (line.length() == 0)
			continue;

		std::stringstream ss(line);

		std::string prefix;
		ss >> prefix;

		// Ignore comments
		if (prefix == "#")
			continue;

		// Material files
		if (prefix == "mtllib") {
			std::string materialFilename;
			ss >> materialFilename;
			m_MaterialPath = m_Path.branch_path() / materialFilename;
			ParseMaterial();
			continue;
		}

		// Material statement
		if (prefix == "usemtl") {
			std::string material;
			ss >> material;
			m_CurrentMaterial = &Materials[material];
			continue;
		}

		// Vertices
		if (prefix == "v") {
			float x, y, z;
			ss >> x >> y >> z;
			Vertices.push_back(std::make_tuple(x, y, z));
			continue;
		}

		// Normals
		if (prefix == "vn") {
			float x, y, z;
			ss >> x >> y >> z;
			Normals.push_back(std::make_tuple(x, y, z));
		}

		// Texture coordinates
		if (prefix == "vt")	{
			float u, v, w;
			ss >> u >> v >> w;
			TextureCoords.push_back(std::make_tuple(u, v, w));
		}

		// Face definitions
		if (prefix == "f") {
			Face face;
			face.Material = m_CurrentMaterial;

			std::string faceDefString;
			while (ss >> faceDefString) {
				std::stringstream ss2(faceDefString);
				FaceDefinition faceDef = { 0, 0, 0 };

				ss2 >> faceDef.VertexIndex;
				ss2.ignore(); // Ignore first delimiter
				if (!ss2)
					continue;

				if (ss2.peek() == '/') {
					ss2.ignore();
					ss2 >> faceDef.NormalIndex;
				} else {
					ss2 >> faceDef.TextureCoordIndex;
					ss2.ignore();
					ss2 >> faceDef.NormalIndex;
				}
				face.Definitions.push_back(faceDef);
			}
			Faces.push_back(face);
		}
	}

	return true;
}

void OBJ::ParseMaterial()
{
	// http://paulbourke.net/dataformats/mtl/
	std::ifstream file(m_MaterialPath.string());
	if (!file.is_open()) {
		LOG_ERROR("Failed to open .mtl \"%s\"", m_MaterialPath.string().c_str());
		return;
	}

	LOG_INFO("Parsing .mtl \"%s\"", m_MaterialPath.string().c_str());

	std::string currentMaterialName;
	MaterialInfo* currentMaterial = nullptr;

	std::string line;
	while (std::getline(file, line)) {
		if (line.length() == 0)
			continue;

		std::stringstream ss(line);

		std::string prefix;
		ss >> prefix;

		// Create a new material definition
		if (prefix == "newmtl")	{
			MaterialInfo mat = 
			{
				"",
				std::make_tuple(0.2f, 0.2f, 0.2f),
				std::make_tuple(0.8f, 0.8f, 0.8f),
				std::make_tuple(1.0f, 1.0f, 1.0f),
				std::make_tuple(1.0f, 1.0f, 1.0f),
				1.0f,
				1.0f,
				0.0f,
				0,
			};

			ss >> currentMaterialName;
			LOG_INFO("Parsing material %s", currentMaterialName.c_str());
			Materials[currentMaterialName] = mat;
			currentMaterial = &Materials[currentMaterialName];
			continue;
		}

		if (!currentMaterial)
			continue;

		// Ambient color
		if (prefix == "Ka")	{
			float r, g, b;
			ss >> r >> g >> b;
			currentMaterial->AmbientColor = std::make_tuple(r, g, b);
			continue;
		}
		// Diffuse color
		if (prefix == "Kd")	{
			float r, g, b;
			ss >> r >> g >> b;
			currentMaterial->DiffuseColor = std::make_tuple(r, g, b);
			continue;
		}
		// Specular color
		if (prefix == "Ks")	{
			float r, g, b;
			ss >> r >> g >> b;
			currentMaterial->SpecularColor = std::make_tuple(r, g, b);
			continue;
		}
		// Transmission filter
		if (prefix == "Tf")	{
			std::stringstream ss2;
			ss2 << ss.str();

			std::string command;
			ss2 >> command;
			if (command == "xyz") {
				// TODO: "The "Ks xyz" statement specifies the specular reflectivity using CIEXYZ values."
			}
			else if (command == "spectral")	{
				// TODO: "The "Tf spectral" statement specifies the transmission filter using a spectral curve."
			} else {
				float r, g, b;
				ss >> r;
				// G and B are optional
				if (!(ss >> g >> b))
				{
					g = r;
					b = r;
				}
				currentMaterial->TransmissionFilter = std::make_tuple(r, g, b);
			}
			continue;
		}
		// Optical density
		if (prefix == "Ni")	{
			ss >> currentMaterial->OpticalDensity;
			continue;
		}
		// Alpha
		if (prefix == "d" || prefix == "Tr") {
			ss >> currentMaterial->Alpha;
			continue;
		}
		// Shininess
		if (prefix == "Ns")	{
			ss >> currentMaterial->Shininess;
			continue;
		}
		// Illumination model
		if (prefix == "illum") {
			int illum = 0;
			ss >> illum;
			currentMaterial->IlluminationModel = illum;
			continue;
		}
		// Texture file
		// TODO:
		if (prefix == "map_Ka" || prefix == "map_Kd") {
			std::string textureFile;
			ss >> textureFile;
			currentMaterial->TextureFile = (m_MaterialPath.branch_path() / textureFile).string();
			continue;
		}
	}
}
