#include "animation.hpp"
#include <map>

struct Animation::Rep {
	int ref_cnt;

	typedef std::map<int, Quat> KeyList;

	KeyList scale_anim, rot_anim, pos_anim;

	Rep() : ref_cnt(1) {}

	Rep(const Rep& t) : ref_cnt(1), scale_anim(t.scale_anim), rot_anim(t.rot_anim), pos_anim(t.pos_anim) {}

	Vector getLinearValue(const KeyList& keys, float time) const
	{
		KeyList::const_iterator next, curr;

		//for( next=keys.begin();next!=keys.end() && time>=next->first;++next ){}
		next = keys.upper_bound((int)time);

		if (next == keys.begin())
			return next->second.v;
		curr = next;
		--curr;
		if (next == keys.end())
			return curr->second.v;

		float delta = (time - curr->first) / (next->first - curr->first);
		return (next->second.v - curr->second.v) * delta + curr->second.v;
	}

	Quat getSlerpValue(const KeyList& keys, float time) const
	{
		KeyList::const_iterator next, curr;

		//for( next=keys.begin();next!=keys.end() && time>=next->first;++next ){}
		next = keys.upper_bound((int)time);

		if (next == keys.begin())
			return next->second;
		curr = next;
		--curr;
		if (next == keys.end())
			return curr->second;

		float delta = (time - curr->first) / (next->first - curr->first);
		return curr->second.slerpTo(next->second, delta);
	}

	void setKey(KeyList& keys, int time, const Quat& value)
	{
		keys[time] = value;
	}
};

Animation::Animation() : rep(new Rep()) {}

Animation::Animation(const Animation& t) : rep(t.rep)
{
	++rep->ref_cnt;
}

Animation::Animation(const Animation& t, int first, int last) : rep(new Rep())
{
	Rep::KeyList::const_iterator it;
	for (it = t.rep->pos_anim.begin(); it != t.rep->pos_anim.end(); ++it) {
		if (it->first < first || it->first > last)
			continue;
		rep->setKey(rep->pos_anim, it->first - first, it->second);
	}
	for (it = t.rep->scale_anim.begin(); it != t.rep->scale_anim.end(); ++it) {
		if (it->first < first || it->first > last)
			continue;
		rep->setKey(rep->scale_anim, it->first - first, it->second);
	}
	for (it = t.rep->rot_anim.begin(); it != t.rep->rot_anim.end(); ++it) {
		if (it->first < first || it->first > last)
			continue;
		rep->setKey(rep->rot_anim, it->first - first, it->second);
	}
}

Animation::~Animation()
{
	if (!--rep->ref_cnt)
		delete rep;
}

Animation& Animation::operator=(const Animation& t)
{
	++t.rep->ref_cnt;
	if (!--rep->ref_cnt)
		delete rep;
	rep = t.rep;
	return *this;
}

Animation::Rep* Animation::write()
{
	if (rep->ref_cnt > 1) {
		--rep->ref_cnt;
		rep = new Rep(*rep);
	}
	return rep;
}

void Animation::setScaleKey(int time, const Vector& q)
{
	write();
	rep->setKey(rep->scale_anim, time, Quat(0, q));
}

void Animation::setPositionKey(int time, const Vector& q)
{
	write();
	rep->setKey(rep->pos_anim, time, Quat(0, q));
}

void Animation::setRotationKey(int time, const Quat& q)
{
	write();
	rep->setKey(rep->rot_anim, time, q);
}

int Animation::numScaleKeys() const
{
	return rep->scale_anim.size();
}

int Animation::numRotationKeys() const
{
	return rep->rot_anim.size();
}

int Animation::numPositionKeys() const
{
	return rep->pos_anim.size();
}

Vector Animation::getScale(float time) const
{
	if (!rep->scale_anim.size())
		return Vector(1, 1, 1);
	return rep->getLinearValue(rep->scale_anim, time);
}

Vector Animation::getPosition(float time) const
{
	if (!rep->pos_anim.size())
		return Vector(0, 0, 0);
	return rep->getLinearValue(rep->pos_anim, time);
}

Quat Animation::getRotation(float time) const
{
	if (!rep->rot_anim.size())
		return Quat();
	return rep->getSlerpValue(rep->rot_anim, time);
}

/*
struct Animation::Rep{

	int ref_cnt;

	struct Key{
		int time;
		Quat value;
	};
	typedef list<Key> KeyList;

	KeyList scale_anim,rot_anim,pos_anim;

	Rep():
	ref_cnt(1){
	}

	Rep( const Rep &t ):
	ref_cnt(1),
	scale_anim(t.scale_anim),rot_anim(t.rot_anim),pos_anim(t.pos_anim){
	}

	Vector getLinearValue( const KeyList &keys,float time )const{
		if( keys.size()==1 ) return keys.front().value.v;
		if( time>=keys.back().time ) return keys.back().value.v;
		if( time<=keys.front().time ) return keys.front().value.v;
		KeyList::const_iterator it;
		for( it=keys.begin();time>=it->time;++it ){}
		const Key *next=&*it;
		const Key *curr=&*--it;
		float delta=( time-curr->time )/( next->time-curr->time );
		return (next->value.v-curr->value.v)*delta+curr->value.v;
	}

	Quat getSlerpValue( const KeyList &keys,float time )const{
		if( keys.size()==1 ) return keys.front().value;
		if( time>=keys.back().time ) return keys.back().value;
		if( time<=keys.front().time ) return keys.front().value;
		KeyList::const_iterator it;
		for( it=keys.begin();time>=it->time;++it ){}
		const Key *next=&*it;
		const Key *curr=&*--it;
		float delta=(time-curr->time)/(next->time-curr->time);
		return curr->value.slerpTo( next->value,delta );
	}

	void setKey( KeyList &keys,int time,const Quat &value ){
		KeyList::iterator it;
		for( it=keys.begin();it!=keys.end() && time>it->time;++it ){}
		if( it==keys.end() || time<it->time ){
			it=keys.insert( it );
			it->time=time;
		}
		it->value=value;
	}
};

Animation::Animation():
rep( new Rep() ){
}

Animation::Animation( const Animation &t ):
rep( t.rep ){
	++rep->ref_cnt;
}

Animation::Animation( const Animation &t,int first,int last ):
rep( new Rep() ){
	Rep::KeyList::const_iterator it;
	for( it=t.rep->pos_anim.begin();it!=t.rep->pos_anim.end();++it ){
		const Rep::Key &key=*it;
		if( key.time<first || key.time>last ) continue;
		rep->setKey( rep->pos_anim,key.time-first,key.value );
	}
	for( it=t.rep->scale_anim.begin();it!=t.rep->scale_anim.end();++it ){
		const Rep::Key &key=*it;
		if( key.time<first || key.time>last ) continue;
		rep->setKey( rep->scale_anim,key.time-first,key.value );
	}
	for( it=t.rep->rot_anim.begin();it!=t.rep->rot_anim.end();++it ){
		const Rep::Key &key=*it;
		if( key.time<first || key.time>last ) continue;
		rep->setKey( rep->rot_anim,key.time-first,key.value );
	}
}

Animation::~Animation(){
	if( !--rep->ref_cnt ) delete rep;
}

Animation &Animation::operator=( const Animation &t ){
	++t.rep->ref_cnt;
	if( !--rep->ref_cnt ) delete rep;
	rep=t.rep;
	return *this;
}

Animation::Rep *Animation::write(){
	if( rep->ref_cnt>1 ){
		--rep->ref_cnt;
		rep=new Rep( *rep );
	}
	return rep;
}

void Animation::setScaleKey( int time,const Vector &q ){
	write();
	rep->setKey( rep->scale_anim,time,Quat( 0,q ) );
}

void Animation::setPositionKey( int time,const Vector &q ){
	write();
	rep->setKey( rep->pos_anim,time,Quat( 0,q ) );
}

void Animation::setRotationKey( int time,const Quat &q ){
	write();
	rep->setKey( rep->rot_anim,time,q );
}

int Animation::numScaleKeys()const{
	return rep->scale_anim.size();
}

int Animation::numRotationKeys()const{
	return rep->rot_anim.size();
}

int Animation::numPositionKeys()const{
	return rep->pos_anim.size();
}

Vector Animation::getScale( float time )const{
	if( !rep->scale_anim.size() ) return Vector(1,1,1);
	return rep->getLinearValue( rep->scale_anim,time );
}

Vector Animation::getPosition( float time )const{
	if( !rep->pos_anim.size() ) return Vector(0,0,0);
	return rep->getLinearValue( rep->pos_anim,time );
}

Quat Animation::getRotation( float time )const{
	if( !rep->rot_anim.size() ) return Quat();
	return rep->getSlerpValue( rep->rot_anim,time );
}
*/
