#include <assert.h>
#include <vector>
#include <string>
#include "ifilesystem.h"
#include "log.h"
#include "render.h"
#include "gpu_buffer.h"
#include "shader.h"
#include "shadersystem.h"
#include "renderdevice.h"
#include "model.h"
#include "modelsystem.h"
#include "texturesmanager.h"

ModelSystem* g_modelSystem = nullptr;

static InputLayoutDesc_t g_staticVertexLayout[] = {
	{ VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION },
	{ VERTEXATTR_VEC3, SHADERSEMANTIC_NORMAL },
	{ VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD },
};

static InputLayoutDesc_t g_skinnedVertexLayout[] = {
	{ VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION },
	{ VERTEXATTR_VEC3, SHADERSEMANTIC_NORMAL },
	{ VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD },
	{ VERTEXATTR_VEC4, SHADERSEMANTIC_BONEIDS },
	{ VERTEXATTR_VEC4, SHADERSEMANTIC_WEIGHTS },
};

Shader* g_unlitShader = nullptr;
Shader* g_litShader = nullptr;
Shader* g_unlitSkinnedShader = nullptr;
Shader* g_litSkinnedShader = nullptr;

static std::string getFileNameWithoutExtension(const std::string& filename)
{
	size_t lastindex = filename.find_last_of(".");
	if (lastindex != std::string::npos) {
		return filename.substr(0, lastindex);
	}

	return filename;
}

ModelSystem::ModelSystem()
{
}

ModelSystem::~ModelSystem()
{
}

void ModelSystem::Init()
{
	// Load unlighted model generic shader
	g_unlitShader = g_shaderSystem->CreateShader("unlit_generic", "data/shaders/unlit_generic.vs", "data/shaders/unlit_generic.ps",
		g_staticVertexLayout, sizeof(g_staticVertexLayout) / sizeof(g_staticVertexLayout[0]));

	// Load lighted model generic shader
	g_litShader = g_shaderSystem->CreateShader("lit_generic", "data/shaders/lit_generic.vs", "data/shaders/lit_generic.ps",
		g_staticVertexLayout, sizeof(g_staticVertexLayout) / sizeof(g_staticVertexLayout[0]));

	// Load unlighted skinned model generic shader
	g_unlitSkinnedShader = g_shaderSystem->CreateShader("unlit_generic_skin", "data/shaders/unlit_generic_skin.vs", "data/shaders/unlit_generic.ps",
		g_skinnedVertexLayout, sizeof(g_skinnedVertexLayout) / sizeof(g_skinnedVertexLayout[0]));


	// Load lighted skinned model generic shader
	g_litSkinnedShader = g_shaderSystem->CreateShader("lit_generic_skin", "data/shaders/lit_generic_skin.vs", "data/shaders/lit_generic.ps",
		g_skinnedVertexLayout, sizeof(g_skinnedVertexLayout) / sizeof(g_skinnedVertexLayout[0]));
}

void ModelSystem::Shutdown()
{
	for (int i = 0; i < m_models.size(); i++)
	{
		if (m_models[i].model)
		{
			delete m_models[i].model;
			m_models[i].model = nullptr;
		}
	}

	m_models.clear();
}

Model* ModelSystem::LoadModel(const char* filename)
{
	auto it = std::find_if(m_models.begin(), m_models.end(), [=](const ModelEntry& entry) { return strcmp(entry.filename, filename) == 0; });
	if (it != m_models.end())
	{
		return it->model;
	}

	if (!GetFileSystem()->IsExist(filename))
	{
		Msg("ModelSystem::LoadModel: File '%s' not exist.", filename);
		return nullptr;
	}

	Model* model = new Model();

	if (strstr(filename, ".obj"))
	{
		model->LoadObj(filename);
	}
	else if (strstr(filename, ".iqm"))
	{
		model->LoadIqm(filename);
	}
	else
	{
		// Find file extension
		size_t stringLength = strlen(filename);

		for (int i = stringLength; i > 0; --i)
		{
			if (filename[i] == '.')
			{
				stringLength = i;
				break;
			}
		}

		const char* fileExtension = &filename[stringLength];

		Msg("ModelSystem::LoadModel: Unknowed file format '%s'", filename, fileExtension);

		return nullptr;
	}

	ModelEntry entry = {};
	strcpy(entry.filename, filename);
	entry.model = model;

	m_models.push_back(entry);

	Msg("Loaded Model '%s'", filename);

	return model;
}
