////////////////////////////////////////////////////////
// filename: ChaDataObject.cpp
// author: Chafumi Touji
// version: 1.0.0
// date: 2020/03/05
////////////////////////////////////////////////////////

#include "defcha3dcore.h"

////////////////////////////////////////////////////////////

CVertexData::CVertexData(){

	memset(ID, 0, SIZE_CHAR_MAX);
	Data = NULL;
}

CVertexData::~CVertexData(){

	delete Data;
	Data = NULL;
}

////////////////////////////////////////////////////////////

CPolygonData::CPolygonData(){

	memset(VertexID, 0, SIZE_CHAR_MAX);
	memset(NormalID, 0, SIZE_CHAR_MAX);
	memset(TexID, 0, SIZE_CHAR_MAX);
	memset(ColorID, 0, SIZE_CHAR_MAX);
	memset(MaterialID, 0, SIZE_CHAR_MAX);
	VertexIndex = new CVector(sizeof(int));
	NormalIndex = new CVector(sizeof(int));
	TexIndex = new CVector(sizeof(int));
	ColorIndex = new CVector(sizeof(int));
}

CPolygonData::~CPolygonData(){

	delete VertexIndex;
	delete NormalIndex;
	delete TexIndex;
	delete ColorIndex;

}

///////////////////////////////////////////////////////////////

CGeometry::CGeometry(){

	VertexCount = 0;
	Vertics = new CVector(sizeof(CVertexData));
	PolygonData = new CVector(sizeof(CPolygonData));
}

CGeometry::~CGeometry(){

	for (int i = 0; i <= Vertics->PushPos - 1; i++){
		CVertexData *data = (CVertexData *) Vertics->GetData(i);
		delete data->Data;
		delete data;
	}
	for (int i = 0; i <= PolygonData->PushPos - 1; i++){
		CPolygonData *data = (CPolygonData *)PolygonData->GetData(i);
		delete data->VertexIndex;
		delete data->TexIndex;
		delete data->ColorIndex;
		delete data->NormalIndex;
		delete data;
	}
}

CVertexData *CGeometry::GetVertexData(char *id){

	for (int i = 0; i <= Vertics->PushPos - 1; i++){
		if (strcmp(((CVertexData *)Vertics->GetData(i))->ID, id) == 0)
			return (CVertexData *) Vertics->GetData(i);
	}

	return NULL;
}

///////////////////////////////////////////////////////////////


CChaDataObject::CChaDataObject(){

	Geometry = new CVector( sizeof( CGeometry ));
	Material = new CVector(sizeof(CMaterial));
	TextureName = new CVector(sizeof( CTextureName ));
}

CChaDataObject::~CChaDataObject(){

	for (int i = 0; i <= Geometry->PushPos - 1; i++){
		CGeometry *data = (CGeometry *)Geometry->GetData(i);
		delete data->Vertics;
		delete data->PolygonData;
	}

	delete Geometry;
	delete Material;
	delete TextureName;
}

CMaterial *CChaDataObject::GetMaterial(char *id){

	for (int i = 0; i <= Material->PushPos - 1; i++){
		if (strcmp(((CMaterial *)Material->GetData(i))->MaterialName, id) == 0)
			return (CMaterial *) Material->GetData(i);
	}

	return NULL;
}

int CChaDataObject::GetMaterialNum(char *id){

	for (int i = 0; i <= Material->PushPos - 1; i++){
		if (strcmp(((CMaterial *)Material->GetData(i))->MaterialName, id) == 0)
			return i;
	}

	return -1;
}

CTextureName *CChaDataObject::GetTextureName(char *id){

	for (int i = 0; i <= TextureName->PushPos - 1; i++){
		if (strcmp(((CTextureName *)TextureName->GetData(i))->TextureID, id) == 0)
			return (CTextureName *)TextureName->GetData(i);
	}

	return NULL;
}

int CChaDataObject::GetTextureNum( int material_num ){

	CMaterial *material = (CMaterial *) Material->GetData(material_num);
	char *texture_id = material->TextureName;

	for (int i = 0; i <= TextureName->PushPos - 1; i++){

		CTextureName *texture = (CTextureName *)TextureName->GetData(i);
		if (strcmp(texture->TextureID, texture_id) == 0)
			return i;
	}

	return -1;
}

bool CChaDataObject::LoadDAE(wchar_t *filename){
		
	MSXML2::IXMLDOMDocumentPtr pDoc("MSXML2.DOMDocument");
	pDoc->validateOnParse = VARIANT_FALSE;
	pDoc->put_async(VARIANT_FALSE);

	pDoc->validateOnParse = VARIANT_FALSE;
	pDoc->put_async(VARIANT_FALSE);

	size_t size;
	char name[SIZE_CHAR_MAX];
	memset(name, 0, SIZE_CHAR_MAX);
	wcstombs_s(&size, name, filename, SIZE_CHAR_MAX);

	HRESULT hr = pDoc->load(name);
	if (hr == 0 ){

		return false;
	}

	LoadTextureName(pDoc);
	LoadMaterial(pDoc);
	LoadMesh(pDoc);

	return true;
}

bool CChaDataObject::LoadMaterial(MSXML2::IXMLDOMDocumentPtr pDoc){

	MSXML2::IXMLDOMNodeListPtr node_list = pDoc->selectNodes(L"COLLADA/library_materials/material");

	size_t size;
	CMaterial Mat;
	char material_id[SIZE_CHAR_MAX];
	char texture_id[SIZE_CHAR_MAX];
	wchar_t path[SIZE_CHAR_MAX];
	for (int i = 0; i <= (int)node_list->Getlength() - 1; i++){

		memset(material_id, 0, SIZE_CHAR_MAX);
		memset(texture_id, 0, SIZE_CHAR_MAX);
		memset(&Mat, 0, sizeof(CMaterial));

		MSXML2::IXMLDOMElementPtr material = node_list->Getitem(i);
		wcstombs_s(&size, material_id, material->getAttribute("id").bstrVal, SIZE_CHAR_MAX);
		strcpy_s(Mat.MaterialName, SIZE_CHAR_MAX, material_id);

		wmemset(path, 0, SIZE_CHAR_MAX);
		MSXML2::IXMLDOMElementPtr material_source = material->selectSingleNode(L"instance_effect");
		wcscpy_s(path, SIZE_CHAR_MAX, L"COLLADA/library_effects/effect[@id='");
		wcscat_s(path, SIZE_CHAR_MAX, material_source->getAttribute("url").bstrVal + 1);
		wcscat_s(path, SIZE_CHAR_MAX, L"']");

		MSXML2::IXMLDOMNodeListPtr effect_list = pDoc->selectNodes(path);
		for (int j = 0; j <= (int)effect_list->Getlength() - 1; j++){

			MSXML2::IXMLDOMElementPtr effect = effect_list->Getitem(j);
			if (effect == NULL)
				continue;

			MSXML2::IXMLDOMElementPtr diffuse = effect->selectSingleNode("profile_COMMON/technique/phong/diffuse/texture");
			if (diffuse == NULL)
				diffuse = effect->selectSingleNode("profile_COMMON/technique/lambert/diffuse/texture");
			if (diffuse != NULL){
				wmemset(path, 0, SIZE_CHAR_MAX);
				wcscpy_s(path, SIZE_CHAR_MAX, L"profile_COMMON/newparam[@sid='");
				wcscat_s(path, SIZE_CHAR_MAX, diffuse->getAttribute("texture").bstrVal);
				wcscat_s(path, SIZE_CHAR_MAX, L"']/sampler2D/source");
				MSXML2::IXMLDOMElementPtr sampler = effect->selectSingleNode(path);
				if (sampler != NULL){
					wmemset(path, 0, SIZE_CHAR_MAX);
					wcscpy_s(path, SIZE_CHAR_MAX, L"profile_COMMON/newparam[@sid='");
					wcscat_s(path, SIZE_CHAR_MAX, sampler->Gettext());
					wcscat_s(path, SIZE_CHAR_MAX, L"']/surface/init_from");
					MSXML2::IXMLDOMElementPtr surface = effect->selectSingleNode(path);
					if (surface != NULL){
						wcstombs_s(&size, texture_id, surface->Gettext(), SIZE_CHAR_MAX);
						strcpy_s(Mat.TextureName, SIZE_CHAR_MAX, texture_id);
					}
				}
			}
		}

		Material->Push((void *) &Mat );
	}

	return true;
}

bool CChaDataObject::LoadTextureName(MSXML2::IXMLDOMDocumentPtr pDoc){

	size_t size;
	MSXML2::IXMLDOMNodeListPtr node_list = pDoc->selectNodes(L"COLLADA/library_images/image");

	char texture_id[SIZE_CHAR_MAX];
	char texture_name[SIZE_CHAR_MAX];
	CTextureName Tex;
	for (int i = 0; i <= (int)node_list->Getlength() - 1; i++){

		MSXML2::IXMLDOMElementPtr tex = node_list->Getitem(i);
		wcstombs_s(&size, texture_id, tex->getAttribute("id").bstrVal, SIZE_CHAR_MAX);
		strcpy_s(texture_name, SIZE_CHAR_MAX, tex->selectSingleNode("init_from")->Gettext());

		memset(&Tex, 0, sizeof(CTextureName));
		strcpy_s((char *)&Tex, SIZE_CHAR_MAX, texture_id );
		strcpy_s((char *)&Tex + SIZE_CHAR_MAX, SIZE_CHAR_MAX, texture_name );

		TextureName->Push((void *)&Tex);
	}

	return true;
}

bool CChaDataObject::LoadMesh(MSXML2::IXMLDOMDocumentPtr pDoc){

	size_t size;
	char geometry_id[SIZE_CHAR_MAX];
	char array_text[SIZE_CHAR_MAX];
	char stride_text[SIZE_CHAR_MAX];
	char semantic_text[SIZE_CHAR_MAX];
	char source_text[SIZE_CHAR_MAX];
	char offset_text[SIZE_CHAR_MAX];
	int array_count = 0;
	int stride = 0;
	int face_count = 0;
	CVector *float_array = NULL;
	char *ctx;
	char *value_text = NULL;
	CVertexData *vertex_data = NULL;
	CPolygonData *pgn_data = NULL;
	CGeometry *gmt = NULL;

	MSXML2::IXMLDOMNodeListPtr geometry_list = pDoc->selectNodes(L"COLLADA/library_geometries/geometry");
	for (int i = 0; i <= (int)geometry_list->Getlength() - 1; i++){

		gmt = new CGeometry();
		memset(geometry_id, 0, SIZE_CHAR_MAX);

		//ジオメトリid取得
		MSXML2::IXMLDOMElementPtr geometry = geometry_list->Getitem(i);
		wcstombs_s(&size, geometry_id, geometry->getAttribute("id").bstrVal, SIZE_CHAR_MAX);

		MSXML2::IXMLDOMElementPtr mesh = geometry->selectSingleNode("mesh");
		MSXML2::IXMLDOMNodeListPtr mesh_list = mesh->selectNodes(L"source");

		///////////////////////////////////////////////////////////
		// vertex ID取得

		MSXML2::IXMLDOMElementPtr node_vertics = mesh->selectSingleNode(L"vertices");
		MSXML2::IXMLDOMElementPtr node_vertics_input = node_vertics->selectSingleNode(L"input");

		char vertics_id[SIZE_CHAR_MAX];
		memset(vertics_id, 0, SIZE_CHAR_MAX);
		wcstombs_s(&size, vertics_id, node_vertics->getAttribute("id").bstrVal, SIZE_CHAR_MAX);

		char vertics_source[SIZE_CHAR_MAX];
		memset(vertics_source, 0, SIZE_CHAR_MAX);
		wcstombs_s(&size, vertics_source, node_vertics_input->getAttribute("source").bstrVal + 1, SIZE_CHAR_MAX);

		////////////////////////////////////////////////////////////////

		bool is_vertex = false;
		for (int k = 0; k <= (int)mesh_list->Getlength() - 1; k++){

			//Vertex, Nomal, UVの座標データを取得

			vertex_data = new CVertexData();

			is_vertex = false;
			MSXML2::IXMLDOMElementPtr source = mesh_list->Getitem(k);
			wcstombs_s(&size, vertex_data->ID, source->getAttribute("id").bstrVal, SIZE_CHAR_MAX);
			if (strcmp(vertex_data->ID, vertics_source) == 0){
				memset(vertex_data->ID, 0, SIZE_CHAR_MAX);
				strcpy_s(vertex_data->ID, SIZE_CHAR_MAX, vertics_id);
				is_vertex = true;
			}

			MSXML2::IXMLDOMElementPtr node = source->selectSingleNode(L"float_array");

			array_count = 0;
			memset(array_text, 0, SIZE_CHAR_MAX);
			wcstombs_s(&size, array_text, node->getAttribute("count").bstrVal, SIZE_CHAR_MAX);
			array_count = atoi(array_text);

			memset(stride_text, 0, SIZE_CHAR_MAX);
			MSXML2::IXMLDOMElementPtr node_format = source->selectSingleNode(L"technique_common/accessor");
			wcstombs_s(&size, stride_text, node_format->getAttribute("stride").bstrVal, SIZE_CHAR_MAX);
			stride = atoi(stride_text);

			int value_count = array_count / stride;
			if (is_vertex == true )
				gmt->VertexCount = value_count;

			int length = array_count * FLOAT_STRING_SIZE;
			float_array = new CVector(sizeof(float));

			value_text = new char[length];
			memset(value_text, 0, length);
			wcstombs_s(&size, value_text, length, node->Gettext(), length - 1);
			char *tmp = strtok_s(value_text, " ", &ctx);
			float value = atof(tmp);
			float_array->Push((void *)&value);
			for (int m = 1; m <= array_count; m++){
				tmp = strtok_s(NULL, " ", &ctx);
				if (tmp == NULL)
					break;
				value = atof(tmp);
				float_array->Push((void *)&value);
			}
			delete[] value_text;

			if (stride == 3){
				CVector *vector_float3 = new CVector(sizeof(float3));
				int cnt = 0;
				while (cnt < array_count){
					float3 value;
					value.x = *(float *)float_array->GetData(cnt);
					value.y = *(float *)float_array->GetData(cnt + 1);
					value.z = *(float *)float_array->GetData(cnt + 2);
					cnt += 3;
					vector_float3->Push((void *)&value);
				}
				vertex_data->Data = vector_float3;
			}
			else if (stride == 2){
				CVector *vector_float2 = new CVector(sizeof(float2));
				int cnt = 0;
				while (cnt < array_count){
					float2 value;
					value.x = *(float *)float_array->GetData(cnt);
					value.y = *(float *)float_array->GetData(cnt + 1);
					cnt += 2;
					vector_float2->Push((void *)&value);
				}
				vertex_data->Data = vector_float2;
			}

			gmt->Vertics->Push((void *)vertex_data);

			delete float_array;
		}

		//////////////////////////////////////////////////////////

		mesh_list = mesh->selectNodes(L"polylist");
		if ((int)mesh_list->Getlength() <= 0)
			mesh_list = mesh->selectNodes(L"triangles");
		for (int k = 0; k <= (int)mesh_list->Getlength() - 1; k++){

			//ポリゴンのインデックス取得

			MSXML2::IXMLDOMElementPtr polylist = mesh_list->Getitem(k);

			pgn_data = new CPolygonData();
			int vertex_offset = -1;
			int normal_offset = -1;
			int tex_offset = -1;
			int color_offset = -1;

			if (polylist->getAttribute("material").bstrVal != NULL)
				wcstombs_s(&size, pgn_data->MaterialID, polylist->getAttribute("material").bstrVal, SIZE_CHAR_MAX);

			MSXML2::IXMLDOMNodeListPtr input_list = polylist->selectNodes(L"input");
			int max_offset = 0;
			for (int l = 0; l <= (int)input_list->Getlength() - 1; l++){
				MSXML2::IXMLDOMElementPtr input = input_list->Getitem(l);

				memset(semantic_text, 0, SIZE_CHAR_MAX);
				memset(source_text, 0, SIZE_CHAR_MAX);
				memset(offset_text, 0, SIZE_CHAR_MAX);

				wcstombs_s(&size, semantic_text, input->getAttribute("semantic").bstrVal, SIZE_CHAR_MAX);
				wcstombs_s(&size, source_text, input->getAttribute("source").bstrVal + 1, SIZE_CHAR_MAX);
				wcstombs_s(&size, offset_text, input->getAttribute("offset").bstrVal, SIZE_CHAR_MAX);
				int offset = atoi(offset_text);

				if (strcmp(semantic_text, "VERTEX") == 0){
					strcpy_s(pgn_data->VertexID, SIZE_CHAR_MAX, source_text);
					vertex_offset = offset;
				}
				if (strcmp(semantic_text, "NORMAL") == 0){
					strcpy_s(pgn_data->NormalID, SIZE_CHAR_MAX, source_text);
					normal_offset = offset;
				}
				if (strcmp(semantic_text, "TEXCOORD") == 0){
					strcpy_s(pgn_data->TexID, SIZE_CHAR_MAX, source_text);
					tex_offset = offset;
				}
				if (strcmp(semantic_text, "COLOR") == 0){
					strcpy_s(pgn_data->ColorID, SIZE_CHAR_MAX, source_text);
					color_offset = offset;
				}
				if (offset > max_offset)
					max_offset = offset;
			}
			max_offset += 1;

			int p_count = polylist->getAttribute("count");
			MSXML2::IXMLDOMElementPtr index_array = polylist->selectSingleNode(L"p");
			CVector *index_tmp = new CVector(sizeof(int));

			int length = p_count * INDEX_STRING_SIZE;
			char *p_text = new char[length];
			memset(p_text, 0, length);
			wcstombs_s(&size, p_text, length, index_array->Gettext(), length - 1);

			char *tmp = strtok_s(p_text, " ", &ctx);
			int index = atoi(tmp);
			index_tmp->Push((void *)&index);
			do{
				tmp = strtok_s(NULL, " ", &ctx);
				if (tmp == NULL){
					break;
				}
				index = atoi(tmp);
				index_tmp->Push((void *)&index);
			} while (tmp != NULL);

			delete[] p_text;

			for (int j = 0; j <= index_tmp->PushPos - 1; j += max_offset){
				if (vertex_offset >= 0){
					pgn_data->VertexIndex->Push( index_tmp->GetData(j + vertex_offset));
				}
				if (normal_offset >= 0){
					pgn_data->NormalIndex->Push(index_tmp->GetData(j + normal_offset));
				}
				if (tex_offset >= 0){
					pgn_data->TexIndex->Push(index_tmp->GetData(j + tex_offset));
				}
				if (color_offset >= 0){
					pgn_data->ColorIndex->Push(index_tmp->GetData(j + color_offset));
				}
			}

			gmt->PolygonData->Push((void *)pgn_data);

			delete index_tmp;
		}
		Geometry->Push((void *)gmt);
	}
		
	return true;
}
