#include "MotionCharactor.h"


vector<string> fk_StrSplit(string argStr, string argToken)
{
	vector<string>		retStrArray;
	string::size_type	curPos = 0, nextPos = 0;

	while(nextPos != string::npos) {
		nextPos = argStr.find(argToken, curPos);
		retStrArray.push_back(argStr.substr(curPos, nextPos-curPos));
		curPos = nextPos+1;
	}

	return retStrArray;
}

CMotionCharactor::CMotionCharactor(void)
{
	init();
}

CMotionCharactor::~CMotionCharactor(void)
{
	init();
}

void CMotionCharactor::init()
{
	for(unsigned int i = 0; i < objModel.size(); i++) {
		delete objModel[i];
	}
	objModel.clear();
	for(unsigned int i = 0; i < jointModel.size(); i++) {
		delete jointModel[i];
	}
	jointModel.clear();

	for(unsigned int i = 0; i < mesh.size(); i++) {
		if(mesh[i]->getObjectType() == FK_IFSTEXTURE) {
			delete (fk_IFSTexture *)mesh[i];
		} else if(mesh[i]->getObjectType() == FK_INDEXFACESET) {
			delete (fk_IndexFaceSet *)mesh[i];
		} else {
			delete mesh[i];
		}
	}
	mesh.clear();
	for(unsigned int i = 0; i < texImage.size(); i++) {
		delete texImage[i];
	}
	texImage.clear();

	objName.clear();
	texName.clear();
	texTable.clear();
	matTable.clear();
	matPalette.clear();

	parentTable.clear();
	keyFrameData.clear();

	mqoName.clear();
	objNum = 0;
	parentConnect = false;

	// モーション複数化対応
	nowFrame.clear();
	maxFrame.clear();

	prevPlayMotionID = 0;
	loopCnt = 0;

	ref_scene = NULL;
}

// MQO ファイルからオブジェクト名を列挙する
bool CMotionCharactor::enumObjectName(string argFileName)
{
	ifstream			in_file(argFileName.c_str());
	vector<string>		readBuf(1), lineStr;
	string::size_type	openPos, closePos;
	unsigned int		i = 0, j = 0, k = 0, l = 0;
	fk_Material			tmpMat;
	fk_Color			tmpCol;
	float				tmpParam[5];
	bool				tmpFlag;

	if(!in_file.is_open()) return false;

	while(getline(in_file, readBuf.at(i))) {
		i++;
		readBuf.push_back("");
	}

	// マテリアル情報の抽出
	for(i = 0; i < readBuf.size(); i++) {
		if(readBuf[i].find("Material") != string::npos) {
			i++;
			while(readBuf[i].find("}") == string::npos) {
				lineStr = fk_StrSplit(readBuf[i], " ");
				for(j = 0; j < lineStr.size(); j++) {
					if(lineStr[j].find("col(") == string::npos) continue;
					// base color
					tmpCol.set(atof(lineStr[j].substr(4, 5).c_str()),
						atof(lineStr[j+1].c_str()),
						atof(lineStr[j+2].c_str()),
						atof(lineStr[j+3].substr(0, 5).c_str())
					);
					// dif
					tmpParam[0] = (float)atof(lineStr[j+4].substr(4, 5).c_str());
					// amb
					tmpParam[1] = (float)atof(lineStr[j+5].substr(4, 5).c_str());
					// emi
					tmpParam[2] = (float)atof(lineStr[j+6].substr(4, 5).c_str());
					// spe
					tmpParam[3] = (float)atof(lineStr[j+7].substr(4, 5).c_str());
					// power
					tmpParam[4] = (float)atof(lineStr[j+8].substr(6, 4).c_str());
					// set
					tmpMat.setDiffuse(tmpCol.getR()*tmpParam[0], tmpCol.getG()*tmpParam[0], tmpCol.getB()*tmpParam[0]);
					tmpMat.setAmbient(tmpCol.getR()*tmpParam[1], tmpCol.getG()*tmpParam[1], tmpCol.getB()*tmpParam[1]);
					tmpMat.setEmission(tmpCol.getR()*tmpParam[2], tmpCol.getG()*tmpParam[2], tmpCol.getB()*tmpParam[2]);
					tmpMat.setSpecular(tmpCol.getR()*tmpParam[3], tmpCol.getG()*tmpParam[3], tmpCol.getB()*tmpParam[3]);
					tmpMat.setShininess(tmpParam[4]);

					matPalette.push_back(tmpMat);

					if(j+9 >= lineStr.size()) {
						texTable.push_back(-1);
						break;
					}

					openPos = lineStr[j+9].find("tex(");
					if(openPos == string::npos) break;
					closePos = lineStr[j+9].find_last_of('\"');
					if(closePos == string::npos) break;

					tmpFlag = false;
					for(k = 0; k < texName.size(); k++) {
						if(texName[k] == lineStr[j+9].substr(openPos+5, closePos-openPos-5)) {
							texTable.push_back(k);
							tmpFlag = true;
							break;
						}
					}
					if(!tmpFlag) {
						texName.push_back(lineStr[j+9].substr(openPos+5, closePos-openPos-5));
						texTable.push_back(texName.size()-1);
					}
				}
				i++;
			}
			break;
		}
	}

	// オブジェクト名の列挙]
	objName.push_back("<base>");
	for(i = 0; i < readBuf.size(); i++) {
		openPos = readBuf[i].find("Object \"");
		if(openPos != string::npos) {
			closePos = readBuf[i].find_last_of('\"');
			if(closePos == string::npos) return false;
			objName.push_back(readBuf[i].substr(openPos+8, closePos-openPos-8));
			while(true) {
				i++;
				// マテリアル指定の抽出
				if(readBuf[i].find("M(") != string::npos) {
					string::size_type	tmpPos = readBuf[i].find(")", readBuf[i].find("M(")+1);	// UV 指定のないオブジェクトも読めるように
					if(tmpPos == string::npos) return false;
					matTable.push_back(atoi(readBuf[i].substr(readBuf[i].find("M(")+2, tmpPos-readBuf[i].find("M(")-2).c_str()));
					break;
				}
			}
		}
	}

	return true;
}

// MQO ファイルから形状とテクスチャを読み込む
bool CMotionCharactor::loadObjectData(string argFileName)
{
	string		texPath;
	fk_Image	alphaChannel;

	init();

	// オブジェクトの読み込み
	if(!enumObjectName(argFileName)) {
		fk_Printf("Enum Object Error.");
		init();
		return false;
	}
	objNum = objName.size();

	jointModel.resize(objNum);
	objModel.resize(objNum);
	mesh.resize(objNum-1);
	texImage.resize(texName.size());

	texPath = argFileName.substr(0, argFileName.find_last_of('/')+1);
	mqoName = argFileName.substr(argFileName.find_last_of('/')+1, string::npos);

	// テクスチャロード
	for(unsigned int i = 0; i < texName.size(); i++) {
		texImage[i] = new fk_Image;

		// PNG 対応
		if(texName[i].rfind(".png") != string::npos || texName[i].rfind(".PNG") != string::npos || texName[i].rfind(".Png") != string::npos) {
			if(!texImage[i]->readPNG(texPath+texName[i])) {
				fk_Printf("Texture File(%s) Read Error.", string(texPath+texName[i]).c_str());
				init();
				return false;
			}
		} else if(texName[i].rfind(".jpg") != string::npos || texName[i].rfind(".JPG") != string::npos || texName[i].rfind(".Jpg") != string::npos
			|| texName[i].rfind(".jpeg") != string::npos || texName[i].rfind(".JPEG") != string::npos || texName[i].rfind(".Jpeg") != string::npos) {
			if(!texImage[i]->readJPG(texPath+texName[i])) {
				fk_Printf("Texture File(%s) Read Error.", string(texPath+texName[i]).c_str());
				init();
				return false;
			}
		} else if(texImage[i]->readBMP(texPath+texName[i])) {
			if(alphaChannel.readBMP(texPath+texName[i].substr(0, texName[i].size()-4)+"_a.bmp")) {
				for(int y = 0; y < alphaChannel.getHeight(); y++) {
					for(int x = 0; x < alphaChannel.getWidth(); x++) {
						texImage[i]->setA(x, y, alphaChannel.getR(x, y));
					}
				}
			}
		} else {
			fk_Printf("Texture File(%s) Read Error.", string(texPath+texName[i]).c_str());
			init();
			return false;
		}

	}

	// 形状ロード
	for(int i = 0; i < objNum; i++) {
		jointModel[i] = new fk_Model;
		objModel[i] = new fk_Model;
		if(i > 0) {
			if(texTable[matTable[i-1]] != -1) {
				mesh[i-1] = new fk_IFSTexture;
				((fk_IFSTexture *)mesh[i-1])->setImage(texImage[texTable[matTable[i-1]]]);
				((fk_IFSTexture *)mesh[i-1])->setTexRendMode(FK_TEX_REND_SMOOTH);

				if(!((fk_IFSTexture *)mesh[i-1])->readMQOFile(argFileName, objName[i])) {
					fk_Printf("MQO File Read Error.");
					init();
					return false;
				}
			} else {
				mesh[i-1] = new fk_IndexFaceSet;
				if(!((fk_IndexFaceSet *)mesh[i-1])->readMQOFile(argFileName, objName[i])) {
					fk_Printf("MQO File Read Error.");
					init();
					return false;
				}
			}

			objModel[i]->setShape(mesh[i-1]);
			objModel[i]->setMaterial(matPalette[matTable[i-1]]);
			objModel[i]->setSmoothMode(true);

			parentTable[i] = 0;
		}
	}

	return true;
}

// FKC ファイルから親子関係と接続位置情報を読み込む
bool CMotionCharactor::loadJointData(string argFileName)
{
	ifstream			in_file(argFileName.c_str());
	string				lineStr;
	vector<string>		arraySplit, readBuf(1);
	int					objIndex = 0, i = 0;

	// オープン正否判定
	if(!in_file.is_open()) return false;

	// ヘッダチェック
	if(!getline(in_file, lineStr)) return false;
	if(lineStr.find("FKC_HEADER_0.2") == string::npos) return false;

	// 親子関係列挙ブロック探索
	do {
		if(!getline(in_file, lineStr)) return false;
	} while(lineStr.find("[ENUM PARENT TABLE BEGIN]") == string::npos);

	// 親子関係読み込み
	parentTable.clear();
	while(true) {
		getline(in_file, lineStr);
		if(lineStr.find("[ENUM PARENT TABLE END]") != string::npos) break;
		arraySplit = fk_StrSplit(lineStr, ",");
		if(arraySplit.size() < 2) return false;
		parentTable[atoi(arraySplit[0].c_str())] = atoi(arraySplit[1].c_str());
	}

	// 接続位置情報探索
	do {
		if(!getline(in_file, lineStr)) return false;
	} while(lineStr.find("[ENUM JOINT POSITION BEGIN]") == string::npos);

	// 接続位置情報読み込み
	while(true) {
		getline(in_file, lineStr);
		if(lineStr.find("[ENUM JOINT POSITION END]") != string::npos) break;
		arraySplit = fk_StrSplit(lineStr, ",");
		if(arraySplit.size() < 3) return false;
		jointModel[objIndex]->glMoveTo(atof(arraySplit[0].c_str()), atof(arraySplit[1].c_str()), atof(arraySplit[2].c_str()));
		objIndex++;
		if(objIndex >= objNum) break;
	}

	jointToPoser();

	return true;
}

// FKM ファイルからモーションデータを読み込む
bool CMotionCharactor::loadMotionData(string argFileName)
{
	ifstream			in_file(argFileName.c_str());
	string				lineStr;
	vector<string>		arraySplit, readBuf(1);
	int					objIndex = 0;

	// モーション複数化対応
	int	totalFrame = 0;
	vector<CKeyFrameMotion>	tmpMotionArray;
	tmpMotionArray.resize(objNum);

	// オープン正否判定
	if(!in_file.is_open()) return false;

	// ヘッダチェック
	if(!getline(in_file, lineStr)) return false;
	if(lineStr.find("FKM_HEADER_0.3") != string::npos) {
		in_file.close();
		return LoadMotionDataEuler(argFileName);
	} else if(lineStr.find("FKM_HEADER_0.2") == string::npos) {
		return false;
	}

	// 親オブジェクトキーフレームブロック探索
	do {
		if(!getline(in_file, lineStr)) return false;
	} while(lineStr.find("[PARENT OBJECT KEYFRAME DATA BEGIN]") == string::npos);

	// 親オブジェクトキーフレーム読み込み
	tmpMotionArray[0].init();
	while(true) {
		getline(in_file, lineStr);
		if(lineStr.find("[PARENT OBJECT KEYFRAME DATA END]") != string::npos) break;
		arraySplit = fk_StrSplit(lineStr, ",");
		if(arraySplit.size() < 9) return false;
		tmpMotionArray[0].pushKeyFrame(
			fk_Quaternion(atof(arraySplit[0].c_str()), atof(arraySplit[1].c_str()), atof(arraySplit[2].c_str()), atof(arraySplit[3].c_str())),
			fk_Vector(atof(arraySplit[4].c_str()), atof(arraySplit[5].c_str()), atof(arraySplit[6].c_str())),
			atoi(arraySplit[7].c_str()), (fk_MotionInterType)atoi(arraySplit[8].c_str())
		);
	}

	// 子オブジェクトキーフレームブロック探索
	do {
		if(!getline(in_file, lineStr)) return false;
	} while(lineStr.find("[CHILD OBJECT KEYFRAME DATA BEGIN]") == string::npos);

	// 子オブジェクトキーフレーム読み込み
	while(true) {
		getline(in_file, lineStr);
		if(lineStr.find("[CHILD OBJECT KEYFRAME DATA END]") != string::npos) break;
		if(lineStr.find("BEGIN") != string::npos) {
			objIndex++;
			if(objIndex >= objNum) break;
			tmpMotionArray[objIndex].init();
			continue;
		} else if(lineStr.find("END") != string::npos) {
			continue;
		}
		arraySplit = fk_StrSplit(lineStr, ",");
		if(arraySplit.size() < 9) return false;
		tmpMotionArray[objIndex].pushKeyFrame(
			fk_Quaternion(atof(arraySplit[0].c_str()), atof(arraySplit[1].c_str()), atof(arraySplit[2].c_str()), atof(arraySplit[3].c_str())),
			fk_Vector(atof(arraySplit[4].c_str()), atof(arraySplit[5].c_str()), atof(arraySplit[6].c_str())),
			atoi(arraySplit[7].c_str()), (fk_MotionInterType)atoi(arraySplit[8].c_str())
		);
	}

	// モーション複数化対応
	keyFrameData.push_back(tmpMotionArray);
	nowFrame.push_back(0);
	totalFrame = 0;
	for(int i = 0; i < objNum; i++) {
		if(totalFrame < keyFrameData[keyFrameData.size()-1][i].getTotalFrameNum()) {
			totalFrame =  keyFrameData[keyFrameData.size()-1][i].getTotalFrameNum();
		}
	}
	maxFrame.push_back(totalFrame);

	return true;
}

// FKM ファイルからモーションデータを読み込む(オイラー角版)
bool CMotionCharactor::LoadMotionDataEuler(string argFileName)
{
	ifstream			in_file(argFileName.c_str());
	string				lineStr;
	vector<string>		arraySplit;
	int					objIndex = 0;
	fk_Quaternion		tmpQ;

	// モーション複数化対応
	int	totalFrame = 0;
	vector<CKeyFrameMotion>	tmpMotionArray;
	tmpMotionArray.resize(objNum);

	// オープン正否判定
	if(!in_file.is_open()) {
		fk_Printf("ファイルが開けません。");
		return false;
	}

	// ヘッダチェック
	if(!getline(in_file, lineStr)) {
		fk_Printf("ファイルが途中で終わっています。");
		return false;
	}
	if(lineStr.find("FKM_HEADER_0.3") == string::npos) {
		fk_Printf("違う種類のファイルを読み込もうとしました。");
		return false;
	}

	// 親オブジェクトキーフレームブロック探索
	do {
		if(!getline(in_file, lineStr)) {
			fk_Printf("ファイルが途中で終わっています。");
			return false;
		}
	} while(lineStr.find("[PARENT OBJECT KEYFRAME DATA BEGIN]") == string::npos);

	// 親オブジェクトキーフレーム読み込み
	tmpMotionArray[0].init();
	while(true) {
		if(!getline(in_file, lineStr)) {
			fk_Printf("ファイルが途中で終わっています。");
			return false;
		}
		if(lineStr.find("[PARENT OBJECT KEYFRAME DATA END]") != string::npos) break;
		arraySplit = fk_StrSplit(lineStr, ",");
		if(arraySplit.size() < 8) {
			fk_Printf("データの形式が想定と異なります。");
			return false;
		}
		tmpQ.makeEuler(atof(arraySplit[0].c_str()), atof(arraySplit[1].c_str()), atof(arraySplit[2].c_str()));
		tmpMotionArray[0].pushKeyFrame(
			tmpQ,
			fk_Vector(atof(arraySplit[3].c_str()), atof(arraySplit[4].c_str()), atof(arraySplit[5].c_str())),
			atoi(arraySplit[6].c_str()), (fk_MotionInterType)atoi(arraySplit[7].c_str())
		);
	}

	// 子オブジェクトキーフレームブロック探索
	do {
		if(!getline(in_file, lineStr)) {
			fk_Printf("ファイルが途中で終わっています。");
			return false;
		}
	} while(lineStr.find("[CHILD OBJECT KEYFRAME DATA BEGIN]") == string::npos);

	// 子オブジェクトキーフレーム読み込み
	while(true) {
		if(!getline(in_file, lineStr)) {
			fk_Printf("ファイルが途中で終わっています。");
			return false;
		}
		if(lineStr.find("[CHILD OBJECT KEYFRAME DATA END]") != string::npos) break;
		if(lineStr.find("BEGIN") != string::npos) {
			objIndex++;
			if(objIndex >= objNum) break;
			tmpMotionArray[objIndex].init();
			continue;
		} else if(lineStr.find("END") != string::npos) {
			continue;
		}
		arraySplit = fk_StrSplit(lineStr, ",");
		if(arraySplit.size() < 8) {
			fk_Printf("データの形式が想定と異なります。");
			return false;
		}
		tmpQ.makeEuler(atof(arraySplit[0].c_str()), atof(arraySplit[1].c_str()), atof(arraySplit[2].c_str()));
		tmpMotionArray[objIndex].pushKeyFrame(
			tmpQ,
			fk_Vector(atof(arraySplit[3].c_str()), atof(arraySplit[4].c_str()), atof(arraySplit[5].c_str())),
			atoi(arraySplit[6].c_str()), (fk_MotionInterType)atoi(arraySplit[7].c_str())
		);
	}

	// モーション複数化対応
	keyFrameData.push_back(tmpMotionArray);
	nowFrame.push_back(0);
	totalFrame = 0;
	for(int i = 0; i < objNum; i++) {
		if(totalFrame < keyFrameData[keyFrameData.size()-1][i].getTotalFrameNum()) {
			totalFrame =  keyFrameData[keyFrameData.size()-1][i].getTotalFrameNum();
		}
	}
	maxFrame.push_back(totalFrame);

	return true;
}

void CMotionCharactor::entryScene(fk_Scene *argScene)
{
	for(int i = 0; i < objNum; i++) {
		argScene->entryModel(objModel[i]);
	}
	ref_scene = argScene;

	return;
}

void CMotionCharactor::removeScene(fk_Scene *argScene)
{
	for(int i = 0; i < objNum; i++) {
		argScene->removeModel(objModel[i]);
	}
	ref_scene = NULL;

	return;

}

void CMotionCharactor::setDrawMode(bool argMode)
{
	if(!argMode) {
		for(int i = 1; i < objNum; i++) {
			if(texTable[matTable[i-1]] != -1) {
				objModel[i]->setShape(((fk_IFSTexture *)mesh[i-1])->getIFS());
			}
			objModel[i]->setDrawMode(FK_LINEMODE);
		}
	} else {
		for(int i = 1; i < objNum; i++) {
			objModel[i]->setShape(mesh[i-1]);
			objModel[i]->setDrawMode(FK_POLYMODE);
		}
	}

	return;
}

int CMotionCharactor::getObjectNum(void)
{
	return objNum;
}

fk_Model * CMotionCharactor::getObjectModel(int argID)
{
	if(argID < 0 || argID >= objNum) return NULL;
	return objModel[argID];
}

fk_Model * CMotionCharactor::getJointModel(int argID)
{
	if(argID < 0 || argID >= objNum) return NULL;
	return jointModel[argID];
}

void CMotionCharactor::jointToPoser(void)
{
	if(parentConnect) return;
	if(objNum == 0) return;

	for(parentIte ite = parentTable.begin(); ite != parentTable.end(); ite++) {
		jointModel[ite->first]->setParent(jointModel[ite->second], false);
		jointModel[ite->first]->glTranslate(-jointModel[ite->second]->getInhPosition());
	}

	for(int i = 0; i < objNum; i++) {
		objModel[i]->setParent(jointModel[i], false);
		objModel[i]->glTranslate(-jointModel[i]->getInhPosition());
	}

	jointModel[0]->setParent(&absParent, false);

	parentConnect = true;

	return;
}

int CMotionCharactor::getNowFrame(int argMotionID)
{
	if(argMotionID < 0 || argMotionID >= (int)keyFrameData.size()) return -1;
	return nowFrame[argMotionID];
}

int CMotionCharactor::getTotalFrame(int argMotionID)
{
	if(argMotionID < 0 || argMotionID >= (int)keyFrameData.size()) return -1;
	return maxFrame[argMotionID];
}

void CMotionCharactor::setNowFrame(int argMotionID, int argFrame)
{
	if(argMotionID < 0 || argMotionID >= (int)keyFrameData.size()) return;
	if(argFrame < 0 || maxFrame[argMotionID] > argFrame) return;
	nowFrame[argMotionID] = argFrame;
}

bool CMotionCharactor::isMotionFinished(int argMotionID)
{
	if(argMotionID < 0 || argMotionID >= (int)keyFrameData.size()) return false;
	if(nowFrame[argMotionID] >= maxFrame[argMotionID]) return true;
	else return false;
}

int CMotionCharactor::getLoopCount(void)
{
	return loopCnt;
}

bool CMotionCharactor::playMotion(int argMotionID)
{
	if(argMotionID < 0 || argMotionID >= (int)keyFrameData.size()) return false;

	// 長さのないモーションを再生しようとした場合
	if(maxFrame[argMotionID] == 0) return false;

	// 前フレと違うモーションを再生する場合は最初から
	if(prevPlayMotionID != argMotionID) {
		nowFrame[prevPlayMotionID] = 0;
		nowFrame[argMotionID] = 0;
		loopCnt = 0;
		objShowHide(argMotionID);
	}

	// ループ時の処理
	if(nowFrame[argMotionID] > maxFrame[argMotionID]) {
		nowFrame[argMotionID] = 0;
		loopCnt++;
	}
	// 再生
	nowFrame[argMotionID]++;
	for(int i = 0; i < objNum; i++) {
		keyFrameData[argMotionID][i].setFrameState(nowFrame[argMotionID], jointModel[i]);
	}

	prevPlayMotionID = argMotionID;
	return true;
}

void CMotionCharactor::stillMotion(int argMotionID, int argFrame)
{
	if(argMotionID < 0 || argMotionID >= (int)keyFrameData.size()) return;
	if(argFrame < 0 || maxFrame[argMotionID] > argFrame) return;

	for(int i = 1; i < objNum; i++) {
		keyFrameData[argMotionID][i].setFrameState(argFrame, jointModel[i]);
	}
	prevPlayMotionID = argMotionID;

	return;
}

void CMotionCharactor::objShowHide(int argMotionID)
{
	if(argMotionID < 0 || argMotionID >= (int)keyFrameData.size()) return;
	if(ref_scene == NULL) return;
	for(int i = 0; i < objNum; i++) {
		if(keyFrameData[argMotionID][i].getTotalKeyNum() > 0) {
			if(keyFrameData[argMotionID][i].interTypeArray[0] == HIDE) {
				ref_scene->removeModel(objModel[i]);
			} else {
				ref_scene->entryModel(objModel[i]);
			}
		} else {
			ref_scene->entryModel(objModel[i]);
		}
	}

	return;
}
