// ontroller.cpp: implementation of the Controller class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CryViewer.h"
#include "Controller.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Controller::Controller() : BaseObj()
{
	memset(&chunk,0,sizeof(chunk));
	nKeys	= 0;
	keys	= NULL;
}

Controller::~Controller()
{
	if(keys) delete[] keys;
}

bool Controller::Load(FILE *f, long pos)
{
	if(fseek(f,pos,SEEK_SET)) return true;

	int res=fread(&chunk,sizeof(chunk),1,f);
	if(res!=1) return true;
	
	if(chunk.chdr.ChunkType != ChunkType_Controller || chunk.chdr.ChunkVersion != CONTROLLER_CHUNK_DESC_VERSION)
	{
		memset(&chunk,0,sizeof(chunk));
		return true;
	}

	hdr=chunk.chdr;
	nKeys=chunk.nKeys;

	//free keys
	if(keys) free(keys);
	keys =NULL;

	//determine key type
	int size=0;
	switch(chunk.type)
	{
	case CTRL_LINEER1:	size=sizeof(CryLin1Key); break;
	case CTRL_LINEER3:	size=sizeof(CryLin3Key); break;
	case CTRL_LINEERQ:	size=sizeof(CryLinQKey); break;
	case CTRL_BEZIER1:	size=sizeof(CryBez1Key); break;
	case CTRL_BEZIER3:	size=sizeof(CryBez3Key); break;
	case CTRL_BEZIERQ:	size=sizeof(CryBezQKey); break;
	case CTRL_TCB1:		size=sizeof(CryTCB1Key); break;
	case CTRL_TCB3:		size=sizeof(CryTCB3Key); break;
	case CTRL_TCBQ:		size=sizeof(CryTCBQKey); break;
	case CTRL_CRYBONE:	size=sizeof(CryBoneKey); break;
	}
	
	if(size)
	{
		keys = calloc(size,nKeys);
		if(!keys) return true;
		int res=fread(keys,size,nKeys,f);
		if(res!=nKeys) return true;
	}

	return false;
}

bool Controller::GetValue(int time, CryQuat& q, CryPoint3 &p, bool* at_key)
{
	if(!nKeys)						return true;
	if(!keys)						return true;
	if(chunk.type != CTRL_CRYBONE)	return true;

	//find the nearest keys
	CryBoneKey *ckeys = (CryBoneKey *)keys;
	if(at_key) *at_key=false;

	//- extern
	if(ckeys[0].time >= time)
	{
		q=ckeys[0].relquat;
		p=ckeys[0].relpos;

		return false;
	}

	//try mid values
	bool found=false;
	for(int i=1;i<nKeys;i++)
	{
		if(time == ckeys[i].time)
		{
			q=ckeys[i].relquat;
			p=ckeys[i].relpos;
			found=true;
			if(at_key) *at_key=true;
			break;
		}
		else if(time < ckeys[i].time)
		{
			float t= (float(time-ckeys[i-1].time))/(ckeys[i].time - ckeys[i-1].time);
			q.Slerp(ckeys[i-1].relquat, ckeys[i].relquat, t);
			CryPoint3 dp= ckeys[i].relpos - ckeys[i-1].relpos;
			p= ckeys[i-1].relpos + dp*t;
			found = true;
			break;
		}
	}
	if(found) return false;

	//+ extern
	q=ckeys[nKeys-1].relquat;
	p=ckeys[nKeys-1].relpos;
	return false;
}

bool Controller::GetValue(int time, CryPoint3 &val, bool* at_key)
{
	if(!nKeys)	return true;
	if(!keys)	return true;
	if(chunk.type==CTRL_LINEER3)
	{
		//find the nearest keys
		CryLin3Key *ckeys = (CryLin3Key *)keys;
		if(at_key) *at_key=false;

		//- extern
		if(ckeys[0].time >= time)
		{
			val=ckeys[0].val;
			return false;
		}

		//try mid values
		bool found=false;
		for(int i=1;i<nKeys;i++)
		{
			if(time == ckeys[i].time)
			{
				val=ckeys[i].val;
				found=true;
				if(at_key) *at_key=true;
				break;
			}
			else if(time < ckeys[i].time)
			{
				float t= (float(time-ckeys[i-1].time))/(ckeys[i].time - ckeys[i-1].time);
				CryPoint3 dp= ckeys[i].val - ckeys[i-1].val;
				val= ckeys[i-1].val + dp*t;
				found = true;
				break;
			}
		}
		if(found) return false;

		//+ extern
		val=ckeys[nKeys-1].val;
		return false;
	}
	
	if(chunk.type==CTRL_BEZIER3)
	{
		//find the nearest keys
		CryBez3Key *ckeys = (CryBez3Key *)keys;
		if(at_key) *at_key=false;
		#pragma message("~~~~~ correct the interpolation ~~~~~")

		//- extern
		if(ckeys[0].time >= time)
		{
			val=ckeys[0].val;
			return false;
		}

		//try mid values
		bool found=false;
		for(int i=1;i<nKeys;i++)
		{
			if(time == ckeys[i].time)
			{
				val=ckeys[i].val;
				found=true;
				if(at_key) *at_key=true;
				break;
			}
			else if(time < ckeys[i].time)
			{
				float t= (float(time-ckeys[i-1].time))/(ckeys[i].time - ckeys[i-1].time);
				CryPoint3 dp= ckeys[i].val - ckeys[i-1].val;
				val= ckeys[i-1].val + dp*t;
				found = true;
				break;
			}
		}
		if(found) return false;

		//+ extern
		val=ckeys[nKeys-1].val;
		return false;
	}

	if(chunk.type==CTRL_BEZIER3)
	{
		//find the nearest keys
		CryTCB3Key *ckeys = (CryTCB3Key *)keys;
		if(at_key) *at_key=false;
		#pragma message("~~~~~ correct the interpolation ~~~~~")

		//- extern
		if(ckeys[0].time >= time)
		{
			val=ckeys[0].val;
			return false;
		}

		//try mid values
		bool found=false;
		for(int i=1;i<nKeys;i++)
		{
			if(time == ckeys[i].time)
			{
				val=ckeys[i].val;
				found=true;
				if(at_key) *at_key=true;
				break;
			}
			else if(time < ckeys[i].time)
			{
				float t= (float(time-ckeys[i-1].time))/(ckeys[i].time - ckeys[i-1].time);
				CryPoint3 dp= ckeys[i].val - ckeys[i-1].val;
				val= ckeys[i-1].val + dp*t;
				found = true;
				break;
			}
		}
		if(found) return false;

		//+ extern
		val=ckeys[nKeys-1].val;
		return false;
	}

	return true;
}
	
bool Controller::GetValue(int time, CryQuat &val, bool* at_key)
{
	if(!nKeys)	return true;
	if(!keys)	return true;
	if(chunk.type==CTRL_LINEERQ)
	{
		//find the nearest keys
		CryLinQKey *ckeys = (CryLinQKey *)keys;
		if(at_key) *at_key=false;

		//- extern
		if(ckeys[0].time >= time)
		{
			val=ckeys[0].val;
			return false;
		}

		//try mid values
		bool found=false;
		for(int i=1;i<nKeys;i++)
		{
			if(time == ckeys[i].time)
			{
				val=ckeys[i].val;
				found=true;
				if(at_key) *at_key=true;
				break;
			}
			else if(time < ckeys[i].time)
			{
				float t= (float(time-ckeys[i-1].time))/(ckeys[i].time - ckeys[i-1].time);
				val.Slerp(ckeys[i-1].val, ckeys[i].val, t);
				found = true;
				break;
			}
		}
		if(found) return false;

		//+ extern
		val=ckeys[nKeys-1].val;
		return false;
	}

	if(chunk.type==CTRL_BEZIERQ)
	{
		//find the nearest keys
		CryBezQKey *ckeys = (CryBezQKey *)keys;
		if(at_key) *at_key=false;

		//- extern
		if(ckeys[0].time >= time)
		{
			val=ckeys[0].val;
			return false;
		}

		//try mid values
		bool found=false;
		for(int i=1;i<nKeys;i++)
		{
			if(time == ckeys[i].time)
			{
				val=ckeys[i].val;
				found=true;
				if(at_key) *at_key=true;
				break;
			}
			else if(time < ckeys[i].time)
			{
				float t= (float(time-ckeys[i-1].time))/(ckeys[i].time - ckeys[i-1].time);
				val.Slerp(ckeys[i-1].val, ckeys[i].val, t);
				found = true;
				break;
			}
		}
		if(found) return false;

		//+ extern
		val=ckeys[nKeys-1].val;
		return false;
	}	

	if(chunk.type==CTRL_TCBQ)
	{
		//find the nearest keys
		CryTCBQKey *ckeys = (CryTCBQKey *)keys;
		if(at_key) *at_key=false;

		//- extern
		if(ckeys[0].time >= time)
		{
			val=ckeys[0].val;
			return false;
		}
#pragma message("~~~~~ correct the interpolation ~~~~~")

		//try mid values
		bool found=false;
		for(int i=1;i<nKeys;i++)
		{
			if(time == ckeys[i].time)
			{
				val=ckeys[i].val;
				found=true;
				if(at_key) *at_key=true;
				break;
			}
			else if(time < ckeys[i].time)
			{
				float t= (float(time-ckeys[i-1].time))/(ckeys[i].time - ckeys[i-1].time);
				val.Slerp(ckeys[i-1].val, ckeys[i].val, t);
				found = true;
				break;
			}
		}
		if(found) return false;

		//+ extern
		val=ckeys[nKeys-1].val;
		return false;
	}		
	
	return true;
}
	