// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
// All rights reserved.
// Code licensed under the BSD License.
// http://www.anki3d.org/LICENSE

#include "Exporter.h"
#include <iostream>

const char* MATERIAL_TEMPLATE = R"(<?xml version="1.0" encoding="UTF-8" ?>
<!-- This file is auto generated by ExporterMaterial.cpp -->
<material shaderProgram="shaders/GBufferGeneric.glslp">

	<mutators>
		<mutator name="DIFFUSE_TEX" value="%diffTexMutator%"/>
		<mutator name="SPECULAR_TEX" value="%specTexMutator%"/>
		<mutator name="ROUGHNESS_TEX" value="%roughnessTexMutator%"/>
		<mutator name="METAL_TEX" value="%metalTexMutator%"/>
		<mutator name="NORMAL_TEX" value="%normalTexMutator%"/>
		<mutator name="PARALLAX" value="%parallaxMutator%"/>
		<mutator name="EMISSIVE_TEX" value="%emissiveTexMutator%"/>
	</mutators>

	<inputs>
		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
		<input shaderInput="prevMvp" builtin="PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX"/>
		<input shaderInput="rotationMat" builtin="ROTATION_MATRIX"/>
		<input shaderInput="globalSampler" builtin="GLOBAL_SAMPLER"/>
		%parallaxInput%

		%diff%
		%spec%
		%roughness%
		%metallic%
		%normal%
		%emission%
		%subsurface%
		%height%
	</inputs>
</material>
)";

void Exporter::exportMaterial(const aiMaterial& mtl) const
{
	aiString path;

	std::string name = getMaterialName(mtl);
	LOGI("Exporting material %s", name.c_str());

	std::string xml = MATERIAL_TEMPLATE;

	// Diffuse texture
	if(mtl.GetTextureCount(aiTextureType_DIFFUSE) > 0)
	{
		if(mtl.GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS)
		{
			std::string diffTex = m_texrpath + getFilename(path.C_Str());
			xml = replaceAllString(xml, "%diff%", "<input shaderInput=\"diffTex\" value=\"" + diffTex + "\"/>");
			xml = replaceAllString(xml, "%diffTexMutator%", "1");
		}
		else
		{
			ERROR("Failed to retrieve texture");
		}
	}
	else
	{
		aiColor3D diffCol = {0.0, 0.0, 0.0};
		mtl.Get(AI_MATKEY_COLOR_DIFFUSE, diffCol);

		xml = replaceAllString(xml,
			"%diff%",
			"<input shaderInput=\"diffColor\" value=\"" + std::to_string(diffCol[0]) + " " + std::to_string(diffCol[1])
				+ " " + std::to_string(diffCol[2]) + "\"/>");

		xml = replaceAllString(xml, "%diffTexMutator%", "0");
	}

	// Specular color
	if(mtl.GetTextureCount(aiTextureType_SPECULAR) > 0)
	{
		if(mtl.GetTexture(aiTextureType_SPECULAR, 0, &path) == AI_SUCCESS)
		{
			std::string specTex = m_texrpath + getFilename(path.C_Str());
			xml = replaceAllString(xml, "%spec%", "<input shaderInput=\"specTex\" value=\"" + specTex + "\"/>");
			xml = replaceAllString(xml, "%specTexMutator%", "1");
		}
		else
		{
			ERROR("Failed to retrieve texture");
		}
	}
	else
	{
		aiColor3D specCol = {0.0, 0.0, 0.0};
		mtl.Get(AI_MATKEY_COLOR_SPECULAR, specCol);

		xml = replaceAllString(xml,
			"%spec%",
			"<input shaderInput=\"specColor\" value=\"" + std::to_string(specCol[0]) + " " + std::to_string(specCol[1])
				+ " " + std::to_string(specCol[2]) + "\"/>");

		xml = replaceAllString(xml, "%specTexMutator%", "0");
	}

	// Roughness
	if(mtl.GetTextureCount(aiTextureType_SHININESS) > 0)
	{
		if(mtl.GetTexture(aiTextureType_SHININESS, 0, &path) == AI_SUCCESS)
		{
			std::string shininessTex = m_texrpath + getFilename(path.C_Str());
			xml = replaceAllString(
				xml, "%roughness%", "<input shaderInput=\"roughnessTex\" value=\"" + shininessTex + "\"/>");

			xml = replaceAllString(xml, "%roughnessTexMutator%", "1");
		}
		else
		{
			ERROR("Failed to retrieve texture");
		}
	}
	else
	{
		float roughness = 0.0;
		if(mtl.mAnKiProperties.find("roughness") != mtl.mAnKiProperties.end())
		{
			roughness = std::stof(mtl.mAnKiProperties.at("roughness"));
		}
		else
		{
			mtl.Get(AI_MATKEY_SHININESS, roughness);
			const float MAX_SHININESS = 511.0;
			roughness = std::min(MAX_SHININESS, roughness);
			if(roughness > MAX_SHININESS)
			{
				LOGW("Shininness exceeds %f", MAX_SHININESS);
			}

			roughness = roughness / MAX_SHININESS;
		}

		xml = replaceAllString(
			xml, "%roughness%", "<input shaderInput=\"roughness\" value=\"" + std::to_string(roughness) + "\" />");

		xml = replaceAllString(xml, "%roughnessTexMutator%", "0");
	}

	// Metallic texture
	if(mtl.GetTextureCount(aiTextureType_REFLECTION) > 0)
	{
		if(mtl.GetTexture(aiTextureType_REFLECTION, 0, &path) == AI_SUCCESS)
		{
			std::string metallicTex = m_texrpath + getFilename(path.C_Str());
			xml =
				replaceAllString(xml, "%metallic%", "<input shaderInput=\"metalTex\" value=\"" + metallicTex + "\"/>");

			xml = replaceAllString(xml, "%metalTexMutator%", "1");
		}
		else
		{
			ERROR("Failed to retrieve texture");
		}
	}
	else
	{
		float metallic = 0.0;
		if(mtl.mAnKiProperties.find("metallic") != mtl.mAnKiProperties.end())
		{
			metallic = std::stof(mtl.mAnKiProperties.at("metallic"));
		}

		xml = replaceAllString(
			xml, "%metallic%", "<input shaderInput=\"metallic\" value=\"" + std::to_string(metallic) + "\"/>");

		xml = replaceAllString(xml, "%metalTexMutator%", "0");
	}

	// Normal texture
	if(mtl.GetTextureCount(aiTextureType_NORMALS) > 0)
	{
		if(mtl.GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS)
		{
			std::string normTex = m_texrpath + getFilename(path.C_Str());
			xml = replaceAllString(xml, "%normal%", "<input shaderInput=\"normalTex\" value=\"" + normTex + "\"/>");

			xml = replaceAllString(xml, "%normalTexMutator%", "1");
		}
		else
		{
			ERROR("Failed to retrieve texture");
		}
	}
	else
	{
		xml = replaceAllString(xml, "%normal%", "");
		xml = replaceAllString(xml, "%normalTexMutator%", "0");
	}

	// Emissive texture
	if(mtl.GetTextureCount(aiTextureType_EMISSIVE) > 0)
	{
		if(mtl.GetTexture(aiTextureType_EMISSIVE, 0, &path) == AI_SUCCESS)
		{
			std::string emissiveTex = m_texrpath + getFilename(path.C_Str());
			xml = replaceAllString(
				xml, "%emission%", "<input shaderInput=\"emissiveTex\" value=\"" + emissiveTex + "\"/>");

			xml = replaceAllString(xml, "%emissiveTexMutator%", "1");
		}
		else
		{
			ERROR("Failed to retrieve texture");
		}
	}
	else
	{
		aiColor3D emissionCol = {0.0, 0.0, 0.0};
		mtl.Get(AI_MATKEY_COLOR_EMISSIVE, emissionCol);

		xml = replaceAllString(xml,
			"%emission%",
			"<input shaderInput=\"emission\" value=\"" + std::to_string(emissionCol[0]) + " "
				+ std::to_string(emissionCol[1]) + " " + std::to_string(emissionCol[2]) + "\"/>");

		xml = replaceAllString(xml, "%emissiveTexMutator%", "0");
	}

	// Subsurface
	{
		float subsurface = 0.0;
		if(mtl.mAnKiProperties.find("subsurface") != mtl.mAnKiProperties.end())
		{
			subsurface = std::stof(mtl.mAnKiProperties.at("subsurface"));
		}

		xml = replaceAllString(
			xml, "%subsurface%", "<input shaderInput=\"subsurface\" value=\"" + std::to_string(subsurface) + "\"/>");
	}

	// Height texture
	if(mtl.GetTextureCount(aiTextureType_DISPLACEMENT) > 0)
	{
		if(mtl.GetTexture(aiTextureType_DISPLACEMENT, 0, &path) == AI_SUCCESS)
		{
			std::string dispTex = m_texrpath + getFilename(path.C_Str());
			xml = replaceAllString(xml,
				"%height%",
				"<input shaderInput=\"heightTex\" value=\"" + dispTex
					+ "\"/>\n"
					  "\t\t<input shaderInput=\"heightMapScale\" value=\"0.05\"/>");

			xml = replaceAllString(
				xml, "%parallaxInput%", "<input shaderInput=\"modelViewMat\" builtin=\"MODEL_VIEW_MATRIX\"/>");

			xml = replaceAllString(xml, "%parallaxMutator%", "1");
		}
		else
		{
			ERROR("Failed to retrieve texture");
		}
	}
	else
	{
		xml = replaceAllString(xml, "%height%", "");
		xml = replaceAllString(xml, "%parallaxInput%", "");
		xml = replaceAllString(xml, "%parallaxMutator%", "0");
	}

	// Replace texture extensions with .anki
	xml = replaceAllString(xml, ".tga", ".ankitex");
	xml = replaceAllString(xml, ".png", ".ankitex");
	xml = replaceAllString(xml, ".jpg", ".ankitex");
	xml = replaceAllString(xml, ".jpeg", ".ankitex");

	// Open and write file
	std::fstream file;
	file.open(m_outputDirectory + name + ".ankimtl", std::ios::out);
	file << xml;
}
