// Bone.cpp: implementation of the Bone class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CryViewer.h"
#include "Bone.h"

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

#pragma warning( disable : 4244)
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Bone::Bone() : BaseObj()
{
	parent		= NULL;
	children	= NULL;
	cont		= NULL;
	BoneNames	= NULL;

	memset(&chunk,0,sizeof(chunk));
	memset(&info,0,sizeof(info));
}

Bone::~Bone()
{
	if(info.nChildren) delete[] children;
}

bool Bone::Load(FILE *f, long pos)
{
	if(pos>=0)
	{
		if(fseek(f,pos,SEEK_SET)) return true;
		//read the chunk header
		int res=fread(&chunk,sizeof(chunk),1,f);
		if(res!=1) return true;
		hdr=chunk.chdr;
	}

	//read  bone info
	int res=fread(&info,sizeof(info),1,f);
	if(res!=1) return true;

	/*
	//read the keys
	keys=(CryKey *)calloc(sizeof(CryKey), nKeys);
	assert(keys);
	res=fread(keys,sizeof(CryKey),nKeys,f);
	if(res!=nKeys) return true;
	*/

	//read the children
	if(info.nChildren)
	{
		children = new Bone[info.nChildren];
		assert(children);

		for(int i=0;i<info.nChildren;i++)
		{
			children[i].parent	=this;
			children[i].chunk	=chunk;
			children[i].hdr		=hdr;
			children[i].Load(f,-1);

		}
	}

	return false;
}

void Bone::Bind(BaseObj **all_objects, int n_obj)
{
	for(int i=0;i<n_obj;i++)
	{
		BaseObj *o=all_objects[i];
		if(!o) continue;

		if(o->hdr.ChunkType==ChunkType_BoneNameList)
		{
			BoneNames=(BoneNameList*)o;
		}

		if(o->hdr.ChunkID == -1) continue;
		if(o->hdr.ChunkID == info.ControllerID) cont=(Controller *)o;
	}

	//recurse bind
	for(int j=0;j<info.nChildren;j++)
	{
		children[j].Bind(all_objects, n_obj);
	}
}

Bone * Bone::FindBone(int ID)
{
	if(ID == info.BoneID) 
		return this;
	else
	{
		for(int i=0;i<info.nChildren;i++)
		{
			Bone *res=children[i].FindBone(ID);
			if(res) return res;
		}
	}

	return NULL;
}

Bone * Bone::FindBone(char *name)
{
	if(BoneNames)
	{
		if(!strcmp(BoneNames->names[info.BoneID].name, name)) return this;
	}
		
	//recurse
	for(int i=0;i<info.nChildren;i++)
	{
		Bone *res=children[i].FindBone(name);
		if(res) return res;
	}

	return NULL;
}

RECT Bone::Paint(HDC hdc, DispOptions &opt)
{
	if(parent)
	{
		HGDIOBJ old_br=NULL;
		if(at_key) old_br=SelectObject(hdc,GetStockObject(WHITE_PEN));

		CryPoint3 pp	=parent->GetPos(opt.time, opt.Interpolate);
		CryPoint3 myp	=GetPos(opt.time, opt.Interpolate);

		CryPoint3 ppp= pp*opt.tm;
		CryPoint3 mpp= myp*opt.tm;


		MoveToEx(hdc,ppp.x, ppp.y, NULL);
		LineTo(hdc,  mpp.x, mpp.y);

		Rectangle(hdc,mpp.x-2,mpp.y-2,mpp.x+2,mpp.y+2);

		if(at_key) SelectObject(hdc,old_br);
	}

	for(int i=0;i<info.nChildren;i++)
	{
		children[i].Paint(hdc, opt);
	}

	return BaseObj::Paint(hdc,opt);
}

CryPoint3 Bone::GetPos(int time, bool interpolate)
{
	if(cont)
	{
					
		CryPoint3 p;
		p.x=cache_mat.data[12];
		p.y=cache_mat.data[13];
		p.z=cache_mat.data[14];

		return p;

	/*
		if(interpolate)
		{
			
			CryPoint3 p;
			p.x=cache_mat.data[12];
			p.y=cache_mat.data[13];
			p.z=cache_mat.data[14];

			return p;
		}
		//CryKey key=keys[time % nKeys];

		for(int i=0;i<nKeys;i++)
		{
			if(keys[i].time > time) break;
		}

		CryKey key=keys[i-1];
		
		//CryMatrix am;
		CryMatrix rm;
		//am.Build(key.absquat, key.abspos);
		rm.Build(key.relquat, key.relpos);
				
		return keys[i-1].abspos;
	*/
	}

	CryPoint3 p=CryPoint3(0.0f,0.0f,0.0f);
	return p;
}

void Bone::Pose(int time)
{
	if(!cont) return;

	CryQuat		q;
	CryPoint3	p;
	bool		at_key;
	if(cont->GetValue(time, q, p, &at_key)) return;


	if(parent)
	{
		CryMatrix relmat;
		relmat.Build(q,p);

		cache_mat.Multiply(parent->cache_mat, relmat);
	}
	else
	{
		CryMatrix relmat;
		relmat.Build(q,p);

		cache_mat.Build(q,p);
	}

	//recurse
	for(int i=0;i<info.nChildren;i++)
	{
		children[i].Pose(time);
	}
}
