#include "stdafx.h"
#include "WavefrontOBJ.h"

#include "File.h"
#include "Path.h"
#include "StreamWriter.h"

namespace Assets::Exporters
{
	bool WavefrontOBJ::ExportAnimation(const Animation& Animation, const String& Path)
	{
		return false;
	}

	bool WavefrontOBJ::ExportModel(const Model& Model, const String& Path)
	{
		auto Writer = IO::StreamWriter(IO::File::Create(Path));
		auto MaterialPath = IO::Path::ChangeExtension(Path, ".mtl");

		Writer.WriteLineFmt("\nmtllib %s\n", (char*)IO::Path::GetFileName(MaterialPath));

		for (auto& Submesh : Model.Meshes)
		{
			for (auto& Face : Submesh.Faces)
			{
				auto& FaceVert1 = Submesh.Vertices[Face[0]].Position();
				auto& FaceVert2 = Submesh.Vertices[Face[1]].Position();
				auto& FaceVert3 = Submesh.Vertices[Face[2]].Position();

				Writer.WriteLineFmt(
					"v %f %f %f\n"
					"v %f %f %f\n"
					"v %f %f %f",
					FaceVert1.X, FaceVert1.Y, FaceVert1.Z,
					FaceVert2.X, FaceVert2.Y, FaceVert2.Z,
					FaceVert3.X, FaceVert3.Y, FaceVert3.Z
				);
			}
		}

		for (auto& Submesh : Model.Meshes)
		{
			for (auto& Face : Submesh.Faces)
			{
				auto& FaceVert1 = Submesh.Vertices[Face[0]].UVLayers(0);
				auto& FaceVert2 = Submesh.Vertices[Face[1]].UVLayers(0);
				auto& FaceVert3 = Submesh.Vertices[Face[2]].UVLayers(0);

				Writer.WriteLineFmt(
					"vt %f %f\n"
					"vt %f %f\n"
					"vt %f %f",
					FaceVert1.U, (1 - FaceVert1.V),
					FaceVert2.U, (1 - FaceVert2.V),
					FaceVert3.U, (1 - FaceVert3.V)
				);
			}
		}

		for (auto& Submesh : Model.Meshes)
		{
			for (auto& Face : Submesh.Faces)
			{
				auto& FaceVert1 = Submesh.Vertices[Face[0]].Normal();
				auto& FaceVert2 = Submesh.Vertices[Face[1]].Normal();
				auto& FaceVert3 = Submesh.Vertices[Face[2]].Normal();

				Writer.WriteLineFmt(
					"vn %f %f %f\n"
					"vn %f %f %f\n"
					"vn %f %f %f",
					FaceVert1.X, FaceVert1.Y, FaceVert1.Z,
					FaceVert2.X, FaceVert2.Y, FaceVert2.Z,
					FaceVert3.X, FaceVert3.Y, FaceVert3.Z
				);
			}
		}

		uint64_t VertexIndex = 1;

		for (auto& Submesh : Model.Meshes)
		{
			if (Submesh.MaterialIndices[0] > -1)
			{
				auto& Material = Model.Materials[Submesh.MaterialIndices[0]];

				Writer.WriteLineFmt(
					"g %s\n"
					"usemtl %s",
					(char*)Material.Name,
					(char*)Material.Name
				);
			}
			else
			{
				Writer.WriteLine(
					"g default_material\n"
					"usemtl default_material"
				);
			}

			for (auto& Face : Submesh.Faces)
			{
				Writer.WriteLineFmt(
					"f %d/%d/%d %d/%d/%d %d/%d/%d",
					(VertexIndex + 2), (VertexIndex + 2), (VertexIndex + 2),
					(VertexIndex + 1), (VertexIndex + 1), (VertexIndex + 1),
					VertexIndex, VertexIndex, VertexIndex
				);

				VertexIndex += 3;
			}
		}

		
		auto MatWriter = IO::StreamWriter(IO::File::Create(MaterialPath));

		for (auto& Material : Model.Materials)
		{
			MatWriter.WriteLineFmt("newmtl %s", (char*)Material.Name);

			MatWriter.WriteLine(
				"illum 4\n"
				"Kd 0.00 0.00 0.00\n"
				"Ka 0.00 0.00 0.00\n"
				"Ks 0.50 0.50 0.50"
			);

			if (Material.Slots.ContainsKey(MaterialSlotType::Albedo))
				MatWriter.WriteLineFmt("map_Kd %s", (char*)Material.Slots[MaterialSlotType::Albedo].first);
			else if (Material.Slots.ContainsKey(MaterialSlotType::Diffuse))
				MatWriter.WriteLineFmt("map_Kd %s", (char*)Material.Slots[MaterialSlotType::Diffuse].first);

			if (Material.Slots.ContainsKey(MaterialSlotType::Normal))
				MatWriter.WriteLineFmt("map_bump %s", (char*)Material.Slots[MaterialSlotType::Normal].first);
			if (Material.Slots.ContainsKey(MaterialSlotType::Specular))
				MatWriter.WriteLineFmt("map_Ks %s", (char*)Material.Slots[MaterialSlotType::Specular].first);
		}

		return true;
	}

	imstring WavefrontOBJ::ModelExtension()
	{
		return ".obj";
	}

	imstring WavefrontOBJ::AnimationExtension()
	{
		return nullptr;
	}

	ExporterScale WavefrontOBJ::ExportScale()
	{
		// OBJ is considered generic, so we won't enforce a scale constant
		return ExporterScale::Default;
	}

	bool WavefrontOBJ::SupportsAnimations()
	{
		return false;
	}

	bool WavefrontOBJ::SupportsModels()
	{
		return true;
	}
}
