#include <iostream>
#include <Ogre.h>
#include <OgreApplicationContext.h>

class App;
struct Vertex2D {
	float x, y;
	Ogre::ColourValue color;

	Vertex2D(float x, float y, const Ogre::ColourValue &color)
		: x(x)
		, y(y)
		, color(color)
	{
	}
};

static bool calculateBarycentricCoordinates(int px, int py, const Vertex2D &v0,
					    const Vertex2D &v1,
					    const Vertex2D &v2, float &u,
					    float &v, float &w)
{
	float area = 0.5f * ((v1.x - v0.x) * (v2.y - v0.y) -
			     (v2.x - v0.x) * (v1.y - v0.y));
	if (area == 0)
		return false; // Degenerate triangle

	u = 0.5f * ((v1.x - px) * (v2.y - py) - (v2.x - px) * (v1.y - py)) /
	    area;
	v = 0.5f * ((v2.x - px) * (v0.y - py) - (v0.x - px) * (v2.y - py)) /
	    area;
	w = 1.0f - u - v;

	return u >= 0 && v >= 0 && w >= 0;
}

static void drawColoredTriangle(Ogre::Image &image, const Vertex2D &v0,
				const Vertex2D &v1, const Vertex2D &v2)
{
	// Determine bounding box of the triangle
	int minX = std::min({ v0.x, v1.x, v2.x });
	int minY = std::min({ v0.y, v1.y, v2.y });
	int maxX = std::max({ v0.x, v1.x, v2.x });
	int maxY = std::max({ v0.y, v1.y, v2.y });

	// Clamp to image bounds
	minX = std::max(0, minX);
	minY = std::max(0, minY);
	maxX = std::min((int)image.getWidth() - 1, maxX);
	maxY = std::min((int)image.getHeight() - 1, maxY);

	for (int y = minY; y <= maxY; ++y) {
		for (int x = minX; x <= maxX; ++x) {
			float u, v, w;
			if (calculateBarycentricCoordinates(x, y, v0, v1, v2, u,
							    v, w)) {
				// Interpolate colors
				Ogre::ColourValue color = u * v0.color +
							  v * v1.color +
							  w * v2.color;

				image.setColourAt(color, x, y, 0);
			}
		}
	}
}

static void getSubmeshNormals(const Ogre::Mesh *mesh,
			      const Ogre::SubMesh *submesh,
			      std::vector<Ogre::Vector3> &normals)
{
	int j;
	float *pReal;
	int vertex_count = 0;
	if (submesh->useSharedVertices)
		vertex_count += mesh->sharedVertexData->vertexCount;
	else
		vertex_count += submesh->vertexData->vertexCount;
	Ogre::HardwareVertexBufferSharedPtr vbuf;
	Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
						mesh->sharedVertexData :
						submesh->vertexData;
	const Ogre::VertexElement *normalsElem =
		vertex_data->vertexDeclaration->findElementBySemantic(
			Ogre::VES_NORMAL);
	if (!normalsElem)
		return;
	OgreAssert(normals.size() == 0 || normals.size() == vertex_count,
		   "bad vertex count");
	normals.resize(vertex_count);
	vbuf = vertex_data->vertexBufferBinding->getBuffer(
		normalsElem->getSource());
	unsigned char *vertex = static_cast<unsigned char *>(
		vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
	for (j = 0; j < vertex_data->vertexCount;
	     ++j, vertex += vbuf->getVertexSize()) {
		normalsElem->baseVertexPointerToElement(vertex, &pReal);
		normals[j] = Ogre::Vector3(pReal[0], pReal[1], pReal[2]);
	}
	vbuf->unlock();
}
static void getSubmeshUVs(const Ogre::Mesh *mesh, const Ogre::SubMesh *submesh,
			  std::vector<Ogre::Vector2> &uvs, int index)
{
	int j;
	float *pReal;
	uvs.resize(0);
	Ogre::HardwareVertexBufferSharedPtr vbuf;
	Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
						mesh->sharedVertexData :
						submesh->vertexData;
	const Ogre::VertexElement *uvElem =
		vertex_data->vertexDeclaration->findElementBySemantic(
			Ogre::VES_TEXTURE_COORDINATES, index);
	int vertex_count = vertex_data->vertexCount;
	if (!uvElem)
		return;
	OgreAssert(uvs.size() == 0 || uvs.size() == vertex_count,
		   "bad vertex count");
	uvs.resize(vertex_count);
	vbuf = vertex_data->vertexBufferBinding->getBuffer(uvElem->getSource());
	unsigned char *uv = static_cast<unsigned char *>(
		vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
	for (j = 0; j < vertex_data->vertexCount; ++j) {
		uvElem->baseVertexPointerToElement(uv, &pReal);
		uvs[j] = Ogre::Vector2(pReal[0], pReal[1]);
		std::cout << "buffer UV: " << index << ": " << j << " " << uvs[j] << " " << (void *)pReal << " " << (void *)uv << std::endl;
		uv += vbuf->getVertexSize();
	}
	vbuf->unlock();
}
static void getSubmeshVertices(const Ogre::Mesh *mesh,
			       const Ogre::SubMesh *submesh,
			       std::vector<Ogre::Vector3> &vertices)
{
	int j;
	float *pReal;
	int vertex_count = 0;
	if (submesh->useSharedVertices)
		vertex_count += mesh->sharedVertexData->vertexCount;
	else
		vertex_count += submesh->vertexData->vertexCount;
	Ogre::HardwareVertexBufferSharedPtr vbuf;
	Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
						mesh->sharedVertexData :
						submesh->vertexData;
	const Ogre::VertexElement *posElem =
		vertex_data->vertexDeclaration->findElementBySemantic(
			Ogre::VES_POSITION);
	if (!posElem)
		return;
	OgreAssert(vertices.size() == 0 || vertices.size() == vertex_count,
		   "bad vertex count");
	vertices.resize(vertex_count);
	vbuf = vertex_data->vertexBufferBinding->getBuffer(
		posElem->getSource());
	unsigned char *vertex = static_cast<unsigned char *>(
		vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
	for (j = 0; j < vertex_data->vertexCount;
	     ++j, vertex += vbuf->getVertexSize()) {
		posElem->baseVertexPointerToElement(vertex, &pReal);
		vertices[j] = Ogre::Vector3(pReal[0], pReal[1], pReal[2]);
	}
	vbuf->unlock();
}
static void getSubmeshIndices(const Ogre::Mesh *mesh,
			      const Ogre::SubMesh *submesh,
			      std::vector<unsigned long> &indices)
{
	int index_count = 0;
	index_count += submesh->indexData->indexCount;
	int index_offset = 0;
	indices.resize(index_count);
	Ogre::IndexData *index_data = submesh->indexData;
	size_t numTris = index_data->indexCount / 3;
	Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;

	bool use32bitindexes =
		(ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);

	unsigned long *pLong = static_cast<unsigned long *>(
		ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
	unsigned short *pShort = reinterpret_cast<unsigned short *>(pLong);

	size_t offset = 0;

	if (use32bitindexes) {
		for (size_t k = 0; k < numTris * 3; ++k) {
			indices[index_offset++] =
				pLong[k] + static_cast<unsigned long>(offset);
		}
	} else {
		for (size_t k = 0; k < numTris * 3; ++k) {
			indices[index_offset++] =
				static_cast<unsigned long>(pShort[k]) +
				static_cast<unsigned long>(offset);
		}
	}

	ibuf->unlock();
}
struct SubMeshInformation {
	Ogre::String materialName;
	bool sharedVertices;
	std::vector<Ogre::Vector3> vertices;
	std::vector<Ogre::Vector3> normals;
	std::vector<Ogre::Vector2> uvs;
	std::vector<Ogre::Vector2> uv2s;
	std::vector<unsigned long> indices;
	Ogre::SubMesh::LODFaceList lodFaceList;
	Ogre::Mesh::VertexBoneAssignmentList boneList;
	void createSubmesh(Ogre::Mesh *mesh)
	{
		int i;
		Ogre::SubMesh *out = mesh->createSubMesh();
		out->useSharedVertices = false;
		out->vertexData = new Ogre::VertexData();
		size_t currOffset = 0;
		Ogre::VertexDeclaration *vertexDecl =
			out->vertexData->vertexDeclaration;
		vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3,
				       Ogre::VES_POSITION);
		currOffset +=
			Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
		vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3,
				       Ogre::VES_NORMAL);
		currOffset +=
			Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
		vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT2,
				       Ogre::VES_TEXTURE_COORDINATES, 0);
		currOffset +=
			Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
		out->vertexData->vertexCount = vertices.size();
		Ogre::HardwareVertexBufferSharedPtr vbuf =
			Ogre::HardwareBufferManager::getSingleton()
				.createVertexBuffer(
					vertexDecl->getVertexSize(0),
					out->vertexData->vertexCount,
					Ogre::HardwareBuffer::
						HBU_STATIC_WRITE_ONLY, // only GPU side
					false);
		Ogre::VertexBufferBinding *binding =
			out->vertexData->vertexBufferBinding;
		binding->setBinding(0, vbuf);
		float *pvertex = static_cast<float *>(
			vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
		out->indexData->indexCount = indices.size();
		out->indexData->indexBuffer =
			Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(
				vertices.size() < 32768 ?
					Ogre::HardwareIndexBuffer::IT_16BIT :
					Ogre::HardwareIndexBuffer::IT_32BIT,
				out->indexData->indexCount,
				Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,
				false);
		OgreAssert(normals.size() == vertices.size(), "bad normals");
		for (i = 0; i < vertices.size(); i++) {
			*pvertex++ = vertices[i].x;
			*pvertex++ = vertices[i].y;
			*pvertex++ = vertices[i].z;
			if (normals.size() > 0) {
				*pvertex++ = normals[i].x;
				*pvertex++ = normals[i].y;
				*pvertex++ = normals[i].z;
			} else {
				*pvertex++ = 0.0f;
				*pvertex++ = 1.0f;
				*pvertex++ = 0.0f;
			}
			if (uvs.size() > 0) {
				*pvertex++ = uvs[i].x;
				*pvertex++ = uvs[i].y;
			} else {
				*pvertex++ = 0.0f;
				*pvertex++ = 1.0f;
			}
		}
		vbuf->unlock();
		Ogre::HardwareIndexBufferSharedPtr ibuf =
			out->indexData->indexBuffer;
		if (vertices.size() < 32768) {
			unsigned short *pindices =
				static_cast<unsigned short *>(ibuf->lock(
					Ogre::HardwareBuffer::HBL_DISCARD));
			for (i = 0; i < indices.size(); i++)
				*pindices++ = (unsigned short)indices[i];
			ibuf->unlock();
		} else {
			unsigned long *pindices = static_cast<unsigned long *>(
				ibuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
			for (i = 0; i < indices.size(); i++)
				*pindices++ = indices[i];
			ibuf->unlock();
		}
		out->clearBoneAssignments();
		auto vbass_it = boneList.begin();
		while (vbass_it != boneList.end()) {
			out->addBoneAssignment(vbass_it->second);
			vbass_it++;
		}
		out->setMaterialName(materialName);
	}
};

static void getSubMeshInformation(const Ogre::Mesh *mesh,
				  const Ogre::SubMesh *submesh,
				  struct SubMeshInformation *info)
{
	int i;
	info->materialName = submesh->getMaterialName();
	std::cout << "material name: " <<  info->materialName << std::endl;
	info->sharedVertices = submesh->useSharedVertices;
	info->lodFaceList = submesh->mLodFaceList;
	info->boneList = submesh->getBoneAssignments();
	getSubmeshIndices(mesh, submesh, info->indices);
	getSubmeshVertices(mesh, submesh, info->vertices);
	getSubmeshNormals(mesh, submesh, info->normals);
	getSubmeshUVs(mesh, submesh, info->uvs, 0);
	std::cout << "UV0: " << info->uvs.size() << std::endl;
	getSubmeshUVs(mesh, submesh, info->uv2s, 1);
	std::cout << "UV1: " << info->uv2s.size() << std::endl;
	for (i = 0; i < info->uvs.size(); i++)
		std::cout << "UV0: " << i << " " << info->uvs[i] << std::endl;
	for (i = 0; i < info->uv2s.size(); i++)
		std::cout << "UV1: " << i << " " << info->uv2s[i] << std::endl;
}

struct MeshMorphTarget {
	Ogre::SkeletonPtr skelp;
	Ogre::Image image;
	struct DataChange {
		int index;
		int submesh;
		Ogre::Vector3 vertex;
		Ogre::Vector3 normal;
	};
	std::vector<DataChange> vertices;
	float weight;
};
struct MeshInformation {
	std::map<Ogre::String, struct MeshMorphTarget> targets;
	std::vector<struct SubMeshInformation> submesh;
	Ogre::SkeletonPtr skelp;
	Ogre::String skelName;
	MeshInformation(Ogre::Mesh *prototype)
	{
		int i;
		skelName = prototype->getSkeletonName();
		skelp = Ogre::SkeletonManager::getSingleton().getByName(
			prototype->getSkeletonName(), "Characters");
		OgreAssert(skelp, "Could not load skeleton " +
					  prototype->getSkeletonName());
		submesh.resize(prototype->getNumSubMeshes());
		for (i = 0; i < submesh.size(); i++) {
			getSubMeshInformation(prototype,
					      prototype->getSubMesh(i),
					      &submesh[i]);
		}
	}
	Ogre::MeshPtr create(const Ogre::String &meshName)
	{
		int j;
		Ogre::MeshPtr baseMesh =
			Ogre::MeshManager::getSingleton().createManual(
				meshName, Ogre::ResourceGroupManager::
						  DEFAULT_RESOURCE_GROUP_NAME);
		applyMorphTargets();
		for (j = 0; j < submesh.size(); j++)
			submesh[j].createSubmesh(baseMesh.get());
		//define a extreme boundary values
		Ogre::Real max_x = -1e+8;
		Ogre::Real min_x = 1e+8;
		Ogre::Real max_y = -1e+8;
		Ogre::Real min_y = 1e+8;
		Ogre::Real max_z = -1e+8;
		Ogre::Real min_z = +1e+8;
		// Setting bounding box
		Ogre::Mesh::SubMeshList mlist = baseMesh->getSubMeshes();
		for (j = 0; j < mlist.size(); j++) {
			Ogre::SubMesh *in = mlist[j];
			Ogre::VertexData *vertex_data = in->vertexData;
			const Ogre::VertexElement *posElem =
				vertex_data->vertexDeclaration
					->findElementBySemantic(
						Ogre::VES_POSITION);
			Ogre::HardwareVertexBufferSharedPtr hwvb =
				in->vertexData->vertexBufferBinding->getBuffer(
					posElem->getSource());
			unsigned char *hbuff =
				static_cast<unsigned char *>(hwvb->lock(
					Ogre::HardwareBuffer::HBL_READ_ONLY));
			Ogre::Real *pValue;
			Ogre::Real value;

			for (size_t idx = 0; idx < vertex_data->vertexCount;
			     ++idx, hbuff += hwvb->getVertexSize()) {
				posElem->baseVertexPointerToElement(hbuff,
								    &pValue);
				value = (*pValue++);
				if (value > max_x)
					max_x = value;
				if (value < min_x)
					min_x = value;
				value = (*pValue++);

				if (value > max_y)
					max_y = value;
				if (value < min_y)
					min_y = value;
				value = (*pValue++);

				if (value > max_z)
					max_z = value;
				if (value < min_z)
					min_z = value;
			}
			hwvb->unlock();
		}
		baseMesh->setSkeletonName(skelName);
		baseMesh->_setBounds(Ogre::AxisAlignedBox(min_x, min_y, min_z,
							  max_x, max_y, max_z));
		return baseMesh;
	}
	std::pair<int, int> findVertex(const Ogre::Vector2 &uv)
	{
		int i, j;
		float d = 1.5f;
		int ret_submesh = -1, ret_index = -1;
		for (i = 0; i < submesh.size(); i++) {
			if (submesh[i].uv2s.size() != submesh[i].normals.size())
				continue;
			for (j = 0; j < submesh[i].uv2s.size(); j++) {
				float l =
					submesh[i].uv2s[j].squaredDistance(uv);
				if (l < d) {
					d = l;
					ret_submesh = i;
					ret_index = j;
				}
			}
		}
		OgreAssert(ret_submesh >= 0 && ret_index >= 0,
			   "Vertex not found");
		return { ret_submesh, ret_index };
	}
	void createTarget(const Ogre::String &targetName,
			  const Ogre::Mesh *target)
	{
		MeshMorphTarget mtarget;
		int i, j, k, l;
		std::unordered_map<int, SubMeshInformation> targetInfo;
		mtarget.skelp = Ogre::SkeletonManager::getSingleton().getByName(
			target->getSkeletonName(), "Characters");
		int count = 0;
		for (i = 0; i < target->getNumSubMeshes(); i++) {
			SubMeshInformation info;
			getSubMeshInformation(target, target->getSubMesh(i),
					      &info);
			std::cout << "-- processed submesh: " << i << std::endl;
			std::cout << "-- processed submesh: "
				  << info.materialName << std::endl;
			if (info.uv2s.size() == 0)
				continue;
			targetInfo[i] = info;
			count++;
		}
		OgreAssert(count > 0, "Bad target mesh count");
		struct VertexIndex {
			int base_submesh;
			int base_index;
		};
		std::vector<VertexIndex> indices;
		Ogre::Image image(Ogre::PF_R8G8B8A8, 512, 512);
		Ogre::Image image_normals(Ogre::PF_R8G8B8A8, 512, 512);
		float vertexMul = 0.0f, normalMul = 0.0f;
		struct DeltaData {
			std::vector<Ogre::Vector3> vertexDeltas;
			std::vector<Ogre::Vector3> normalDeltas;
			std::vector<Ogre::Vector2> uvs;
			std::vector<unsigned long> indices;
		};
		std::vector<DeltaData> deltas;
		std::cout << "target start " << targetName << "\n";
		deltas.reserve(targetInfo.size());
		for (i = 0; i < targetInfo.size(); i++) {
			if (targetInfo.find(i) == targetInfo.end())
				continue;
			std::cout << "submesh: " << i << std::endl;
			OgreAssert(targetInfo[i].uv2s.size() == targetInfo[i].normals.size(), "bad UV2 count");
			OgreAssert(targetInfo[i].vertices.size() > 0, "bad vertex count");
			struct DeltaData dd;
			dd.vertexDeltas.resize(targetInfo[i].vertices.size());
			dd.normalDeltas.resize(targetInfo[i].vertices.size());
			dd.uvs.resize(targetInfo[i].vertices.size());
			std::cout
				<< "\tvertices: " << targetInfo[i].vertices.size()
				<< std::endl;
			for (j = 0; j < targetInfo[i].vertices.size(); j++) {
				const Ogre::Vector2 &target_uv =
					targetInfo[i].uv2s[j];
				auto ti = findVertex(target_uv);
				const Ogre::Vector3 &base_v =
					submesh[ti.first].vertices[ti.second];
				const Ogre::Vector3 &target_v =
					targetInfo[i].vertices[j];
				const Ogre::Vector3 &base_n =
					submesh[ti.first].normals[ti.second];
				const Ogre::Vector3 &target_n =
					targetInfo[i].normals[j];
				Ogre::Vector3 vertexDelta = target_v - base_v;
				Ogre::Vector3 normalDelta = target_n - base_n;
				dd.vertexDeltas[j] = vertexDelta;
				dd.normalDeltas[j] = normalDelta;
				dd.uvs[j] = target_uv;
				std::cout << "UV: " << j << " " << target_uv << " " << ti.second << std::endl;
				float lv = vertexDelta.length();
				if (vertexMul < lv)
					vertexMul = lv;
				float ln = normalDelta.length();
				if (normalMul < ln)
					normalMul = ln;
			}

			dd.indices = targetInfo[i].indices;
			std::cout << "indices: " << dd.indices.size() << std::endl;
			deltas.push_back(dd);
		}
		std::cout << "multipliers: " << vertexMul << " " << normalMul
			  << "\n";
		OgreAssert(vertexMul > 0.0f && normalMul > 0.0f,
			   "bad multipliers");
		//		normalMul = 1.0f;
		//		vertexMul = 1.0f;
		for (i = 0; i < deltas.size(); i++) {
			OgreAssert(deltas[i].normalDeltas.size() == deltas[i].vertexDeltas.size(), "bad delta count");
			for (j = 0; j < deltas[i].vertexDeltas.size(); j++) {
				Ogre::Vector3 d =
					deltas[i].normalDeltas[j] / normalMul *
						0.5f +
					Ogre::Vector3(0.5f, 0.5f, 0.5f);
				d.x = Ogre::Math::Clamp(d.x, 0.0f, 1.0f);
				d.y = Ogre::Math::Clamp(d.y, 0.0f, 1.0f);
				d.z = Ogre::Math::Clamp(d.z, 0.0f, 1.0f);
				deltas[i].normalDeltas[j] = d;
				d =
					deltas[i].vertexDeltas[j] / vertexMul *
						0.5f +
					Ogre::Vector3(0.5f, 0.5f, 0.5f);
				d.x = Ogre::Math::Clamp(d.x, 0.0f, 1.0f);
				d.y = Ogre::Math::Clamp(d.y, 0.0f, 1.0f);
				d.z = Ogre::Math::Clamp(d.z, 0.0f, 1.0f);
				deltas[i].vertexDeltas[j] = d;
				std::cout << " vertex delta: " << deltas[i].vertexDeltas[j];
				std::cout << " normal delta: " << deltas[i].normalDeltas[j];
				std::cout << std::endl;
			}
		}
		image.setTo(Ogre::ColourValue(0.5, 0.5, 0.5, 1));
		image_normals.setTo(Ogre::ColourValue(0.5, 0.5, 0.5, 1));
		for (i = 0; i < deltas.size(); i++) {
			for (j = 0; j < deltas[i].indices.size(); j += 3) {
				int i1 = (int)deltas[i].indices[j];
				int i2 = (int)deltas[i].indices[j + 1];
				int i3 = (int)deltas[i].indices[j + 2];
				Ogre::Vector2 u0 = deltas[i].uvs[i1];
				Ogre::Vector2 u1 = deltas[i].uvs[i2];
				Ogre::Vector2 u2 = deltas[i].uvs[i3];
				Ogre::ColourValue c0(
					deltas[i].vertexDeltas[i1].x,
					deltas[i].vertexDeltas[i1].y,
					deltas[i].vertexDeltas[i1].z, 1.0f);
				Ogre::ColourValue c1(
					deltas[i].vertexDeltas[i2].x,
					deltas[i].vertexDeltas[i2].y,
					deltas[i].vertexDeltas[i2].z, 1.0f);
				Ogre::ColourValue c2(
					deltas[i].vertexDeltas[i3].x,
					deltas[i].vertexDeltas[i3].y,
					deltas[i].vertexDeltas[i3].z, 1.0f);
				Ogre::ColourValue x0(
					deltas[i].normalDeltas[i1].x,
					deltas[i].normalDeltas[i1].y,
					deltas[i].normalDeltas[i1].z, 1.0f);
				Ogre::ColourValue x1(
					deltas[i].normalDeltas[i2].x,
					deltas[i].normalDeltas[i2].y,
					deltas[i].normalDeltas[i2].z, 1.0f);
				Ogre::ColourValue x2(
					deltas[i].normalDeltas[i3].x,
					deltas[i].normalDeltas[i3].y,
					deltas[i].normalDeltas[i3].z, 1.0f);
				Vertex2D v0(u0.x * 512, u0.y * 512, c0);
				Vertex2D v1(u1.x * 512, u1.y * 512, c1);
				Vertex2D v2(u2.x * 512, u2.y * 512, c2);
				Vertex2D xv0(u0.x * 512, u0.y * 512, x0);
				Vertex2D xv1(u1.x * 512, u1.y * 512, x1);
				Vertex2D xv2(u2.x * 512, u2.y * 512, x2);
				std::cout << "triangle: ";
				std::cout << i1 << " " << i2 << " " << i3 << " ";
				std::cout << u0 << " " << u1 << " " << u2 << " ";
				std::cout << "(" << v0.x <<  ", " << v0.y << ") ";
				std::cout << "(" << v1.x <<  ", " << v1.y << ") ";
				std::cout << "(" << v2.x <<  ", " << v2.y << ") ";
				std::cout << std::endl;
				// SoftwareRenderer2D::drawTriangle(image, v0, v1, v2);
				drawColoredTriangle(image, v0, v1, v2);
				drawColoredTriangle(image_normals, xv0, xv1,
						    xv2);
			}
		}
		std::cout << "output image\n";
		image.save(targetName + "_debug.png");
		image_normals.save(targetName + "_normals_debug.png");
		std::vector<Ogre::Vector3> vertices;
		std::vector<Ogre::Vector3> normals;
		std::vector<Ogre::Vector2> uvs;
		std::vector<int> uv_submeshes;
		std::vector<int> uv_indices;
		std::vector<Ogre::Vector2> change_uvs;
		std::vector<MeshMorphTarget::DataChange> changes;
		for (i = 0; i < targetInfo.size(); i++) {
			if (targetInfo[i].uv2s.size() !=
			    targetInfo[i].normals.size())
				continue;
			OgreAssert(targetInfo[i].normals.size() ==
					   targetInfo[i].uv2s.size(),
				   "UV2 missing");
			const std::vector<Ogre::Vector2> &puv =
				targetInfo[i].uv2s;
			const std::vector<Ogre::Vector3> &pvertices =
				targetInfo[i].vertices;
			const std::vector<Ogre::Vector3> &pnormals =
				targetInfo[i].normals;
			for (j = 0; j < pvertices.size(); j++) {
				MeshMorphTarget::DataChange dc;
				dc.index = -1;
				dc.submesh = -1;
				change_uvs.push_back(puv[j]);
				dc.normal = pnormals[j];
				dc.vertex = pvertices[j];
				changes.push_back(dc);
			}
		}
		for (i = 0; i < submesh.size(); i++) {
			int submesh_id = i;
			if (submesh[i].uv2s.size() != submesh[i].normals.size())
				continue;
			for (j = 0; j < submesh[i].vertices.size(); j++) {
				int index_id = j;
				const Ogre::Vector2 &xuv = submesh[i].uv2s[j];
				uvs.push_back(xuv);
				uv_submeshes.push_back(submesh_id);
				uv_indices.push_back(index_id);
			}
		}
		for (i = 0; i < changes.size(); i++) {
			float l = 1.0f;
			int sub = -1, index = -1;
			for (j = 0; j < uvs.size(); j++) {
				float xl =
					change_uvs[i].squaredDistance(uvs[j]);
				if (xl < l) {
					l = xl;
					sub = uv_submeshes[j];
					index = uv_indices[j];
				}
			}
			OgreAssert(sub >= 0 && index >= 0, "Indexing error");
			if (l < 0.000000005f) {
				changes[i].submesh = sub;
				changes[i].index = index;
			}
		}
		mtarget.vertices = changes;

		mtarget.weight = 0.0f;
		targets[targetName] = mtarget;
	}
	void setTargetWeight(const Ogre::String &targetName, float weight)
	{
		if (targets.find(targetName) == targets.end())
			return;
		targets[targetName].weight = weight;
	}
	void applyMorphTargets()
	{
		auto it = targets.begin();
		float sum = 0.0f;
		while (it != targets.end()) {
			sum += it->second.weight;
			it++;
		}
		if (sum > 1.0f) {
			it = targets.begin();
			while (it != targets.end()) {
				it->second.weight = it->second.weight / sum;
				it++;
			}
		}
		it = targets.begin();
		while (it != targets.end()) {
			if (it->second.weight > 0.001f) {
				int i;
				for (i = 0; i < it->second.vertices.size();
				     i++) {
					int sub_id =
						it->second.vertices[i].submesh;
					int index_id =
						it->second.vertices[i].index;
					if (sub_id < 0 || index_id < 0)
						continue;
					Ogre::Vector3 orig_vertex =
						submesh[sub_id]
							.vertices[index_id];
					Ogre::Vector3 new_vertex =
						it->second.vertices[i].vertex;
					Ogre::Vector3 vertex_update =
						Ogre::Math::lerp(
							orig_vertex, new_vertex,
							it->second.weight);
					Ogre::Vector3 orig_normal =
						submesh[sub_id]
							.normals[index_id];
					Ogre::Vector3 new_normal =
						it->second.vertices[i].normal;
					Ogre::Vector3 normal_update =
						Ogre::Math::lerp(
							orig_normal, new_normal,
							it->second.weight);
					submesh[sub_id].vertices[index_id] =
						vertex_update;
					submesh[sub_id].normals[index_id] =
						normal_update;
				}
			}
			it++;
		}
	}
};

struct SimpleListener : public OgreBites::InputListener {
	App *mApp;
	SimpleListener(App *app)
		: mApp(app)
	{
	}
	void frameRendered(const Ogre::FrameEvent &evt) override;
};

class App : public OgreBites::ApplicationContext {
public:
	App()
		: OgreBites::ApplicationContext("MorphTargetsResearch")
	{
	}
	void locateResources() override
	{
		Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
			"Characters", true);
		Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
			"./characters", "FileSystem", "Characters", true, true);
		Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
			"../characters", "FileSystem", "Characters", true,
			true);
		OgreBites::ApplicationContext::locateResources();
	}
	void loadResources() override
	{
	}
	Ogre::MeshPtr loadMesh(Ogre::String meshName)
	{
		Ogre::MeshPtr ret = Ogre::MeshManager::getSingleton().load(
			meshName, "Characters");
		return ret;
	}
	void meshProcessing()
	{
		int i;
		//code to test and create entity based off of sub-meshes

		//vector of meshes
		std::vector<Ogre::MeshPtr> m_Meshes;

		//define meshes
		//		Ogre::MeshPtr base = Ogre::MeshManager::getSingleton().load(
		//			"male/normal-male.glb", "Characters");
		Ogre::MeshPtr base = loadMesh("shapes/male/edited-normal-male-base.glb");
		m_Meshes.push_back(base);
		Ogre::MeshPtr shape = loadMesh("shapes/male/edited-shape-test-male.glb");
		OgreAssert(shape, "no shape");
		MeshInformation meshinfo(base.get());
		std::string m_ModelId = "MergedMesh";
		meshinfo.createTarget("shape", shape.get());
		meshinfo.setTargetWeight("shape", 1.0f);
		Ogre::MeshPtr m_BaseMesh = meshinfo.create(m_ModelId);
		Ogre::Entity *ent =
			mScnMgr->createEntity("entName", m_BaseMesh->getName());
		//		ent->setMaterialName("TestMat");

		//code here is temporary and only ment as a test for the modelers
		//Entity* testEntity = mSceneMgr->createEntity("cc", source->getName());
		//testEntity->setMaterialName("TestMat");
		Ogre::SceneNode *thisSceneNode =
			mScnMgr->getRootSceneNode()->createChildSceneNode();
		//		thisSceneNode->setPosition(100, 100, 100);
		thisSceneNode->attachObject(ent);
#if 0
		mIdle = ent->getAnimationState("idle");
		mIdle->setEnabled(true);
		mIdle->setLoop(true);
		mIdle->setWeight(1.0f);
#endif
	}

	void setup() override
	{
		OgreBites::ApplicationContext::setup();
		Ogre::Root *root = getRoot();
		Ogre::SceneManager *scnMgr = root->createSceneManager();
		Ogre::RTShader::ShaderGenerator *shadergen =
			Ogre::RTShader::ShaderGenerator::getSingletonPtr();
		shadergen->addSceneManager(scnMgr);
		// also need to tell where we are
		mScnMgr.reset(scnMgr);
		mCameraNode.reset(
			mScnMgr->getRootSceneNode()->createChildSceneNode());
		mCameraNode->setPosition(0, 2, 3);
		mCameraNode->lookAt(Ogre::Vector3(0, 1, -1),
				    Ogre::Node::TS_PARENT);

		Ogre::Light *light = mScnMgr->createLight("MainLight");
		Ogre::SceneNode *lightNode =
			mScnMgr->getRootSceneNode()->createChildSceneNode();
		//    lightNode->setPosition(0, 10, 15);
		lightNode->setDirection(
			Ogre::Vector3(0.55, -0.3, 0.75).normalisedCopy());
		lightNode->attachObject(light);
		light->setType(Ogre::Light::LT_DIRECTIONAL);
		light->setDiffuseColour(Ogre::ColourValue::White);
		light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));

		mScnMgr->setAmbientLight(
			Ogre::ColourValue(0.5f, 0.5f, 0.5f, 1.0f));

		// create the camera
		Ogre::Camera *cam = mScnMgr->createCamera("normal_camera");
		cam->setNearClipDistance(0.1f); // specific to this sample
		cam->setAutoAspectRatio(true);
		mCameraNode->attachObject(cam);
		mCamera.reset(cam);

		// and tell it to render into the main window
		getRenderWindow()->addViewport(cam);
		meshProcessing();
#if 0
		Ogre::Entity *ent_base =
			mScnMgr->createEntity("male/normal-male.glb");
		Ogre::Entity *ent_shape = mScnMgr->createEntity(
			"shapes/male/chibi/vroid-normal-male-chibi.glb");
		Ogre::SceneNode *characterNode =
			mScnMgr->getRootSceneNode()->createChildSceneNode("ch");
		characterNode->attachObject(ent_base);
#endif
		addInputListener(OGRE_NEW SimpleListener(this));
		Ogre::Image image(Ogre::PF_R8G8B8A8, 1024, 1024);
		image.setTo(Ogre::ColourValue(0, 0, 0, 1));
		Vertex2D v0(0.0f, 0.0f, Ogre::ColourValue(1, 0, 0, 1));
		Vertex2D v1(500.0f, 100.0f, Ogre::ColourValue(0, 1, 0, 1));
		Vertex2D v2(0.0f, 500.0f, Ogre::ColourValue(0, 0, 1, 1));
		// SoftwareRenderer2D::drawTriangle(image, v0, v1, v2);
		drawColoredTriangle(image, v0, v1, v2);
		image.save("checkup.png");
	}
	void animationTime(float time)
	{
		mIdle->addTime(time);
	}

private:
	std::unique_ptr<Ogre::SceneNode> mCameraNode;
	std::unique_ptr<Ogre::Camera> mCamera;
	std::unique_ptr<Ogre::SceneManager> mScnMgr;
	Ogre::AnimationState *mIdle;
};

void SimpleListener::frameRendered(const Ogre::FrameEvent &evt)
{
	mApp->animationTime(evt.timeSinceLastFrame);
}

int main(int argc, char *argv[])
{
	App ctx;
	ctx.initApp();
	ctx.setWindowGrab(true);

	ctx.getRoot()->startRendering();
	ctx.setWindowGrab(false);
	ctx.closeApp();
	return 0;
}
