/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


	#ifndef	__PLATFORM_IOS__
		#include <malloc.h>
	#endif
		#include "automation/automation_player.h"
		#include "automation/automation_source.h"
		#include "platform_config.h"

		using namespace GS::Automation;


//------------------------------------------------------------------------------
Motion				*Player::GetMotion(const char *id) const
{
	ListForeachPtr(Motion *, i, motions)
		if	(!String::Compare(i->name, id))
			return i;
	return NULL;
}
Motion				*Player::GetMotionFromIndex(uint index) const
{	return motions.ObjectAt(index);	}
bool				Player::AddMotion(Motion *motion)
{	return asbool(motions.Add(motion));	}
void				Player::RemoveMotion(Motion *motion)
{	motions.Remove(motion);	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Source				*Player::StartAutomation(Source *source, float _blend, AddSourceMode mode, float _weight)
{
	if	(!source)
		return NULL;

	// Source blend.
	switch (mode)
	{
		case SourceSet:	// Dispose of all current sources.
			ListForeachPtr(Source *, s, sources)
				s->Dispose(_blend);
			break;

		default: break;
	}

	// Blend in source.
	source->SetWeight(_weight, _blend);
	sources.Add(source);

	switch (source->GetType())
	{
		case Source::TypeMotion:
			source->relative = flags.IsSet(FlagRelativeMotion);
			break;

		default: break;
	}
	return source;
}
void				Player::Evaluate(const GS::Time &dt_time)
{
	if	(!sources.GetCount() || property_provider.IsNull())
		return;

	// Allocate weight table from stack.
	weight_table = (float *)alloca(sizeof(float) * MotionChannel::Last);
	for (int n = 0; n < MotionChannel::Last; ++n)
		weight_table[n] = 0;

	// Update sources.
	ListForeachPtr(Source *, s, sources)
	{
		s->Update(dt_time);
		if	(s->CanDispose())
			sources.Remove(s);
	}

	// Mix rotation.
	float w = 0;
	Quaternion q, source_q = property_provider->GetRotation();

	ListForeachPtr(Source *, s, sources)
		if	(s->GetWeight() > 0.01)
			if	(s->EvaluateRotation(source_q))
			{
				if	(w > 0.01)
						q = Quaternion::Slerp(w / (w + s->GetWeight()), source_q, q);
				else	q = source_q;

				w += s->GetWeight();
			}

	if	(w > 0)
		property_provider->SetRotation(q);

	// Mix sources.
	ListForeachPtr(Source *, s, sources)
		if	(s->GetWeight())
			s->Evaluate(*this);

	// Drop weight table.
	weight_table = NULL;
}
void				Player::BlendAutomatedProperty(MotionChannel::Type type, float _v, float _w)
{
	if	(!_w || !weight_table)
		return;

	float v, *w = &weight_table[(uint)type];

	if	(property_provider->GetProperty(type, v))
	{
		v = (v * *w + _v * _w) / (*w + _w);
		*w += _w;
		property_provider->SetProperty(type, v);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Player::IsAutomated() const
{	return asbool(sources.GetCount());	}
bool				Player::IsAutomationDone() const
{
	ListForeachPtr(Source *, s, GetSourceList())
		if	(!s->IsDone())
			return false;
	return true;
}
bool				Player::GetAutomationActive() const
{	return active;	}
void				Player::SetAutomationActive(bool _active)
{	active = _active;	}
void				Player::DisposeAutomationSources()
{	sources.Clear();	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Player::Player()
{
	active = true;
	weight_table = NULL;
}
Player::~Player()
{
	DisposeAutomationSources();
}
//------------------------------------------------------------------------------
