#ifndef _MODELCLASS_H_
#define _MODELCLASS_H_


//////////////
// INCLUDES //
//////////////
#include "Logger.h"
#include "macro.h"

#include <d3d11.h>
#include <directxmath.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <WICTextureLoader.h>
using namespace DirectX;
using namespace std;

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "texture_class.h"

enum class TextureType
{
	Diffuse,
	Normal,
	Specular,
	Alpha
};

struct TextureContainer
{

	// Textures
	std::vector<ID3D11ShaderResourceView*> diffuse;
	std::vector<ID3D11ShaderResourceView*> normal;
	std::vector<ID3D11ShaderResourceView*> specular;
	std::vector<ID3D11ShaderResourceView*> alpha;

	// Textures Paths
	std::vector<std::wstring> diffusePaths;
	std::vector<std::wstring> normalPaths;
	std::vector<std::wstring> specularPaths;
	std::vector<std::wstring> alphaPaths;


	// Get the vector of textures_ based on the type
	std::vector<ID3D11ShaderResourceView*>& Get(TextureType type) const {
		switch (type) {
		case TextureType::Diffuse:  return const_cast<std::vector<ID3D11ShaderResourceView*>&>(diffuse);
		case TextureType::Normal:   return const_cast<std::vector<ID3D11ShaderResourceView*>&>(normal);
		case TextureType::Specular: return const_cast<std::vector<ID3D11ShaderResourceView*>&>(specular);
		case TextureType::Alpha:    return const_cast<std::vector<ID3D11ShaderResourceView*>&>(alpha);
		default: return const_cast<std::vector<ID3D11ShaderResourceView*>&>(diffuse);
		}
	}

	// Get the vector of textures_ paths based on the type
	std::vector<std::wstring> GetPaths(TextureType type) const {
		switch (type)
		{
			case TextureType::Diffuse: return std::vector<std::wstring>(diffusePaths);
			case TextureType::Normal: return std::vector<std::wstring>(normalPaths);
			case TextureType::Specular: return std::vector<std::wstring>(specularPaths);
			case TextureType::Alpha: return std::vector<std::wstring>(alphaPaths);
			default: return std::vector<std::wstring>(diffusePaths);
		}
	}


	// Get The texture based on the type and index
	ID3D11ShaderResourceView* GetTexture(TextureType type, int index) const {
		auto& vec = Get(type);
		if (index >= 0 && index < vec.size())
			return vec[index];
		return nullptr;
	}

	// Get The texture path based on the type and index
	std::wstring GetTexturePath(TextureType type, int index) const
	{
		std::vector<std::wstring> path = GetPaths(type);
		if (index >= 0 && index < path.size())
			return path[index];
		return L"";
	}

	// Release all textures_ and textures_ paths

	void ReleaseAll() {
		ReleaseVector(diffuse);
		ReleaseVector(normal);
		ReleaseVector(specular);
		ReleaseVector(alpha);

		ReleaseVector(diffusePaths);
		ReleaseVector(normalPaths);
		ReleaseVector(specularPaths);
		ReleaseVector(alphaPaths);
	}

	// Assign a texture and its path to the appropriate vector based on the index
	void AssignTexture(TextureContainer& textContainer, ID3D11ShaderResourceView* texture , const std::wstring paths, int index)
	{
		switch (index)
		{
		case 0:
			textContainer.diffuse.push_back(texture);
			textContainer.diffusePaths.push_back(paths);
			break;
		case 1:
			textContainer.normal.push_back(texture);
			textContainer.normalPaths.push_back(paths);
			break;
		case 2:
			textContainer.specular.push_back(texture);
			textContainer.specularPaths.push_back(paths);
			break;
		case 3:
			textContainer.alpha.push_back(texture);
			textContainer.alphaPaths.push_back(paths);
			break;
		default:
			textContainer.diffuse.push_back(texture);
			textContainer.diffusePaths.push_back(paths);
			break;
		}
	}

private:
	void ReleaseVector(std::vector<ID3D11ShaderResourceView*>& vec) {
		for (auto& tex : vec) {
			if (tex) {
				tex->Release();
				tex = nullptr;
			}
		}
		vec.clear();
	}

	void ReleaseVector(std::vector<std::wstring>& vec) {
		vec.clear();
	}
};
////////////////////////////////////////////////////////////////////////////////
// Class name: model_class
////////////////////////////////////////////////////////////////////////////////
class model_class
{
protected:

	struct VertexType
	{
		XMFLOAT3 position;
		XMFLOAT2 texture;
		XMFLOAT3 normal;
		XMFLOAT3 tangent;
		XMFLOAT3 binormal;
	};

	struct ModelType
	{
		float x, y, z;
		float tu, tv;
		float nx, ny, nz;
		float tx, ty, tz;
		float bx, by, bz;
	};

	struct Vertex {
		float x, y, z;
	};

	struct Texture {
		float u, v;
	};

	struct Normal {
		float nx, ny, nz;
	};

	struct TempVertexType
	{
		float x, y, z;
		float tu, tv;
		float nx, ny, nz;
	};

	struct VectorType
	{
		float x, y, z;
	};

	struct Face {
		int v1, v2, v3;
		int t1, t2, t3;
		int n1, n2, n3;
	};

public:
	model_class();
	model_class(const model_class&) = delete;
	model_class& operator=(const model_class&) = delete;
	~model_class();

	//bool initialize(ID3D11Device*, ID3D11DeviceContext*, char*, std::vector<ID3D11ShaderResourceView*>);
    // Nouvelle surcharge avec TextureContainer
	bool Initialize(ID3D11Device*, ID3D11DeviceContext*, char*, const TextureContainer&);
	// Nouvelle méthode - initialisation sans textures_
	bool Initialize(ID3D11Device*, ID3D11DeviceContext*, char*);
	void Shutdown();
	void Render(ID3D11DeviceContext*);

	int GetIndexCount();
	int GetVertexCount() const { return m_vertexCount; }

	// TEXTURE //
	
	//ID3D11ShaderResourceView* get_texture(int index) const;
	ID3D11ShaderResourceView* GetTexture(TextureType type, int index) const;
	
	//bool ChangeTexture(ID3D11Device* device, ID3D11DeviceContext* deviceContext, std::wstring filename, int index);
	bool ChangeTexture(ID3D11Device* device, ID3D11DeviceContext* deviceContext,std::wstring filename, TextureType type, int index);
    
	// Méthodes pour ajouter une nouvelle texture
	bool AddTexture(ID3D11Device* device, ID3D11DeviceContext* deviceContext,std::wstring filename, TextureType type);
	bool AddTexture(ID3D11ShaderResourceView* texture, TextureType type);

	void SetTextureContainer (TextureContainer& texturesContainer) { m_Textures = texturesContainer; }
	TextureContainer GetTextureContainer() const { return m_Textures; }

	bool PreloadTextures(ID3D11Device* device, ID3D11DeviceContext* deviceContext, TextureContainer& textureContainer);
	
protected:
	int m_vertexCount, m_indexCount;
	ID3D11Buffer* m_vertexBuffer, * m_indexBuffer;
	
private:
	bool InitializeBuffers(ID3D11Device*);
	void ShutdownBuffers();
	void RenderBuffers(ID3D11DeviceContext*);
	void ReleaseTextures();

	bool LoadModel(char*);
	bool LoadObjModel(char*);
	bool LoadTxtModel(char*);
	void ReleaseModel();

	void CalculateModelVectors();
	void CalculateTangentBinormal(TempVertexType, TempVertexType, TempVertexType, VectorType&, VectorType&);
	
	TextureContainer m_Textures;
	ModelType* m_model;
};

#endif