
#include "PajaTypes.h"
#include "ControllerC.h"
#include "DataBlockI.h"
#include "EditableI.h"
#include "UndoC.h"
#include "KeyC.h"
#include <math.h>
#include <assert.h>
#include <stdlib.h>		//qsort
#include "FileIO.h"


using namespace Composition;
using namespace Edit;
using namespace PajaTypes;
using namespace FileIO;
using namespace Import;



const uint32		CONTROLLER_VERSION = 1;

enum ControllerChunksE {
	CHUNK_CONTROLLER_BASE = 0x1000,
	CHUNK_CONTROLLER_FLOAT_KEY = 0x2000,
	CHUNK_CONTROLLER_FILE_KEY = 0x3000,
};



ControllerC::ControllerC( ParamI* pParent, ControllerTypeE eType ) :
	m_pParent( pParent ),
	m_ui32StartOrt( CONT_ORT_CONSTANT ),
	m_ui32EndOrt( CONT_ORT_CONSTANT ),
	m_ui32NumChannels( 0 )
{
	set_type( eType );
}

ControllerC::ControllerC( EditableI* pOriginal ) :
	EditableI( pOriginal ),
	m_pParent( 0 ),
	m_ui32StartOrt( CONT_ORT_CONSTANT ),
	m_ui32EndOrt( CONT_ORT_CONSTANT ),
	m_ui32NumChannels( 0 )
{
	// empty
}

ControllerC::~ControllerC()
{
	// If this is a clone, bail out.
	if( get_original() )
		return;

	// delete keys
	for( uint32 i = 0; i < m_rKeys.size(); i++ )
		m_rKeys[i]->release();
}



ControllerC*
ControllerC::create_new( ParamI* pParent, ControllerTypeE eType )
{
	return new ControllerC( pParent, eType );
}

DataBlockI*
ControllerC::create()
{
	return new ControllerC( m_pParent, CONT_TYPE_NONE );
}

DataBlockI*
ControllerC::create( EditableI* pOriginal )
{
	return new ControllerC( pOriginal );
}

void
ControllerC::copy( EditableI* pEditable )
{
	ControllerC*	pCont = (ControllerC*)pEditable;
	uint32			i;

	m_ui32StartOrt = pCont->m_ui32StartOrt;
	m_ui32EndOrt = pCont->m_ui32EndOrt;
	m_ui32NumChannels = pCont->m_ui32NumChannels;
	m_ui32Type = pCont->m_ui32Type;

	// remove old keys
	for( i = 0; i < m_rKeys.size(); i++ )
		m_rKeys[i]->release();
	m_rKeys.clear();

	// add new keys
	for( i = 0; i < pCont->m_rKeys.size(); i++ )
		m_rKeys.push_back( (KeyC*)pCont->m_rKeys[i]->duplicate() );
}

void
ControllerC::restore( EditableI* pEditable )
{
	ControllerC*	pCont = (ControllerC*)pEditable;

	// guard that different channels data isnt mixed.
//	assert( m_ui32NumChannels != pCont->m_ui32NumChannels );

	m_ui32StartOrt = pCont->m_ui32StartOrt;
	m_ui32EndOrt = pCont->m_ui32EndOrt;
	m_rKeys = pCont->m_rKeys;
	m_ui32Type = pCont->m_ui32Type;

	// Restoring, notify parent that we have changed.
	if( !get_original() )
	{
		if( m_pParent )
			m_pParent->update_notify( this );
	}
}

PluginClass::SuperClassIdC
ControllerC::get_base_class_id() const
{
	return BASECLASS_CONTROLLER;
}

int32
ControllerC::get_key_count()
{
	return m_rKeys.size();
}

KeyC*
ControllerC::get_key( int32 i32Index )
{
	if( i32Index >= 0 && i32Index < (int32)m_rKeys.size() )
		return m_rKeys[i32Index];
	return 0;
}

void
ControllerC::del_key( int32 i32Index )
{
	assert( i32Index >= 0 && i32Index < (int32)m_rKeys.size() );

	KeyC*	pKey = m_rKeys[i32Index];
	m_rKeys.erase( m_rKeys.begin() + i32Index );
	if( get_undo() )
		get_undo()->add_discardable_data( pKey, DATA_REMOVED );
}

KeyC*
ControllerC::get_key_at_time( int32 i32Time )
{
	for( uint32 i = 0; i < m_rKeys.size(); i++ )
		if( m_rKeys[i]->get_time() == i32Time )
			return m_rKeys[i];
	return 0;
}

KeyC*
ControllerC::add_key_at_time( int32 i32Time )
{
	KeyC*	pKey;

	if( m_ui32Type == CONT_TYPE_FILE ) {

		pKey = (KeyC*)FileKeyC::create_new();

		FileKeyC*	pCurKey = 0;

		// Is the requested time before the first key?
		if( m_rKeys.size() ) {
			if( m_rKeys[0]->get_time() >= i32Time ) {
				pCurKey = (FileKeyC*)m_rKeys[0];
			}

			// Is the requested time after the last key?
			if( m_rKeys[(int32)m_rKeys.size() - 1]->get_time() <= i32Time ) {
				pCurKey = (FileKeyC*)m_rKeys[(int32)m_rKeys.size() - 1];
			}

			// interpolate
			for( int32 i = 1; i < (int32)m_rKeys.size(); i++ ) {
				if( m_rKeys[i]->get_time() > i32Time ) {
					pCurKey = (FileKeyC*)m_rKeys[i - 1];
					break;
				}
			}
		}
		//return (int32)((float32)(i32Duration + m_i32TimeOffset) * m_f32TimeScale);

		float32			f32TimeScale = 1.0f;
		int32			i32TimeOffset = 0;
		FileHandleC*	pHandle = 0;

		if( pCurKey ) {
			pHandle = pCurKey->get_file_handle();
			f32TimeScale = pCurKey->get_time_scale();
		}

		((FileKeyC*)pKey)->set_file_handle( pHandle );
		((FileKeyC*)pKey)->set_time_scale( f32TimeScale );
		((FileKeyC*)pKey)->set_time_offset( i32TimeOffset );
	}
	else {
		pKey = (KeyC*)FloatKeyC::create_new( m_ui32NumChannels );
		float32	f32Values[KEY_MAXCHANNELS];
		get_value( f32Values, i32Time );
		((FloatKeyC*)pKey)->set_value( f32Values );
	}

	pKey->set_time( i32Time );

	switch( m_ui32Type ) {
	case CONT_TYPE_INT: pKey->set_flags( KEY_HOLD ); break;
	case CONT_TYPE_FILE: pKey->set_flags( KEY_FILE ); break;
	default: pKey->set_flags( KEY_SMOOTH ); break;
	}

	m_rKeys.push_back( pKey );
	sort_keys();
	prepare();

	if( get_undo() )
		get_undo()->add_discardable_data( pKey, DATA_CREATED );

	return pKey;
}


static
int
compare_func( const void* vpParam1, const void* vpParam2 )
{
	KeyC*	pKey1 = *(KeyC**)vpParam1;
	KeyC*	pKey2 = *(KeyC**)vpParam2;

	if( pKey1->get_time() < pKey2->get_time() )
		return -1;

	return 1;
}

void
ControllerC::sort_keys()
{
	if( m_rKeys.size() )
		qsort( &m_rKeys[0], m_rKeys.size(), sizeof( KeyC* ), compare_func );
}



float32
ControllerC::ease( float32 f32U, float32 f32A, float32 f32B ) 
{
	float32	f32K;
	float32	f32S = f32A + f32B;
	
	if( f32U == 0.0f || f32U == 1.0f ) return f32U;
	if( f32S == 0.0 ) return f32U;
	if( f32S > 1.0f ) {
		f32A = f32A / f32S;
		f32B = f32B / f32S;
	}
	f32K = 1.0f / (2.0f - f32A - f32B);
	if( f32U < f32A )
		return ((f32K / f32A) * f32U * f32U);
	else if( f32U < 1.0f - f32B )
		return (f32K * (2.0f * f32U - f32A));
	else {
		f32U = 1.0f - f32U;
		return (1.0f - (f32K / f32B) * f32U * f32U);
	}
}

void
ControllerC::compute_hermite_basis( float f32U, float* pV ) 
{
	float32	f32U2, f32U3, f32A;
	
	f32U2 = f32U * f32U;
	f32U3 = f32U2 * f32U;
	f32A  = 2.0f * f32U3 - 3.0f * f32U2;
	pV[0] = 1.0f + f32A;
	pV[1] = -f32A;
	pV[2] = f32U - 2.0f * f32U2 + f32U3;
	pV[3] = -f32U2 + f32U3;
}

void
ControllerC::comp_first_deriv( uint32 ui32CurIndex, uint32 ui32NextIndex )
{
	float32	f32T;

	f32T = 0.5f * (1.0f - ((FloatKeyC*)m_rKeys[ui32CurIndex])->get_tens());

	float32	f32CurOutTan[KEY_MAXCHANNELS];
	float32	f32CurValue[KEY_MAXCHANNELS];
	float32	f32NextValue[KEY_MAXCHANNELS];
	float32	f32NextInTan[KEY_MAXCHANNELS];

	((FloatKeyC*)m_rKeys[ui32CurIndex])->get_value( f32CurValue );
	((FloatKeyC*)m_rKeys[ui32NextIndex])->get_value( f32NextValue );
	((FloatKeyC*)m_rKeys[ui32NextIndex])->get_in_tan( f32NextInTan );

	for( uint32 i = 0; i < m_ui32NumChannels; i++ ) {
		f32CurOutTan[i] = f32T * (3.0f * (f32NextValue[i] - f32CurValue[i]) - f32NextInTan[i]);
	}

	((FloatKeyC*)m_rKeys[ui32CurIndex])->set_out_tan( f32CurOutTan );
}

void
ControllerC::comp_last_deriv( uint32 ui32CurIndex, uint32 ui32NextIndex )
{
	float32	f32T;

	f32T = 0.5f * (1.0f - ((FloatKeyC*)m_rKeys[ui32NextIndex])->get_tens());

	float32	f32CurOutTan[KEY_MAXCHANNELS];
	float32	f32CurValue[KEY_MAXCHANNELS];
	float32	f32NextValue[KEY_MAXCHANNELS];
	float32	f32NextInTan[KEY_MAXCHANNELS];

	((FloatKeyC*)m_rKeys[ui32CurIndex])->get_value( f32CurValue );
	((FloatKeyC*)m_rKeys[ui32CurIndex])->get_out_tan( f32CurOutTan );
	((FloatKeyC*)m_rKeys[ui32NextIndex])->get_value( f32NextValue );

	for( uint32 i = 0; i < m_ui32NumChannels; i++ ) {
		f32NextInTan[i] = -f32T * (3.0f * (f32CurValue[i] - f32NextValue[i]) + f32CurOutTan[i]);
	}

	((FloatKeyC*)m_rKeys[ui32NextIndex])->set_in_tan( f32NextInTan );
}

void
ControllerC::comp_2key_deriv( uint32 ui32CurIndex, uint32 ui32NextIndex )
{
	float32	f32Tens0, f32Tens1;

	f32Tens0 = 1.0f - ((FloatKeyC*)m_rKeys[ui32CurIndex])->get_tens();
	f32Tens1 = 1.0f - ((FloatKeyC*)m_rKeys[ui32NextIndex])->get_tens();

	float32	f32CurValue[KEY_MAXCHANNELS];
	float32	f32NextValue[KEY_MAXCHANNELS];
	float32	f32CurOutTan[KEY_MAXCHANNELS];
	float32	f32NextInTan[KEY_MAXCHANNELS];

	((FloatKeyC*)m_rKeys[ui32CurIndex])->get_value( f32CurValue );
	((FloatKeyC*)m_rKeys[ui32NextIndex])->get_value( f32NextValue );

	for( uint32 i = 0; i < m_ui32NumChannels; i++ ) {
		f32CurOutTan[i] = f32Tens0 * (f32NextValue[i] - f32CurValue[i]);
		f32NextInTan[i] = f32Tens1 * (f32NextValue[i] - f32CurValue[i]);
	}

	((FloatKeyC*)m_rKeys[ui32CurIndex])->set_out_tan( f32CurOutTan );
	((FloatKeyC*)m_rKeys[ui32NextIndex])->set_in_tan( f32NextInTan );
}

void
ControllerC::comp_middle_deriv( uint32 ui32PrevIndex, uint32 ui32Index, uint32 ui32NextIndex )
{
	float32	tm, cm, cp, bm, bp, tmcm, tmcp, ksm, ksp, kdm, kdp, c;
	float32	dt, fp, fn;

	int32		i32PrevTime = m_rKeys[ui32PrevIndex]->get_time();
	int32		i32Time = m_rKeys[ui32Index]->get_time();
	int32		i32NextTime = m_rKeys[ui32NextIndex]->get_time();

	float32	f32PrevValue[KEY_MAXCHANNELS];
	float32	f32CurValue[KEY_MAXCHANNELS];
	float32	f32NextValue[KEY_MAXCHANNELS];
	float32	f32CurOutTan[KEY_MAXCHANNELS];
	float32	f32CurInTan[KEY_MAXCHANNELS];

	((FloatKeyC*)m_rKeys[ui32PrevIndex])->get_value( f32PrevValue );
	((FloatKeyC*)m_rKeys[ui32Index])->get_value( f32CurValue );
	((FloatKeyC*)m_rKeys[ui32NextIndex])->get_value( f32NextValue );


	// handle time in loop case
	if( i32NextTime <= i32PrevTime )
		i32NextTime += (m_rKeys[m_rKeys.size() - 1]->get_time() - m_rKeys[0]->get_time());
	if( i32PrevTime >= i32Time )
		i32PrevTime -= (m_rKeys[m_rKeys.size() - 1]->get_time() - m_rKeys[0]->get_time());

	// fp,fn apply speed correction when continuity is 0.0
	dt = 0.5f * (float32)(i32NextTime - i32PrevTime);
	fp = ((float32)(i32Time - i32PrevTime)) / dt;
	fn = ((float32)(i32NextTime - i32Time)) / dt;
	
	c  = (float32)fabs( ((FloatKeyC*)m_rKeys[ui32Index])->get_cont() );
	fp = fp + c - c * fp;
	fn = fn + c - c * fn;
	cm = 1.0f - ((FloatKeyC*)m_rKeys[ui32Index])->get_cont();
	tm = 0.5f * (1.0f - ((FloatKeyC*)m_rKeys[ui32Index])->get_tens());
	cp = 2.0f - cm;
	bm = 1.0f - ((FloatKeyC*)m_rKeys[ui32Index])->get_bias();
	bp = 2.0f - bm;
	tmcm = tm * cm;			tmcp = tm * cp;
	ksm = tmcm * bp * fp;	ksp = tmcp * bm * fp;
	kdm = tmcp * bp * fn; 	kdp = tmcm * bm * fn;
	
	for( uint32 i = 0; i < m_ui32NumChannels; i++ ) {
		float32	delm = f32CurValue[i] - f32PrevValue[i];
		float32	delp = f32NextValue[i] - f32CurValue[i];
		f32CurInTan[i] = ksm * delm + ksp * delp;
		f32CurOutTan[i] = kdm * delm + kdp * delp;
	}

	((FloatKeyC*)m_rKeys[ui32Index])->set_in_tan( f32CurInTan );
	((FloatKeyC*)m_rKeys[ui32Index])->set_out_tan( f32CurOutTan );
}


void
ControllerC::prepare()
{
	if( m_rKeys.size() < 2 )
		return;

	if( m_ui32Type != CONT_TYPE_FILE ) {
		// save old state
		if( get_undo() ) {
			for( uint32 i = 0; i < m_rKeys.size(); i++ ) {
				UndoC*	pOldUndo = m_rKeys[i]->begin_editing( get_undo() );
				m_rKeys[i]->end_editing( pOldUndo );
			}
		}

		if( m_rKeys.size() == 2 && (m_rKeys[0]->get_flags() & KEY_SMOOTH) &&
			(m_rKeys[1]->get_flags() & KEY_SMOOTH) ) {
			comp_2key_deriv( 0, 1 );
		}
		else {
			uint32	i;

			// comp middle derivates
			for( i = 1; i < m_rKeys.size() - 1; i++ ) {
				if( m_rKeys[i]->get_flags() & KEY_SMOOTH )
					comp_middle_deriv( i - 1, i, i + 1 );
			}

			// comp first derivates
			for( i = 0; i < m_rKeys.size() - 1; i++ ) {
				if( !(m_rKeys[i]->get_flags() & KEY_SMOOTH) && m_rKeys[i + 1]->get_flags() & KEY_SMOOTH )
					comp_first_deriv( i, i + 1 );
			}

			if( m_ui32StartOrt == CONT_ORT_LOOP ) {
				if( m_rKeys[0]->get_flags() & KEY_SMOOTH )
					comp_middle_deriv( m_rKeys.size() - 2, 0, 1 );
			}
			else {
				if( m_rKeys[0]->get_flags() & KEY_SMOOTH )
					comp_first_deriv( 0, 1 );
			}

			// comp last derivates
			for( i = 1; i < m_rKeys.size(); i++ ) {
				if( !(m_rKeys[i]->get_flags() & KEY_SMOOTH) && m_rKeys[i - 1]->get_flags() & KEY_SMOOTH )
					comp_last_deriv( i - 1, i );
			}

			if( m_ui32EndOrt == CONT_ORT_LOOP ) {
				if( m_rKeys[m_rKeys.size() - 1]->get_flags() & KEY_SMOOTH )
					comp_middle_deriv( m_rKeys.size() - 2, m_rKeys.size() - 1, 1 );
			}
			else {
				if( m_rKeys[m_rKeys.size() - 1]->get_flags() & KEY_SMOOTH )
					comp_last_deriv( m_rKeys.size() - 2, m_rKeys.size() - 1 );
			}
		}
	}
}


void
ControllerC::set_start_ort( uint32 ui32Ort )
{
	m_ui32StartOrt = ui32Ort;
}

void
ControllerC::set_end_ort( uint32 ui32Ort )
{
	m_ui32EndOrt = ui32Ort;
}

uint32
ControllerC::get_start_ort()
{
	return m_ui32StartOrt;
}

uint32
ControllerC::get_end_ort()
{
	return m_ui32EndOrt;
}



void
ControllerC::copy_value( float32* pOut, float32* pIn, uint32 ui32NumCh )
{
	for( uint32 i = 0; i < ui32NumCh; i++ )
		pOut[i] = pIn[i];
}



void
ControllerC::get_value( FileHandleC*& pHandle, int32& i32FileTime, int32 i32Time )
{

	if( m_rKeys.size() == 0 ) {
		pHandle = 0;
		i32FileTime = 0;
		return;
	}

	if( m_rKeys.size() == 1 ) {
		pHandle = ((FileKeyC*)m_rKeys[0])->get_file_handle();
		i32FileTime = (int32)((float32)(i32Time - m_rKeys[0]->get_time() + ((FileKeyC*)m_rKeys[0])->get_time_offset()) * ((FileKeyC*)m_rKeys[0])->get_time_scale());
		return;
	}

	int32	i32StartTime = get_min_time();
	int32	i32EndTime = get_max_time();

	// Handle out of range
	if( i32Time < i32StartTime ) {
		if( m_ui32StartOrt == CONT_ORT_REPEAT || m_ui32StartOrt == CONT_ORT_LOOP )
			i32Time = i32EndTime + (i32Time - i32StartTime) % (i32EndTime - i32StartTime);
		else {
			pHandle = ((FileKeyC*)m_rKeys[0])->get_file_handle();
			i32FileTime = (int32)((float32)(i32Time - m_rKeys[0]->get_time() + ((FileKeyC*)m_rKeys[0])->get_time_offset()) * ((FileKeyC*)m_rKeys[0])->get_time_scale());
			return;
		}
	}
	else if( i32Time >= i32EndTime ) {

		if( m_ui32EndOrt == CONT_ORT_REPEAT || m_ui32EndOrt == CONT_ORT_LOOP )
			i32Time = i32StartTime + (i32Time - i32StartTime) % (i32EndTime - i32StartTime);
		else {
			pHandle = ((FileKeyC*)m_rKeys[m_rKeys.size() - 1])->get_file_handle();
			i32FileTime = (int32)((float32)(i32Time - m_rKeys[m_rKeys.size() - 1]->get_time() + ((FileKeyC*)m_rKeys[m_rKeys.size() - 1])->get_time_offset()) * ((FileKeyC*)m_rKeys[m_rKeys.size() - 1])->get_time_scale());
			return;
		}
	}

	// Check if the value is out of range

	FileKeyC*	pKey;

	// Is the requested time before the first key?
	
	pKey = (FileKeyC*)m_rKeys[0];
	if( pKey->get_time() >= i32Time ) {
		pHandle = pKey->get_file_handle();
		i32FileTime = (int32)((float32)(i32Time - pKey->get_time() + pKey->get_time_offset()) * pKey->get_time_scale());
		return;
	}

	// Is the requested time after the last key?
	pKey = (FileKeyC*)m_rKeys[(int32)m_rKeys.size() - 1];
	if( pKey->get_time() <= i32Time ) {
		pHandle = pKey->get_file_handle();
		i32FileTime = (int32)((float32)(i32Time - pKey->get_time() + pKey->get_time_offset()) * pKey->get_time_scale());
		return;
	}

	// interpolate
	for( uint32 i = 1; i < m_rKeys.size(); i++ ) {
		if( m_rKeys[i]->get_time() > i32Time ) {
			pKey = (FileKeyC*)m_rKeys[i - 1];
			pHandle = pKey->get_file_handle();
			i32FileTime = (int32)((float32)(i32Time - pKey->get_time() + pKey->get_time_offset()) * pKey->get_time_scale());
			return;
		}
	}

	// we never should arrive here!
	assert( 0 );
}


void
ControllerC::get_value( PajaTypes::float32* pValues, PajaTypes::int32 i32Time )
{
	float32	f32CurVal[KEY_MAXCHANNELS];

	if( m_rKeys.size() == 0 ) {
		for( uint32 i = 0; i < m_ui32NumChannels; i++ )
			pValues[i] = 0;
		return;
	}

	if( m_rKeys.size() == 1 ) {
		((FloatKeyC*)m_rKeys[0])->get_value( f32CurVal );
		copy_value( pValues, f32CurVal, m_ui32NumChannels );
		return;
	}

	int32	i32StartTime = get_min_time();
	int32	i32EndTime = get_max_time();

	// Handle out of range
	if( i32Time < i32StartTime ) {
		if( m_ui32StartOrt == CONT_ORT_REPEAT || m_ui32StartOrt == CONT_ORT_LOOP )
			i32Time = i32EndTime + (i32Time - i32StartTime) % (i32EndTime - i32StartTime);
		else {
			((FloatKeyC*)m_rKeys[0])->get_value( f32CurVal );
			copy_value( pValues, f32CurVal, m_ui32NumChannels );
			return;
		}
	}
	else if( i32Time >= i32EndTime ) {

		if( m_ui32EndOrt == CONT_ORT_REPEAT || m_ui32EndOrt == CONT_ORT_LOOP )
			i32Time = i32StartTime + (i32Time - i32StartTime) % (i32EndTime - i32StartTime);
		else {
			((FloatKeyC*)m_rKeys[m_rKeys.size() - 1])->get_value( f32CurVal );
			copy_value( pValues, f32CurVal, m_ui32NumChannels );
			return;
		}
	}

	// Check if the value is out of range

	// Is the requested time before the first key?
	if( m_rKeys[0]->get_time() >= i32Time ) {
		((FloatKeyC*)m_rKeys[0])->get_value( f32CurVal );
		copy_value( pValues, f32CurVal, m_ui32NumChannels );
		return;
	}

	// Is the requested time after the last key?
	if( m_rKeys[m_rKeys.size() - 1]->get_time() <= i32Time ) {
		((FloatKeyC*)m_rKeys[m_rKeys.size() - 1])->get_value( f32CurVal );
		copy_value( pValues, f32CurVal, m_ui32NumChannels );
		return;
	}

	// interpolate
	for( uint32 i = 1; i < m_rKeys.size(); i++ ) {
		if( m_rKeys[i]->get_time() > i32Time ) {

			if( m_rKeys[i - 1]->get_flags() & KEY_HOLD ) {
				// A hold segment.
				((FloatKeyC*)m_rKeys[i - 1])->get_value( f32CurVal );
				copy_value( pValues, f32CurVal, m_ui32NumChannels );
				return;
			}
			else  {
				float32	f32U = float( i32Time - m_rKeys[i - 1]->get_time() ) /
								float( m_rKeys[i]->get_time() - m_rKeys[i - 1]->get_time() );

				f32U = ease( f32U, ((FloatKeyC*)m_rKeys[i - 1])->get_ease_out(), ((FloatKeyC*)m_rKeys[i])->get_ease_in() );

				float32	f32PrevVal[KEY_MAXCHANNELS];
				((FloatKeyC*)m_rKeys[i])->get_value( f32CurVal );
				((FloatKeyC*)m_rKeys[i - 1])->get_value( f32PrevVal );

				// Calc value
				if( ((m_rKeys[i - 1]->get_flags() & KEY_LINEAR) && (m_rKeys[i]->get_flags() & KEY_LINEAR)) ||
					((m_rKeys[i - 1]->get_flags() & KEY_LINEAR) && (m_rKeys[i]->get_flags() & KEY_HOLD)) ) {

					// A linear segment.
					for( uint32 j = 0; j < m_ui32NumChannels; j++ )
						pValues[j] = f32PrevVal[j] + (f32CurVal[j] - f32PrevVal[j]) * f32U;
				}
				else {
					// A Smooth segment
					float32	f32CurInTan[KEY_MAXCHANNELS];
					float32	f32PrevOutTan[KEY_MAXCHANNELS];

					((FloatKeyC*)m_rKeys[i])->get_in_tan( f32CurInTan );
					((FloatKeyC*)m_rKeys[i - 1])->get_out_tan( f32PrevOutTan );

					float32	f32V[4];
					compute_hermite_basis( f32U, f32V );

					for( uint32 j = 0; j < m_ui32NumChannels; j++ )
						pValues[j] = f32PrevVal[j] * f32V[0] + f32CurVal[j] * f32V[1] +
										f32PrevOutTan[j] * f32V[2] + f32CurInTan[j] * f32V[3];
				}

				return;
			}
		}
	}

	// we never should arrive here!
	assert( 0 );
}

/*
float32
ControllerC::get_min_val()
{
	float32		f32Min = 0;
	float32		f32Values[KEY_MAXCHANNELS];

	if( m_rKeys.size() && m_ui32NumChannels ) {
		m_rKeys[0]->get_value( f32Values );
		f32Min = f32Values[0];

		for( uint32 i = 0; i < m_rKeys.size(); i++ ) {
			m_rKeys[i]->get_value( f32Values );
			for( uint32 j = 0; j < m_ui32NumChannels; j++ )
				if( f32Values[j] < f32Min )
					f32Min = f32Values[j];
		}
	}
	return f32Min;
}

float32
ControllerC::get_max_val()
{
	float32		f32Max = 0;
	float32		f32Values[KEY_MAXCHANNELS];

	if( m_rKeys.size() && m_ui32NumChannels ) {
		m_rKeys[0]->get_value( f32Values );
		f32Max = f32Values[0];

		for( uint32 i = 0; i < m_rKeys.size(); i++ ) {
			m_rKeys[i]->get_value( f32Values );
			for( uint32 j = 0; j < m_ui32NumChannels; j++ )
				if( f32Values[j] > f32Max )
					f32Max = f32Values[j];
		}
	}
	return f32Max;
}
*/

int32
ControllerC::get_min_time()
{
	if( m_rKeys.size() )
		return m_rKeys[0]->get_time();
	return 0;
}

int32
ControllerC::get_max_time()
{
	if( m_rKeys.size() )
		return m_rKeys[m_rKeys.size() - 1]->get_time();
	return 0;
}

uint32
ControllerC::get_type()
{
	return m_ui32Type;
}

uint32
ControllerC::get_num_channels()
{
	return m_ui32NumChannels;
}

void
ControllerC::set_parent( ParamI* pParent )
{
	m_pParent = pParent;
}

ParamI*
ControllerC::get_parent()
{
	return m_pParent;
}

void
ControllerC::set_type( PajaTypes::uint32 ui32Type )
{
	switch( ui32Type ) {
	case CONT_TYPE_NONE:	m_ui32NumChannels = 0; break;
	case CONT_TYPE_INT:		m_ui32NumChannels = 1; break;
	case CONT_TYPE_FLOAT:	m_ui32NumChannels = 1; break;
	case CONT_TYPE_VECTOR2:	m_ui32NumChannels = 2; break;
	case CONT_TYPE_VECTOR3:	m_ui32NumChannels = 3; break;
	case CONT_TYPE_COLOR:	m_ui32NumChannels = 4; break;
	case CONT_TYPE_FILE:	m_ui32NumChannels = 0; break;
	}

	m_ui32Type = ui32Type;
}

void
ControllerC::get_value( int32& i32Val, int32 i32Time )
{
	float32	f32Values[KEY_MAXCHANNELS];
	get_value( f32Values, i32Time );
	i32Val = (int32)f32Values[0];
}

void
ControllerC::get_value( float32& f32Val, int32 i32Time )
{
	float32	f32Values[KEY_MAXCHANNELS];
	get_value( f32Values, i32Time );
	f32Val = f32Values[0];
}

void
ControllerC::get_value( Vector2C& rVal, int32 i32Time )
{
	float32	f32Values[KEY_MAXCHANNELS];
	get_value( f32Values, i32Time );
	rVal[0] = f32Values[0];
	rVal[1] = f32Values[1];
}

void
ControllerC::get_value( Vector3C& rVal, int32 i32Time )
{
	float32	f32Values[KEY_MAXCHANNELS];
	get_value( f32Values, i32Time );
	rVal[0] = f32Values[0];
	rVal[1] = f32Values[1];
	rVal[2] = f32Values[2];
}

void
ControllerC::get_value( ColorC& rVal, int32 i32Time )
{
	float32	f32Values[KEY_MAXCHANNELS];
	get_value( f32Values, i32Time );
	rVal[0] = f32Values[0];
	rVal[1] = f32Values[1];
	rVal[2] = f32Values[2];
	rVal[3] = f32Values[3];
}

uint32
ControllerC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	uint8	ui8Temp;

	pSave->begin_chunk( CHUNK_CONTROLLER_BASE, CONTROLLER_VERSION );
		// channel count
		ui8Temp = (uint8)m_ui32NumChannels;
		ui32Error = pSave->write( &ui8Temp, sizeof( ui8Temp ) );
		// type
		ui8Temp = (uint8)m_ui32Type;
		ui32Error = pSave->write( &ui8Temp, sizeof( ui8Temp ) );
		// start ort
		ui8Temp = (uint8)m_ui32StartOrt;
		ui32Error = pSave->write( &ui8Temp, sizeof( ui8Temp ) );
		// end ort
		ui8Temp = (uint8)m_ui32EndOrt;
		ui32Error = pSave->write( &ui8Temp, sizeof( ui8Temp ) );
	pSave->end_chunk();

	// save keys

	for( uint32 i = 0; i < m_rKeys.size(); i++ ) {

		if( m_rKeys[i]->get_flags() & KEY_FILE ) {
			pSave->begin_chunk( CHUNK_CONTROLLER_FILE_KEY, CONTROLLER_VERSION );
			ui32Error = m_rKeys[i]->save( pSave );
			pSave->end_chunk();
		}
		else {
			pSave->begin_chunk( CHUNK_CONTROLLER_FLOAT_KEY, CONTROLLER_VERSION );
			ui32Error = m_rKeys[i]->save( pSave );
			pSave->end_chunk();
		}

	}

	return ui32Error;
}

uint32
ControllerC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_CONTROLLER_BASE:
			{
				if( pLoad->get_chunk_version() == CONTROLLER_VERSION ) {
					uint8	ui8Temp;
					// channel count
					ui32Error = pLoad->read( &ui8Temp, sizeof( ui8Temp ) );
					m_ui32NumChannels = ui8Temp;
					// type
					ui32Error = pLoad->read( &ui8Temp, sizeof( ui8Temp ) );
					m_ui32Type = ui8Temp;
					// start ort
					ui32Error = pLoad->read( &ui8Temp, sizeof( ui8Temp ) );
					m_ui32StartOrt = ui8Temp;
					// end ort
					ui32Error = pLoad->read( &ui8Temp, sizeof( ui8Temp ) );
					m_ui32EndOrt = ui8Temp;
				}
			}
			break;
		case CHUNK_CONTROLLER_FLOAT_KEY:
			{
				if( pLoad->get_chunk_version() == CONTROLLER_VERSION ) {
					KeyC*	pKey = (KeyC*)FloatKeyC::create_new( m_ui32NumChannels );
					ui32Error = pKey->load( pLoad );
					m_rKeys.push_back( pKey );
				}
			}
			break;
		case CHUNK_CONTROLLER_FILE_KEY:
			{
				if( pLoad->get_chunk_version() == CONTROLLER_VERSION ) {
					KeyC*	pKey = (KeyC*)FileKeyC::create_new();
					ui32Error = pKey->load( pLoad );
					m_rKeys.push_back( pKey );
				}
			}
			break;
		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END )
			return ui32Error;
	}

	if( ui32Error != IO_OK && ui32Error != IO_END )
		return ui32Error;

	sort_keys();
	prepare();

	return IO_OK;
}

