#include "stdafx.h"
#include "AssimpUtil.h"

#include <QFileInfo>

#include <LibGeo/Path.h>
#include <sstream>

#include <C2/lm/range1.h>

#include <C2/util/string_util.h>

#include "../FileUtil.h"



void AssimpUtil::CopyAIVerts(lib_geo::BaseMesh& mesh_dst, aiMesh* mesh)
{
	mesh_dst.m_Verts.resize(mesh->mNumVertices);
	for (unsigned int i = 0; i < mesh->mNumVertices; ++i)
	{
		const aiVector3D& v = mesh->mVertices[i];
		mesh_dst.m_Verts[i].set(v.x, v.y, v.z);
	}

	if (mesh->HasNormals())
	{
		mesh_dst.m_Normals.resize(mesh->mNumVertices);
		for (unsigned int i = 0; i < mesh->mNumVertices; ++i)
		{
			const aiVector3D& n = mesh->mNormals[i];
			mesh_dst.m_Normals[i].set(n.x, n.y, n.z);
		}
	}

	aiVector3D* tbuf = mesh->mTextureCoords[0];
	if (tbuf)
	{
		mesh_dst.m_UVs.resize(mesh->mNumVertices);
		for (unsigned int i = 0; i < mesh->mNumVertices; ++i)
		{
			const aiVector3D& v = mesh->mTextureCoords[0][i];
			mesh_dst.m_UVs[i].set(v.x, v.y);
		}
	}
}

void AssimpUtil::CopyAIFaces(lib_geo::BaseMesh& mesh_dst, aiMesh* mesh)
{
	mesh_dst.m_Faces.resize(mesh->mNumFaces);
	for (unsigned int i = 0; i < mesh->mNumFaces; ++i)
	{
		const aiFace& sf = mesh->mFaces[i];
		lib_geo::BaseFace& df = mesh_dst.m_Faces[i];

		df.m_VertIds.resize(sf.mNumIndices);
		for (unsigned int k = 0; k < sf.mNumIndices; ++k)
		{
			df.m_VertIds[k] = sf.mIndices[k];
		}

		df.m_MatIdx = mesh->mMaterialIndex;
	}

	if (mesh_dst.HasUV())
		mesh_dst.SyncFaceVidToUVid();

	if (mesh_dst.HasNormal())
		mesh_dst.SyncFaceVidToNid();
}

lgr::color4f AssimpUtil::AIToColor(aiMaterialProperty* prop)
{
	lgr::color4f col;

	int elems = prop->mDataLength / sizeof(float);
	float* val = (float*)prop->mData;
	if (elems >= 1)
		col.r() = val[0];
	if (elems >= 2)
		col.g() = val[1];
	if (elems >= 3)
		col.b() = val[2];

	if (elems >= 4)
		col.a() = val[3];
	else
		col.a() = 1.0f;

	return col;
}

std::string AssimpUtil::GetTextureNameFromFilePath(const std::string& filepath)
{
	QString path_qs = QString::fromLocal8Bit(filepath.c_str());
	QString name_qs = QFileInfo(path_qs).fileName();
	return name_qs.toLocal8Bit().data();
}

void AssimpUtil::CopyMaterials(MeshBuf* mbuf, const aiScene* ai_scene, gl::TextureConfig& texConf, const std::string& dirpath)
{
	if (!ai_scene->HasMaterials())
		return;

	lib_geo::BaseMesh& mesh_dst = mbuf->m_Mesh;

	mesh_dst.m_Materials.resize(ai_scene->mNumMaterials);
	mbuf->CreateTextureBuf(ai_scene->mNumMaterials);
	for (unsigned int i = 0; i < ai_scene->mNumMaterials; ++i)
	{
		lib_geo::BaseMaterial& mat = mesh_dst.m_Materials[i];

		aiMaterial* m = ai_scene->mMaterials[i];
		for (unsigned int j = 0; j < m->mNumProperties; ++j)
		{
			aiMaterialProperty* prop = m->mProperties[j];
			if (prop->mKey == aiString("?mat.name"))
			{
				mat.m_Name = ((aiString*)prop->mData)->data;
			}
			else if (prop->mKey == aiString("$clr.ambient"))
			{
				mat.m_Ambient = AIToColor(prop);
			}
			else if (prop->mKey == aiString("$clr.diffuse"))
			{
				mat.m_Diffuse = AIToColor(prop);
			}
			else if (prop->mKey == aiString("$clr.specular"))
			{
				mat.m_Specular = AIToColor(prop);
			}
			else if (prop->mKey == aiString("$mat.shininess"))
			{
				mat.m_Shininess = ((float*)prop->mData)[0];
			}
			else if (prop->mKey == aiString("$tex.file"))
			{
				aiString* ai_filepath = (aiString*)prop->mData;
				std::string filepath;
				filepath += dirpath;
				filepath += "\\";
				filepath += ai_filepath->data;
				const std::string name = GetTextureNameFromFilePath(filepath);

				mbuf->InitColorTexture(i, filepath, name, texConf);
			}
		}
	}
}

void AssimpUtil::CreateBoneTree(NodeMap& nodemap, SceneNode* bn)
{
	if (bn == NULL)
		return;

	nodemap.Bones[bn->m_Name] = bn;

	for (SceneNode& cn : bn->m_Children)
	{
		CreateBoneTree(nodemap, &cn);
	}
}

int AssimpUtil::GetNumKeyOfFrame(const aiNodeAnim* ch)
{
	int num_keys = ch->mNumPositionKeys;
	return num_keys;
}

void AssimpUtil::ReadKeys_Raw(aiNodeAnim* ch, geom::SeqTrans& st)
{
	int num_key = AssimpUtil::GetNumKeyOfFrame(ch);

	// ファイルフォーマットの仕様としては, 属性ごとにキー要素数が異なる可能性があるが,
	// 制御が面倒なので一致しているという前提で処理を作っている.
	st.m_Translate.resize(num_key);
	st.m_Scale.resize(num_key);
	st.m_Rotate.resize(num_key);

	for (unsigned int i = 0; i < ch->mNumPositionKeys; ++i)
	{
		ae::ConvertVec(st.m_Translate[i], ch->mPositionKeys[i].mValue);
	}

	for (unsigned int i = 0; i < ch->mNumScalingKeys; ++i)
	{
		ae::ConvertVec(st.m_Scale[i], ch->mScalingKeys[i].mValue);
	}

	for (unsigned int i = 0; i < ch->mNumRotationKeys; ++i)
	{
		ae::AiQuatToLmQuat(st.m_Rotate[i], ch->mRotationKeys[i].mValue);
	}

	st.m_KeyIdxOffset = GetNumMinusKeyFrames(ch);
}

void AssimpUtil::ReadKeys_Time(aiNodeAnim* ch, geom::SeqTrans& st, const aiAnimation* anim)
{
	double FPS = 30.0 / anim->mDuration;
	double FrameStep = anim->mDuration / 30.0;

	lm::range1<double> time_range;
	if (ch->mNumPositionKeys > 0)
	{
		time_range.expand(ch->mPositionKeys[0].mTime);
		time_range.expand(ch->mPositionKeys[ch->mNumPositionKeys - 1].mTime);
	}
	if (ch->mNumScalingKeys > 0)
	{
		time_range.expand(ch->mScalingKeys[0].mTime);
		time_range.expand(ch->mScalingKeys[ch->mNumScalingKeys - 1].mTime);
	}
	if (ch->mNumRotationKeys > 0)
	{
		time_range.expand(ch->mRotationKeys[0].mTime);
		time_range.expand(ch->mRotationKeys[ch->mNumRotationKeys - 1].mTime);
	}

	int NumTimeKeys = RoundF(time_range.length() / FrameStep) + 1;
	double TimeMin = time_range.min_val();

	lm::vec3f default_t;
	if (ch->mNumPositionKeys > 0)
		ae::ConvertVec(default_t, ch->mPositionKeys[ch->mNumPositionKeys - 1].mValue);

	lm::vec3f default_s;
	if (ch->mNumScalingKeys > 0)
		ae::ConvertVec(default_s, ch->mScalingKeys[ch->mNumScalingKeys - 1].mValue);

	lm::quat4f default_r;
	if (ch->mNumRotationKeys > 0)
		ae::AiQuatToLmQuat(default_r, ch->mRotationKeys[ch->mNumRotationKeys - 1].mValue);

	st.m_Translate.resize(NumTimeKeys, default_t);
	st.m_Scale.resize(NumTimeKeys, default_s);
	st.m_Rotate.resize(NumTimeKeys, default_r);

	for (unsigned int i = 0; i < ch->mNumPositionKeys; ++i)
	{
		lm::vec3f t;
		const aiVectorKey& pk = ch->mPositionKeys[i];
		ae::ConvertVec(t, pk.mValue);
		if (i == 0)
		{
			int ft = ToFrame(pk.mTime, TimeMin, FrameStep);
			for (int j = 0; j <= ft; ++j)
			{
				st.m_Translate[j] = t;
			}
		}
		else
		{
			const aiVectorKey& pkl = ch->mPositionKeys[i - 1];
			int tmi0 = ToFrame(pkl.mTime, TimeMin, FrameStep);
			int tmi1 = ToFrame(pk.mTime, TimeMin, FrameStep);

			lm::vec3f tl = st.m_Translate[tmi0];
			for (int j = tmi0 + 1; j <= tmi1; ++j)
			{
				double n = (double)(j - tmi0) / (double)(tmi1 - tmi0);
				st.m_Translate[j] = t * (float)n + tl * (float)(1.0 - n);
			}
		}
	}

	for (unsigned int i = 0; i < ch->mNumScalingKeys; ++i)
	{
		lm::vec3f s;
		const aiVectorKey& sk = ch->mScalingKeys[i];
		ae::ConvertVec(s, sk.mValue);
		if (i == 0)
		{
			int ft = ToFrame(sk.mTime, TimeMin, FrameStep);
			for (int j = 0; j <= ft; ++j)
			{
				st.m_Scale[j] = s;
			}
		}
		else
		{
			const aiVectorKey& skl = ch->mScalingKeys[i - 1];
			int tmi0 = ToFrame(skl.mTime, TimeMin, FrameStep);
			int tmi1 = ToFrame(sk.mTime, TimeMin, FrameStep);

			lm::vec3f sl = st.m_Scale[tmi0];
			for (int j = tmi0 + 1; j <= tmi1; ++j)
			{
				double n = (double)(j - tmi0) / (double)(tmi1 - tmi0);
				float fn = (float)n;
				float frn = (float)(1.0 - n);
				st.m_Scale[j].x = powf(s.x, fn) * powf(sl.x, frn);
				st.m_Scale[j].y = powf(s.y, fn) * powf(sl.y, frn);
				st.m_Scale[j].z = powf(s.z, fn) * powf(sl.z, frn);
			}
		}
	}

	for (unsigned int i = 0; i < ch->mNumRotationKeys; ++i)
	{
		lm::quat4f q;
		const aiQuatKey& rk = ch->mRotationKeys[i];
		ae::AiQuatToLmQuat(q, rk.mValue);
		if (i == 0)
		{
			int ft = ToFrame(rk.mTime, TimeMin, FrameStep);
			for (int j = 0; j <= ft; ++j)
			{
				st.m_Rotate[j] = q;
			}
		}
		else
		{
			const aiQuatKey& rkl = ch->mRotationKeys[i - 1];
			size_t tmi0 = ToFrame(rkl.mTime, TimeMin, FrameStep);
			size_t tmi1 = ToFrame(rk.mTime, TimeMin, FrameStep);

			lm::quat4f ql;
			ae::AiQuatToLmQuat(ql, rkl.mValue);
			for (int j = tmi0 + 1; j <= tmi1; ++j)
			{
				double n = (double)(j - tmi0) / (double)(tmi1 - tmi0);
				st.m_Rotate[j] = lm::quat4f::interpolate(ql, q, (float)n);
			}
		}
	}

	st.m_KeyIdxOffset = GetNumMinusKeyFrames(ch);
}

int AssimpUtil::RoundF(double f)
{
	int i = (int)f;
	double d = f - (double)i;
	if (d >= 0.9)
		return i + 1;
	else
		return i;
}

int AssimpUtil::ToFrame(double time, double TimeMin, double FrameStep)
{
	return RoundF((time - TimeMin) / FrameStep);
}

// 0秒未満のキーフレーム数を数える
int AssimpUtil::GetNumMinusKeyFrames(const aiNodeAnim* ch)
{
	for (unsigned int i = 0; i < ch->mNumPositionKeys; ++i)
	{
		if(ch->mPositionKeys[i].mTime >= 0)
			return i;
	}

	// 全フレームが0秒未満
	assert(false);

	return ch->mNumPositionKeys;
}


namespace ae
{


void ConvertVec(lm::vec3f& dst, const aiVector3D& src)
{
	dst.x = src.x;
	dst.y = src.y;
	dst.z = src.z;
}

void ConvertQuat(lm::quat4f& dst, const aiQuaternion& src)
{
	dst.x = src.x;
	dst.y = src.y;
	dst.z = src.z;
	dst.w = src.w;
}

void ConvertMat(lm::matrix4f& lm, const aiMatrix4x4& am)
{
	lm(0, 0) = am.a1;
	lm(0, 1) = am.a2;
	lm(0, 2) = am.a3;
	lm(0, 3) = am.a4;
	lm(1, 0) = am.b1;
	lm(1, 1) = am.b2;
	lm(1, 2) = am.b3;
	lm(1, 3) = am.b4;
	lm(2, 0) = am.c1;
	lm(2, 1) = am.c2;
	lm(2, 2) = am.c3;
	lm(2, 3) = am.c4;
	lm(3, 0) = am.d1;
	lm(3, 1) = am.d2;
	lm(3, 2) = am.d3;
	lm(3, 3) = am.d4;
	lm.transpose();
}

void AiQuatToLmQuat(lm::quat4f& lq, const aiQuaternion& aq)
{
	lq.x = aq.x;
	lq.y = aq.y;
	lq.z = aq.z;
	lq.w = aq.w;
}


}
