/** 
 * nslExternalAnimation.cpp
 *
 *		Copyright (c) 2011, Fumi.Iseki
 * 
 */

#include "linden_common.h"
#include "llcharacter.h"

#include "nslExternalAnimation.h"



using namespace nsl;



static  std::string  SLJointName[] =
{
	"mPelvis",
	"mTorso",
	"mChest",
	"mNeck",
	"mHead",
	"mCollarLeft",
	"mShoulderLeft",
	"mElbowLeft",
	"mWristLeft",
	"mCollarRight",
	"mShoulderRight",
	"mElbowRight",
	"mWristRight",
	"mHipLeft",
	"mKneeLeft",
	"mAnkleLeft",
	"mHipRight",
	"mKneeRight",
	"mAnkleRight"
}; 







void  ExternalAnimation::init(LLUUID id)
{
	uuid		= id;
	isExternal	= FALSE;
	channel		= ReadExternalAnimationUUID(id.asString());

	// Shared Memory for IPC
	for (int i=0; i<SL_MAX_JOINT_NUM; i++) {
		mapHandle[i] = NULL;
		ptrShm[i]    = NULL;
	}

	if (channel>0) {
		isExternal = TRUE;
		CreateShm();
	}
}




int  ExternalAnimation::ReadExternalAnimationUUID(std::string uuid)
{
	int	 chnnl = 0;
	char read_line[LEN_UUID_PARAM];

	FILE* fp = fopen(ANIM_UUID_FNAME, "r");
	if (fp==NULL) return chnnl;

	while (!feof(fp)) {
		fgets(read_line, LEN_UUID_PARAM, fp);
		char* read_uuid = pack_char(read_line, ' ');
		if (read_uuid!=NULL) {
			char* sparam = getSecondParam(read_uuid);
			std::string str = read_uuid;

			if (uuid==str) {
				if (sparam!=NULL) chnnl = atoi(sparam);
				if (chnnl<=0 || chnnl>SL_MAX_CHANNEL_NUM) chnnl = 1;
				::free(read_uuid);
				break;
			}
			::free(read_uuid);
		}
	}
		
	fclose(fp);

	return chnnl;
}





/////////////////////////////////////////////////////////////////////////////////////
// 共有メモリ

void 	ExternalAnimation::CreateShm(void)
{
	if (!isExternal) return;

	for (int i=0; i<SL_MAX_JOINT_NUM; i++) {
		CreateJointShm(i);
	}
}





void 	ExternalAnimation::CreateJointShm(int n)
{
	if (ptrShm[n]==NULL) {
		if (mapHandle[n]==NULL) {
			mapHandle[n] = CreateFileMappingA((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, SL_SHM_SIZE*SL_MAX_CHANNEL_NUM, SLJointName[n].c_str());
		}

		if (mapHandle[n]!=NULL) {
			ptrShm[n] = (double*)MapViewOfFile(mapHandle[n], FILE_MAP_READ, 0, 0, SL_SHM_SIZE*SL_MAX_CHANNEL_NUM);
		}
	}
}





void 	ExternalAnimation::OpenJointShm(int n)
{
	//if (n<0 || n>=SL_MAX_JOINT_NUM) return;
	
	if (ptrShm[n]==NULL) {
		if (mapHandle[n]==NULL) {
			mapHandle[n] = OpenFileMappingA(FILE_MAP_READ, FALSE, SLJointName[n].c_str());
		}

		if (mapHandle[n]!=NULL) {
			ptrShm[n] = (double*)MapViewOfFile(mapHandle[n], FILE_MAP_READ, 0, 0, SL_SHM_SIZE*SL_MAX_CHANNEL_NUM);
		}
	}
}




void 	ExternalAnimation::CloseShm(void)
{
	for (int i=0; i<SL_MAX_JOINT_NUM; i++) {
		if (ptrShm[i]!=NULL)    UnmapViewOfFile(ptrShm[i]);
		if (mapHandle[i]!=NULL) CloseHandle(mapHandle[i]);
	}
}





LLQuaternion  ExternalAnimation::GetAnimationRotation(std::string joint_name)
{
	LLQuaternion quat(0.0, 0.0, 0.0, 1.0);

	int     channelpos = (channel-1)*SL_SHM_SIZE;
	double* element = NULL;

	for (int i=0; i<SL_MAX_JOINT_NUM; i++) {
		if (joint_name==SLJointName[i]) {
			OpenJointShm(i);
			element = ptrShm[i] + channelpos;
			break;
		}
	}

	if (element!=NULL) {
		quat.set(element[4], element[5], element[6], element[7]);
	}

	return quat;
}




LLVector3  ExternalAnimation::GetAnimationPosition(std::string joint_name)
{
	LLVector3 vect(0.0, 0.0, 0.0);

	int     channelpos = (channel-1)*SL_SHM_SIZE;
	double* element = NULL;

	for (int i=0; i<SL_MAX_JOINT_NUM; i++) {
		if (joint_name==SLJointName[i]) {
			OpenJointShm(i);
			element = ptrShm[i] + channelpos;
			break;
		}
	}

	if (element!=NULL) {
		vect.set(element[1], element[2], element[3]);
	}

	return vect;
}




LLVector3  ExternalAnimation::GetAnimationScale(std::string joint_name)
{
	return LLVector3(0.0, 0.0, 0.0);
}







/////////////////////////////////////////////////////////////////////////////////////
// Tools


char*  nsl::getSecondParam(char* str)
{
	char* ret = NULL;
	int   len = strlen(str);

	for (int i=0; i<len; i++) {
		if (str[i]==',' || str[i]==':' || str[i]==' ') {
			str[i] = '\0';
			if (str[i+1]!='\r' && str[i+1]!='\n') { 
				ret = &(str[i+1]);
			}
			break;
		}
	}

	return ret;
}







/**
char*  nsl::pack_char(char* mesg, char cc)

	機能：文字列中及び，先頭のcc(複数)，終わりのcc(複数)，CR, LF を削除
		  タブは空白に変換．
		  メッセージ中の連続する cc は 1個に変換
		  返されたデータは free()する必要がある．

		  CR, LF も削除（無視）されるので注意．

	戻り値：変換後の文字列
*/


#define  CR	 	0x0d		// 改行 
#define  LF	 	0x0a		// ラインフィード 
#define  TAB	0x09		// タブ 


char*  nsl::pack_char(char* mesg, char cc)
{
	int i, j;
	int sf = FALSE;	// 連続 cc 用のフラグ
	int ff = TRUE; 	// 先頭の cc を無視するためのフラグ
	char* pp;

	if (mesg==NULL) return NULL;

	pp = (char*)malloc(strlen(mesg)+1);
	if (pp==NULL) return NULL;

	i = j = 0;
	while(mesg[i]!='\0') {
		if (mesg[i]==TAB) mesg[i] = ' ';
		if (mesg[i]!=CR && mesg[i]!=LF) {		// CR LF を読み飛ばす．
			if (mesg[i]==cc) {
				if (sf==FALSE) {
					sf = TRUE;
					if (ff==TRUE) ff = FALSE;
					else  pp[j++] = mesg[i];
				}
			}
			else {
				sf = ff = FALSE;
				pp[j++] = mesg[i];
			}
		}
		i++;
	}
	pp[j] = '\0';

	// 末尾の cc を削除
	i = (int)strlen(pp) - 1;
	while(i>=0) {
		if (pp[i]==cc) i--; 
		else  break;
	}
	pp[i+1] = '\0';

	return pp;
}


