//
// Parameters - ParamI.cpp
//
//////////////////////////////////////////////////////////////////////

#include "ParamI.h"
#include "PajaTypes.h"
#include "Vector2C.h"
#include "Vector3C.h"
#include "ControllerC.h"
#include "LayerC.h"
#include <assert.h>
#include "ClassIdC.h"
#include "FileHandleC.h"
#include "ImportableI.h"
#include "Composition.h"

// STL
#include <vector>
#include <string>


using namespace std;
using namespace PajaTypes;
using namespace Composition;
using namespace Edit;
using namespace PluginClass;
using namespace Import;
using namespace FileIO;
using namespace PajaSystem;


enum ParameterChunksE {
	CHUNK_PARAMETER_BASE =		0x1000,
	CHUNK_PARAMETER_INT =		0x2000,
	CHUNK_PARAMETER_INT_LABEL =	0x2001,
	CHUNK_PARAMETER_FLOAT =		0x3000,
	CHUNK_PARAMETER_VECTOR2 =	0x4000,
	CHUNK_PARAMETER_VECTOR3 =	0x5000,
	CHUNK_PARAMETER_COLOR =		0x6000,
	CHUNK_PARAMETER_TEXT =		0x7000,
	CHUNK_PARAMETER_FILE =		0x8000,
	CHUNK_PARAMETER_FILE_TIME =	0x8001,
	CHUNK_PARAMETER_FILE_CONT = 0x8002,
	CHUNK_PARAMETER_LINK =	0x9000,
};

const uint32	PARAMETER_VERSION_1 = 1;
const uint32	PARAMETER_VERSION = 2;



//
// ParamI
//
// Interface for parameters
//

ParamI::ParamI() :
	m_ui32Id( 0 ),
	m_ui32Style( 0 ),
	m_pCont( 0 ),
	m_i32Height( 60 ),
	m_i32Flags( ITEM_PARAMETER )
{
	// empty
}

ParamI::ParamI( EditableI* pOriginal ) :
	EditableI( pOriginal ),
	m_ui32Id( 0 ),
	m_ui32Style( 0 ),
	m_pCont( 0 ),
	m_i32Height( 60 ),
	m_i32Flags( ITEM_PARAMETER )
{
	// empty
}


ParamI::ParamI( GizmoI* pParent, const char* szName, uint32 ui32ID, uint32 ui32Style, bool bAnimatable ) :
	m_pParent( pParent ),
	m_sName( szName ),
	m_ui32Id( ui32ID ),
	m_ui32Style( ui32Style ),
	m_pCont( 0 ),
	m_i32Height( 60 ),
	m_i32Flags( ITEM_PARAMETER )
{
	if( bAnimatable )
		m_i32Flags |= ITEM_ANIMATABLE;
}


ParamI::~ParamI()
{
	// empty
}

void
ParamI::copy( EditableI* pEditable )
{
	ParamI*	pParam = (ParamI*)pEditable;
	m_ui32Id = pParam->m_ui32Id;
	m_ui32Style = pParam->m_ui32Style;
	m_i32Flags = pParam->m_i32Flags;
	m_i32Height = pParam->m_i32Height;
	m_sName = pParam->m_sName;
}

void
ParamI::restore( EditableI* pEditable )
{
	ParamI*	pParam = (ParamI*)pEditable;
	m_ui32Id = pParam->m_ui32Id;
	m_ui32Style = pParam->m_ui32Style;
	m_i32Flags = pParam->m_i32Flags;
	m_i32Height = pParam->m_i32Height;
	m_pParent = pParam->m_pParent;
	m_sName = pParam->m_sName;

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

PluginClass::SuperClassIdC
ParamI::get_base_class_id() const
{
	return BASECLASS_PARAMETER;
}

void
ParamI::set_flags( int32 i32Flags )
{
	m_i32Flags = i32Flags;
}

void
ParamI::add_flags( int32 i32Flags )
{
	m_i32Flags |= i32Flags;
}

void
ParamI::del_flags( int32 i32Flags )
{
	m_i32Flags &= ~i32Flags;
}

void
ParamI::toggle_flags( int32 i32Flags )
{
	m_i32Flags ^= i32Flags;
}

int32
ParamI::get_flags()
{
	return m_i32Flags;
}

void
ParamI::set_expanded_height( int32 i32Height )
{
	if( i32Height < 1 ) i32Height = 1;
	m_i32Height = i32Height;
}

int32
ParamI::get_expanded_height()
{
	return m_i32Height;
}

void
ParamI::set_name( const char* name )
{
	m_sName = name;
}

const char*
ParamI::get_name()
{
	return m_sName.c_str();
}

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

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

uint32
ParamI::update_notify( EditableI* pCaller )
{
	// Relay the message to the parent.
	if( m_pParent )
		return m_pParent->update_notify( this );
	return PARAM_NOTIFY_NONE;
}

uint32
ParamI::save( SaveC* pSave )
{
	uint32		ui32Error = IO_OK;
	// flags
	ui32Error = pSave->write( &m_i32Flags, sizeof( m_i32Flags ) );
	// height
	ui32Error = pSave->write( &m_i32Height, sizeof( m_i32Height ) );

	return ui32Error;

}

uint32
ParamI::load( LoadC* pLoad )
{
//	char	szStr[256];
	uint32	ui32Error = IO_OK;
	// flags
	ui32Error = pLoad->read( &m_i32Flags, sizeof( m_i32Flags ) );
	// height
	ui32Error = pLoad->read( &m_i32Height, sizeof( m_i32Height ) );

	return IO_OK;
}



//
// ParamIntC
// - integer
//


ParamIntC::ParamIntC() :
	m_i32Const( 0 ),
	m_i32Inc( 1 )
{
	set_min_max( 0, 0 );
	m_pCont = ControllerC::create_new( this, CONT_TYPE_INT );
}

ParamIntC::ParamIntC( EditableI* pOriginal ) :
	ParamI( pOriginal ),
	m_i32Const( 0 ),
	m_i32Inc( 1 )
{
	set_min_max( 0, 0 );
//	m_pCont = ControllerC::create_new( CONT_TYPE_INT );
}

ParamIntC::ParamIntC( GizmoI* pParent, const char* name, int32 value, uint32 id, uint32 style,
					  bool animatable, int32 min, int32 max, int32 inc ) :
	ParamI( pParent, name, id, style, animatable ),
	m_i32Const( value ),
	m_i32Inc( inc )
{
	set_min_max( min, max );

	if( m_i32Flags & ITEM_CLAMPVALUES ) {
		if( m_i32Const < m_i32Min ) m_i32Const = m_i32Min;
		if( m_i32Const > m_i32Max ) m_i32Const = m_i32Max;
	}

	m_pCont = ControllerC::create_new( this, CONT_TYPE_INT );
}

ParamIntC::~ParamIntC()
{
	if( get_original() )
		return;

	if( m_pCont )
		m_pCont->release();
}

ParamIntC*
ParamIntC::create_new( GizmoI* pParent, const char* name, int32 value, uint32 id, uint32 style,
					  bool animatable, int32 min, int32 max, int32 inc )
{
	return new ParamIntC( pParent, name, value, id, style, animatable, min, max, inc );
}


DataBlockI*
ParamIntC::create()
{
	return new ParamIntC;
}

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

void
ParamIntC::copy( EditableI* pEditable )
{
	ParamI::copy( pEditable );
	ParamIntC*	pParam = (ParamIntC*)pEditable;
	m_i32Const = pParam->m_i32Const;
	m_i32Inc = pParam->m_i32Inc;
	m_i32Min = pParam->m_i32Min;
	m_i32Max = pParam->m_i32Max;
	m_rLabels = pParam->m_rLabels;
	m_pCont->copy( pParam->m_pCont );
	m_pCont->set_parent( this );
}

void
ParamIntC::restore( EditableI* pEditable )
{
	ParamI::restore( pEditable );
	ParamIntC*	pParam = (ParamIntC*)pEditable;
	m_i32Const = pParam->m_i32Const;
	m_i32Inc = pParam->m_i32Inc;
	m_i32Min = pParam->m_i32Min;
	m_i32Max = pParam->m_i32Max;
	m_rLabels = pParam->m_rLabels;
	m_pCont = pParam->m_pCont;
}

uint32
ParamIntC::get_type() const
{
	return PARAM_TYPE_INT;
}
	
void
ParamIntC::set_controller( ControllerC* cont )
{
	m_pCont = cont;
}

ControllerC*
ParamIntC::get_controller()
{
	return m_pCont;
}

void
ParamIntC::set_min_max( int32 min, int32 max )
{
	m_i32Min = min;
	m_i32Max = max;

	if( m_i32Min == m_i32Max )
		del_flags( ITEM_CLAMPVALUES );
	else
		add_flags( ITEM_CLAMPVALUES );
}

int32
ParamIntC::get_min() const
{
	return m_i32Min;
}

int32
ParamIntC::get_max() const
{
	return m_i32Max;
}

void
ParamIntC::set_increment( float32 inc )
{
	m_i32Inc = (int32)inc;
}

float32
ParamIntC::get_increment() const
{
	return (float32)m_i32Inc;
}

bool
ParamIntC::get_min_max( PajaTypes::float32* pMin, PajaTypes::float32* pMax )
{
	pMin[0] = (float32)m_i32Min;
	pMax[0] = (float32)m_i32Max;
	return (get_flags() & ITEM_CLAMPVALUES) != 0;
}

void
ParamIntC::set_style( uint32 style )
{
	m_ui32Style = style;
}

uint32
ParamIntC::get_style() const
{
	return m_ui32Style;
}

void
ParamIntC::set_id( uint32 id )
{
	m_ui32Id = id;
}

uint32
ParamIntC::get_id() const
{
	return m_ui32Id;
}

// bind name to a value.
// this is valid only for combo, checkbox and radiobutton styles
// in radio button style a selected name is returned, but in
// checkbox style or checked values are bitwise orred together.
void
ParamIntC::clear_labels()
{
	m_rLabels.resize( 0 );
}

void
ParamIntC::add_label( int32 val, const char* name )
{
	add_label( val, std::string( name ) );
}

void
ParamIntC::add_label( int32 val, const string& name )
{
	vector<LabelS>::iterator	it;
	LabelS	label;

	label.m_i32Val = val;
	label.m_sName = name;

	for( it = m_rLabels.begin(); it != m_rLabels.end(); it++ ) {
		if( val == it->m_i32Val ) {
			// If label already exists just replace the name
			it->m_sName = name;
			return;
		}
		else if( val < it->m_i32Val ) {
			// add new labels
			m_rLabels.insert( it, label );
			return;
		}
	}
	m_rLabels.insert( m_rLabels.end(), label );
}

uint32
ParamIntC::get_label_count() const
{
	return m_rLabels.size();
}

const char*
ParamIntC::get_label_name( uint32 i )
{
	assert( i >= 0 && i < m_rLabels.size() );
	return m_rLabels[i].m_sName.c_str();
}

int32
ParamIntC::get_label_value( uint32 i )
{
	assert( i >= 0 && i < m_rLabels.size() );
	return m_rLabels[i].m_i32Val;
}

void
ParamIntC::remove_label( int32 val )
{
	vector<LabelS>::iterator	it;
	for( it = m_rLabels.begin(); it != m_rLabels.end(); it++ ) {
		if( val == it->m_i32Val ) {
			m_rLabels.erase( it );
			return;
		}
	}
}

uint32
ParamIntC::set_val( int32 i32Time, const int32& rVal )
{
	m_i32Const = rVal;

	if( get_flags() & ITEM_CLAMPVALUES ) {
		if( m_i32Const < m_i32Min ) m_i32Const = m_i32Min;
		if( m_i32Const > m_i32Max ) m_i32Const = m_i32Max;
	}

	if( get_flags() & ITEM_ANIMATED ) {
		// if animated also change the controller
		KeyC*	pKey = m_pCont->get_key_at_time( i32Time );

		UndoC*	pOldUndo = m_pCont->begin_editing( get_undo() );
		// if there's no key at this time, add new key
		if( !pKey ) {
			pKey = m_pCont->add_key_at_time( i32Time );
		}

		UndoC*	pOldKeyUndo = pKey->begin_editing( get_undo() );
		float32	f32Value[KEY_MAXCHANNELS];
		f32Value[0] = (float32)m_i32Const;
		((FloatKeyC*)pKey)->set_value( f32Value );
		pKey->end_editing( pOldKeyUndo );

		m_pCont->prepare();

		m_pCont->end_editing( pOldUndo );
	}

	// notify parent
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

void
ParamIntC::get_val( int32 i32Time, int32& rVal )
{
	if( get_flags() & ITEM_ANIMATED && m_pCont->get_key_count() ) {
		m_pCont->get_value( rVal, i32Time );
		if( get_flags() & ITEM_CLAMPVALUES ) {
			if( rVal < m_i32Min ) rVal = m_i32Min;
			if( rVal > m_i32Max ) rVal = m_i32Max;
		}
	}
	else
		rVal = m_i32Const;
}

uint32
ParamIntC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;

	// save the base of the parameter
	pSave->begin_chunk( CHUNK_PARAMETER_BASE, PARAMETER_VERSION );
		ui32Error = ParamI::save( pSave );
		// controller
		ui32Error = m_pCont->save( pSave );
	pSave->end_chunk();

	// save int parameter
	pSave->begin_chunk( CHUNK_PARAMETER_INT, PARAMETER_VERSION );
		// const
		ui32Error = pSave->write( &m_i32Const, sizeof( m_i32Const ) );
		// inc
		ui32Error = pSave->write( &m_i32Inc, sizeof( m_i32Inc ) );
		// min
		ui32Error = pSave->write( &m_i32Min, sizeof( m_i32Min ) );
		// max
		ui32Error = pSave->write( &m_i32Max, sizeof( m_i32Max ) );
	pSave->end_chunk();

	//save labels
	for( uint32 i = 0; i < m_rLabels.size(); i++ ) {
		pSave->begin_chunk( CHUNK_PARAMETER_INT_LABEL, PARAMETER_VERSION );
			// make sure this string fits into the buffer when it's loaded back
			std::string	sName = m_rLabels[i].m_sName;
			if( sName.size() > 255 )
				sName.resize( 255 );
			ui32Error = pSave->write_str( sName.c_str() );
			int32	i32Val = m_rLabels[i].m_i32Val;
			ui32Error = pSave->write( &i32Val, sizeof( i32Val ) );
		pSave->end_chunk();
	}

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_PARAMETER_INT:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					// const
					ui32Error = pLoad->read( &m_i32Const, sizeof( m_i32Const ) );
					// inc
					ui32Error = pLoad->read( &m_i32Inc, sizeof( m_i32Inc ) );
					// min
					ui32Error = pLoad->read( &m_i32Min, sizeof( m_i32Min ) );
					// max
					ui32Error = pLoad->read( &m_i32Max, sizeof( m_i32Max ) );
				}
			}
			break;

		case CHUNK_PARAMETER_INT_LABEL:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					char	szStr[256];
					int32	i32Val;
					ui32Error = pLoad->read_str( szStr );
					ui32Error = pLoad->read( &i32Val, sizeof( i32Val ) );
					add_label( i32Val, szStr );
				}
			}
			break;

		case CHUNK_PARAMETER_BASE:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					ui32Error = ParamI::load( pLoad );
					// controller
					ui32Error = m_pCont->load( pLoad );
				}
			}
			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;

	return IO_OK;
}



//
// ParamFloatC
// - floating point
//


ParamFloatC::ParamFloatC() :
	m_f32Const( 0 ),
	m_f32Inc( 0.01f )
{
	set_min_max( 0, 0 );
	m_pCont = ControllerC::create_new( this, CONT_TYPE_FLOAT );
}

ParamFloatC::ParamFloatC( EditableI* pOriginal ) :
	ParamI( pOriginal ),
	m_f32Const( 0 ),
	m_f32Inc( 0.01f )
{
	set_min_max( 0, 0 );
}

ParamFloatC::ParamFloatC( GizmoI* pParent, const char* szName, float32 f32Value, uint32 ui32Id, uint32 ui32Style,
						  bool bAnimatable, float32 f32Min, float32 f32Max, float32 f32Inc ) :
	ParamI( pParent, szName, ui32Id, ui32Style, bAnimatable ),
	m_f32Const( f32Value ),
	m_f32Min( f32Min ),
	m_f32Max( f32Max ),
	m_f32Inc( f32Inc )
{
	set_min_max( f32Min, f32Max );
	if( get_flags() & ITEM_CLAMPVALUES ) {
		if( m_f32Const < m_f32Min ) m_f32Const = m_f32Min;
		if( m_f32Const > m_f32Max ) m_f32Const = m_f32Max;
	}
	m_pCont = ControllerC::create_new( this, CONT_TYPE_FLOAT );
}

ParamFloatC::~ParamFloatC()
{
	if( get_original() )
		return;

	if( m_pCont )
		m_pCont->release();
}


ParamFloatC*
ParamFloatC::create_new( GizmoI* pParent, const char* szName, float32 f32Value, uint32 ui32Id, uint32 ui32Style,
						  bool bAnimatable, float32 f32Min, float32 f32Max, float32 f32Inc )
{
	return new ParamFloatC( pParent, szName, f32Value, ui32Id, ui32Style, bAnimatable, f32Min, f32Max, f32Inc );
}

DataBlockI*
ParamFloatC::create()
{
	return new ParamFloatC;
}

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

void
ParamFloatC::copy( EditableI* pEditable )
{
	ParamI::copy( pEditable );
	ParamFloatC*	pParam = (ParamFloatC*)pEditable;
	m_f32Const = pParam->m_f32Const;
	m_f32Min = pParam->m_f32Min;
	m_f32Max = pParam->m_f32Max;
	m_f32Inc = pParam->m_f32Inc;
	m_pCont->copy( pParam->m_pCont );
	m_pCont->set_parent( this );
}

void
ParamFloatC::restore( EditableI* pEditable )
{
	ParamI::restore( pEditable );
	ParamFloatC*	pParam = (ParamFloatC*)pEditable;
	m_f32Const = pParam->m_f32Const;
	m_f32Min = pParam->m_f32Min;
	m_f32Max = pParam->m_f32Max;
	m_f32Inc = pParam->m_f32Inc;
	m_pCont = pParam->m_pCont;
}


uint32
ParamFloatC::get_type() const
{
	return PARAM_TYPE_FLOAT;
}

void
ParamFloatC::set_controller( ControllerC* pCont )
{
	m_pCont = pCont;
}

ControllerC*
ParamFloatC::get_controller()
{
	return m_pCont;
}


void
ParamFloatC::set_min_max( float32 f32min, float32 f32max )
{
	m_f32Min = f32min;
	m_f32Max = f32max;

	if( m_f32Min == m_f32Max )
		del_flags( ITEM_CLAMPVALUES );
//		m_bClampValues = false;
	else
		add_flags( ITEM_CLAMPVALUES );
//		m_bClampValues = true;
}

float32
ParamFloatC::get_min() const
{
	return m_f32Min;
}

float32
ParamFloatC::get_max() const
{
	return m_f32Max;
}

void
ParamFloatC::set_increment( float32 f32Inc )
{
	m_f32Inc = f32Inc;
}

float32
ParamFloatC::get_increment() const
{
	return m_f32Inc;
}

bool
ParamFloatC::get_min_max( PajaTypes::float32* pMin, PajaTypes::float32* pMax )
{
	pMin[0] = m_f32Min;
	pMax[0] = m_f32Max;
	return (get_flags() & ITEM_CLAMPVALUES) != 0;
}

void
ParamFloatC::set_style( uint32 ui32Style )
{
	m_ui32Style = ui32Style;
}

uint32
ParamFloatC::get_style() const
{
	return m_ui32Style;
}


void
ParamFloatC::set_id( uint32 ui32Id )
{
	m_ui32Id = ui32Id;
}

uint32
ParamFloatC::get_id() const
{
	return m_ui32Id;
}


uint32
ParamFloatC::set_val( int32 i32Time, const float32& rVal )
{
	m_f32Const = rVal;

	if( get_flags() & ITEM_CLAMPVALUES ) {
		if( m_f32Const < m_f32Min ) m_f32Const = m_f32Min;
		if( m_f32Const > m_f32Max ) m_f32Const = m_f32Max;
	}

	if( get_flags() & ITEM_ANIMATED ) {
		// if animated also change the controller
		KeyC*	pKey = m_pCont->get_key_at_time( i32Time );

		UndoC*	pOldUndo = m_pCont->begin_editing( get_undo() );
		// if there's no key at this time, add new key
		if( !pKey ) {
			pKey = m_pCont->add_key_at_time( i32Time );
		}

		UndoC*	pOldKeyUndo = pKey->begin_editing( get_undo() );
		float32	f32Value[KEY_MAXCHANNELS];
		f32Value[0] = m_f32Const;
		((FloatKeyC*)pKey)->set_value( f32Value );
//		pKey->set_value( (uint32)0, m_f32Const );
		pKey->end_editing( pOldKeyUndo );

		m_pCont->prepare();

		m_pCont->end_editing( pOldUndo );
	}

	// notify parent
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

void
ParamFloatC::get_val( int32 i32Time, float32& rVal )
{
	if( get_flags() & ITEM_ANIMATED && m_pCont->get_key_count() ) {
		m_pCont->get_value( rVal, i32Time );

		if( get_flags() & ITEM_CLAMPVALUES ) {
			if( rVal < m_f32Min ) rVal = m_f32Min;
			if( rVal > m_f32Max ) rVal = m_f32Max;
		}
	}
	else
		rVal = m_f32Const;
}

uint32
ParamFloatC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;

	// save the base of the parameter
	pSave->begin_chunk( CHUNK_PARAMETER_BASE, PARAMETER_VERSION );
		ui32Error = ParamI::save( pSave );
		// controller
		ui32Error = m_pCont->save( pSave );
	pSave->end_chunk();

	// save int parameter
	pSave->begin_chunk( CHUNK_PARAMETER_FLOAT, PARAMETER_VERSION );
		// const
		ui32Error = pSave->write( &m_f32Const, sizeof( m_f32Const ) );
		// inc
		ui32Error = pSave->write( &m_f32Inc, sizeof( m_f32Inc ) );
		// min
		ui32Error = pSave->write( &m_f32Min, sizeof( m_f32Min ) );
		// max
		ui32Error = pSave->write( &m_f32Max, sizeof( m_f32Max ) );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_PARAMETER_FLOAT:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					// const
					ui32Error = pLoad->read( &m_f32Const, sizeof( m_f32Const ) );
					// inc
					ui32Error = pLoad->read( &m_f32Inc, sizeof( m_f32Inc ) );
					// min
					ui32Error = pLoad->read( &m_f32Min, sizeof( m_f32Min ) );
					// max
					ui32Error = pLoad->read( &m_f32Max, sizeof( m_f32Max ) );
				}
			}
			break;

		case CHUNK_PARAMETER_BASE:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					ui32Error = ParamI::load( pLoad );
					// controller
					ui32Error = m_pCont->load( pLoad );
				}
			}
			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;

	return IO_OK;
}


//
// ParamVector2C
// - 2D vector
//


ParamVector2C::ParamVector2C() :
	m_rConst( 0, 0 ),
	m_f32Inc( 0.01f )
{
	set_min_max( Vector2C(), Vector2C() );
	m_pCont = ControllerC::create_new( this, CONT_TYPE_VECTOR2 );
}

ParamVector2C::ParamVector2C( EditableI* pOriginal ) :
	ParamI( pOriginal ),
	m_rConst( 0, 0 ),
	m_f32Inc( 0.01f )
{
	set_min_max( Vector2C(), Vector2C() );
}

ParamVector2C::ParamVector2C( GizmoI* pParent, const char* name, const Vector2C& value, uint32 id, uint32 style,
							  bool animatable, const Vector2C& min, const Vector2C& max,
							  float32 inc ) :
	ParamI( pParent, name, id, style, animatable ),
	m_rConst( value ),
	m_f32Inc( inc )
{
	set_min_max( min, max );
	if( get_flags() & ITEM_CLAMPVALUES ) {
		if( m_rConst[0] < m_rMin[0] ) m_rConst[0] = m_rMin[0];
		if( m_rConst[0] > m_rMax[0] ) m_rConst[0] = m_rMax[0];
		if( m_rConst[1] < m_rMin[1] ) m_rConst[1] = m_rMin[1];
		if( m_rConst[1] > m_rMax[1] ) m_rConst[1] = m_rMax[1];
	}
	m_pCont = ControllerC::create_new( this, CONT_TYPE_VECTOR2 );
}

ParamVector2C::~ParamVector2C()
{
	if( get_original() )
		return;

	if( m_pCont )
		m_pCont->release();
}


ParamVector2C*
ParamVector2C::create_new( GizmoI* pParent, const char* name, const Vector2C& value, uint32 id, uint32 style,
					   bool animatable, const Vector2C& min, const Vector2C& max,
					   float32 inc )
{
	return new ParamVector2C( pParent, name, value, id, style, animatable, min, max, inc );
}


DataBlockI*
ParamVector2C::create()
{
	return new ParamVector2C;
}

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

void
ParamVector2C::copy( EditableI* pEditable )
{
	ParamI::copy( pEditable );
	ParamVector2C*	pParam = (ParamVector2C*)pEditable;
	m_rConst = pParam->m_rConst;
	m_rMin = pParam->m_rMin;
	m_rMax = pParam->m_rMax;
	m_f32Inc = pParam->m_f32Inc;
	m_pCont->copy( pParam->m_pCont );
	m_pCont->set_parent( this );
}

void
ParamVector2C::restore( EditableI* pEditable )
{
	ParamI::restore( pEditable );
	ParamVector2C*	pParam = (ParamVector2C*)pEditable;
	m_rConst = pParam->m_rConst;
	m_rMin = pParam->m_rMin;
	m_rMax = pParam->m_rMax;
	m_f32Inc = pParam->m_f32Inc;
	m_pCont = pParam->m_pCont;
}

uint32
ParamVector2C::get_type() const
{
	return PARAM_TYPE_VECTOR2;
}

void
ParamVector2C::set_controller( ControllerC* cont )
{
	m_pCont = cont;
}

ControllerC*
ParamVector2C::get_controller()
{
	return m_pCont;
}

void
ParamVector2C::set_min_max( const Vector2C& min, const Vector2C& max )
{
	m_rMin = min;
	m_rMax = max;

	if( m_rMin == m_rMax )
		del_flags( ITEM_CLAMPVALUES );
//		m_bClampValues = false;
	else
		add_flags( ITEM_CLAMPVALUES );
//		m_bClampValues = true;
}

const Vector2C&
ParamVector2C::get_min() const
{
	return m_rMin;
}

const Vector2C&
ParamVector2C::get_max() const
{
	return m_rMax;
}

void
ParamVector2C::set_increment( float32 inc )
{
	m_f32Inc = inc;
}

float32
ParamVector2C::get_increment() const
{
	return m_f32Inc;
}

bool
ParamVector2C::get_min_max( PajaTypes::float32* pMin, PajaTypes::float32* pMax )
{
	pMin[0] = m_rMin[0];
	pMin[1] = m_rMin[1];
	pMax[0] = m_rMax[0];
	pMax[1] = m_rMax[1];
	return (get_flags() & ITEM_CLAMPVALUES) != 0;
}

void
ParamVector2C::set_style( uint32 style )
{
	m_ui32Style = style;
}

uint32
ParamVector2C::get_style() const
{
	return m_ui32Style;
}


void
ParamVector2C::set_id( uint32 id )
{
	m_ui32Id = id;
}

uint32
ParamVector2C::get_id() const
{
	return m_ui32Id;
}


uint32
ParamVector2C::set_val( int32 i32Time, const Vector2C& rVal )
{
	m_rConst = rVal;

	if( get_flags() & ITEM_CLAMPVALUES ) {
		if( m_rConst[0] < m_rMin[0] ) m_rConst[0] = m_rMin[0];
		if( m_rConst[0] > m_rMax[0] ) m_rConst[0] = m_rMax[0];
		if( m_rConst[1] < m_rMin[1] ) m_rConst[1] = m_rMin[1];
		if( m_rConst[1] > m_rMax[1] ) m_rConst[1] = m_rMax[1];
	}

	if( get_flags() & ITEM_ANIMATED ) {
		// if animated also change the controller
		KeyC*	pKey = m_pCont->get_key_at_time( i32Time );

		UndoC*	pOldUndo = m_pCont->begin_editing( get_undo() );
		// if there's no key at this time, add new key
		if( !pKey ) {
			pKey = m_pCont->add_key_at_time( i32Time );
		}

		UndoC*	pOldKeyUndo = pKey->begin_editing( get_undo() );
		float32	f32Value[KEY_MAXCHANNELS];
		f32Value[0] = m_rConst[0];
		f32Value[1] = m_rConst[1];
		((FloatKeyC*)pKey)->set_value( f32Value );
//		pKey->set_value( (uint32)0, m_rConst[0] );
//		pKey->set_value( (uint32)1, m_rConst[1] );
		pKey->end_editing( pOldKeyUndo );

		m_pCont->prepare();

		m_pCont->end_editing( pOldUndo );
	}

	// notify parent
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

void
ParamVector2C::get_val( int32 i32Time, Vector2C& rVal )
{
	if( get_flags() & ITEM_ANIMATED && m_pCont->get_key_count() ) {
		m_pCont->get_value( rVal, i32Time );

		if( get_flags() & ITEM_CLAMPVALUES ) {
			if( rVal[0] < m_rMin[0] ) rVal[0] = m_rMin[0];
			if( rVal[0] > m_rMax[0] ) rVal[0] = m_rMax[0];
			if( rVal[1] < m_rMin[1] ) rVal[1] = m_rMin[1];
			if( rVal[1] > m_rMax[1] ) rVal[1] = m_rMax[1];
		}
	}
	else
		rVal = m_rConst;
}

uint32
ParamVector2C::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	float32	f32Val[2];

	// save the base of the parameter
	pSave->begin_chunk( CHUNK_PARAMETER_BASE, PARAMETER_VERSION );
		ui32Error = ParamI::save( pSave );
		// controller
		ui32Error = m_pCont->save( pSave );
	pSave->end_chunk();

	// save int parameter
	pSave->begin_chunk( CHUNK_PARAMETER_VECTOR2, PARAMETER_VERSION );
		// const
		f32Val[0] = m_rConst[0];
		f32Val[1] = m_rConst[1];
		ui32Error = pSave->write( &f32Val, sizeof( f32Val ) );
		// inc
		ui32Error = pSave->write( &m_f32Inc, sizeof( m_f32Inc ) );
		// min
		f32Val[0] = m_rMin[0];
		f32Val[1] = m_rMin[1];
		ui32Error = pSave->write( &f32Val, sizeof( f32Val ) );
		// max
		f32Val[0] = m_rMax[0];
		f32Val[1] = m_rMax[1];
		ui32Error = pSave->write( &f32Val, sizeof( f32Val ) );
	pSave->end_chunk();

	return ui32Error;
}

uint32
ParamVector2C::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	float32	f32Val[2];

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_PARAMETER_VECTOR2:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					// const
					ui32Error = pLoad->read( &f32Val, sizeof( f32Val ) );
					m_rConst[0] = f32Val[0];
					m_rConst[1] = f32Val[1];
					// inc
					ui32Error = pLoad->read( &m_f32Inc, sizeof( m_f32Inc ) );
					// min
					ui32Error = pLoad->read( &f32Val, sizeof( f32Val ) );
					m_rMin[0] = f32Val[0];
					m_rMin[1] = f32Val[1];
					// max
					ui32Error = pLoad->read( &f32Val, sizeof( f32Val ) );
					m_rMax[0] = f32Val[0];
					m_rMax[1] = f32Val[1];
				}
			}
			break;

		case CHUNK_PARAMETER_BASE:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					ui32Error = ParamI::load( pLoad );
					// controller
					ui32Error = m_pCont->load( pLoad );
				}
			}
			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;

	return IO_OK;
}


//
// ParamVector3C
// - 3D point
//


ParamVector3C::ParamVector3C() :
	m_rConst( 0, 0, 0 ),
	m_f32Inc( 0.01f )
{
	set_min_max( Vector3C(), Vector3C() );
	m_pCont = ControllerC::create_new( this, CONT_TYPE_VECTOR3 );
}

ParamVector3C::ParamVector3C( EditableI* pOriginal ) :
	ParamI( pOriginal ),
	m_rConst( 0, 0, 0 ),
	m_rMin( 0, 0, 0 ),
	m_rMax( 1.0f, 1.0f, 1.0f ),
	m_f32Inc( 0.01f )
{
	set_min_max( Vector3C(), Vector3C() );
}

ParamVector3C::ParamVector3C( GizmoI* pParent, const char* name, const Vector3C& value, uint32 id, uint32 style,
							  bool animatable, const Vector3C& min, const Vector3C& max,
							  float32 inc ) :
	ParamI( pParent, name, id, style, animatable ),
	m_rConst( value ),
	m_f32Inc( inc )
{
	set_min_max( min, max );
	if( get_flags() & ITEM_CLAMPVALUES ) {
		if( m_rConst[0] < m_rMin[0] ) m_rConst[0] = m_rMin[0];
		if( m_rConst[0] > m_rMax[0] ) m_rConst[0] = m_rMax[0];
		if( m_rConst[1] < m_rMin[1] ) m_rConst[1] = m_rMin[1];
		if( m_rConst[1] > m_rMax[1] ) m_rConst[1] = m_rMax[1];
		if( m_rConst[2] < m_rMin[2] ) m_rConst[2] = m_rMin[2];
		if( m_rConst[2] > m_rMax[2] ) m_rConst[2] = m_rMax[2];
	}
	m_pCont = ControllerC::create_new( this, CONT_TYPE_VECTOR3 );
}

ParamVector3C::~ParamVector3C()
{
	if( get_original() )
		return;

	if( m_pCont )
		m_pCont->release();
}


ParamVector3C*
ParamVector3C::create_new( GizmoI* pParent, const char* name, const Vector3C& value, uint32 id, uint32 style,
					   bool animatable, const Vector3C& min, const Vector3C& max,
					   float32 inc )
{
	return new ParamVector3C( pParent, name, value, id, style, animatable, min, max, inc );
}


DataBlockI*
ParamVector3C::create()
{
	return new ParamVector3C;
}

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

void
ParamVector3C::copy( EditableI* pEditable )
{
	ParamI::copy( pEditable );
	ParamVector3C*	pParam = (ParamVector3C*)pEditable;
	m_rConst = pParam->m_rConst;
	m_rMin = pParam->m_rMin;
	m_rMax = pParam->m_rMax;
	m_f32Inc = pParam->m_f32Inc;
	m_pCont->copy( pParam->m_pCont );
	m_pCont->set_parent( this );
}

void
ParamVector3C::restore( EditableI* pEditable )
{
	ParamI::restore( pEditable );
	ParamVector3C*	pParam = (ParamVector3C*)pEditable;
	m_rConst = pParam->m_rConst;
	m_rMin = pParam->m_rMin;
	m_rMax = pParam->m_rMax;
	m_f32Inc = pParam->m_f32Inc;
	m_pCont = pParam->m_pCont;
}

uint32
ParamVector3C::get_type() const
{
	return PARAM_TYPE_VECTOR3;
}

void
ParamVector3C::set_controller( ControllerC* cont )
{
	m_pCont = cont;
}

ControllerC*
ParamVector3C::get_controller()
{
	return m_pCont;
}


void
ParamVector3C::set_min_max( const Vector3C& min, const Vector3C& max )
{
	m_rMin = min;
	m_rMax = max;

	if( m_rMin == m_rMax )
		del_flags( ITEM_CLAMPVALUES );
//		m_bClampValues = false;
	else
		add_flags( ITEM_CLAMPVALUES );
//		m_bClampValues = true;
}

const Vector3C&
ParamVector3C::get_min() const
{
	return m_rMin;
}

const Vector3C&
ParamVector3C::get_max() const
{
	return m_rMax;
}


void
ParamVector3C::set_increment( float32 inc )
{
	m_f32Inc = inc;
}

float32
ParamVector3C::get_increment() const
{
	return m_f32Inc;
}

bool
ParamVector3C::get_min_max( PajaTypes::float32* pMin, PajaTypes::float32* pMax )
{
	pMin[0] = m_rMin[0];
	pMin[1] = m_rMin[1];
	pMin[2] = m_rMin[2];
	pMax[0] = m_rMax[0];
	pMax[1] = m_rMax[1];
	pMax[2] = m_rMax[2];
	return (get_flags() & ITEM_CLAMPVALUES) != 0;
}

void
ParamVector3C::set_style( uint32 style )
{
	m_ui32Style = style;
}

uint32
ParamVector3C::get_style() const
{
	return m_ui32Style;
}


void
ParamVector3C::set_id( uint32 id )
{
	m_ui32Id = id;
}

uint32
ParamVector3C::get_id() const
{
	return m_ui32Id;
}


uint32
ParamVector3C::set_val( int32 i32Time, const Vector3C& rVal )
{
	m_rConst = rVal;

	if( get_flags() & ITEM_CLAMPVALUES ) {
		if( m_rConst[0] < m_rMin[0] ) m_rConst[0] = m_rMin[0];
		if( m_rConst[0] > m_rMax[0] ) m_rConst[0] = m_rMax[0];
		if( m_rConst[1] < m_rMin[1] ) m_rConst[1] = m_rMin[1];
		if( m_rConst[1] > m_rMax[1] ) m_rConst[1] = m_rMax[1];
		if( m_rConst[2] < m_rMin[2] ) m_rConst[2] = m_rMin[2];
		if( m_rConst[2] > m_rMax[2] ) m_rConst[2] = m_rMax[2];
	}

	if( get_flags() & ITEM_ANIMATED ) {
		// if animated also change the controller
		KeyC*	pKey = m_pCont->get_key_at_time( i32Time );

		UndoC*	pOldUndo = m_pCont->begin_editing( get_undo() );
		// if there's no key at this time, add new key
		if( !pKey ) {
			pKey = m_pCont->add_key_at_time( i32Time );
		}

		UndoC*	pOldKeyUndo = pKey->begin_editing( get_undo() );
		float32	f32Value[KEY_MAXCHANNELS];
		f32Value[0] = m_rConst[0];
		f32Value[1] = m_rConst[1];
		f32Value[2] = m_rConst[2];
		((FloatKeyC*)pKey)->set_value( f32Value );
//		pKey->set_value( (uint32)0, m_rConst[0] );
//		pKey->set_value( (uint32)1, m_rConst[1] );
//		pKey->set_value( (uint32)2, m_rConst[2] );
		pKey->end_editing( pOldKeyUndo );

		m_pCont->prepare();

		m_pCont->end_editing( pOldUndo );
	}

	// notify parent
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

void
ParamVector3C::get_val( int32 i32Time, Vector3C& rVal )
{
	if( get_flags() & ITEM_ANIMATED && m_pCont->get_key_count() ) {
		m_pCont->get_value( rVal, i32Time );
		if( get_flags() & ITEM_CLAMPVALUES ) {
			if( rVal[0] < m_rMin[0] ) rVal[0] = m_rMin[0];
			if( rVal[0] > m_rMax[0] ) rVal[0] = m_rMax[0];
			if( rVal[1] < m_rMin[1] ) rVal[1] = m_rMin[1];
			if( rVal[1] > m_rMax[1] ) rVal[1] = m_rMax[1];
			if( rVal[2] < m_rMin[2] ) rVal[2] = m_rMin[2];
			if( rVal[2] > m_rMax[2] ) rVal[2] = m_rMax[2];
		}
	}
	else
		rVal = m_rConst;
}

uint32
ParamVector3C::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	float32	f32Val[3];

	// save the base of the parameter
	pSave->begin_chunk( CHUNK_PARAMETER_BASE, PARAMETER_VERSION );
		ui32Error = ParamI::save( pSave );
		// controller
		ui32Error = m_pCont->save( pSave );
	pSave->end_chunk();

	// save int parameter
	pSave->begin_chunk( CHUNK_PARAMETER_VECTOR3, PARAMETER_VERSION );
		// const
		f32Val[0] = m_rConst[0];
		f32Val[1] = m_rConst[1];
		f32Val[2] = m_rConst[2];
		ui32Error = pSave->write( &f32Val, sizeof( f32Val ) );
		// inc
		ui32Error = pSave->write( &m_f32Inc, sizeof( m_f32Inc ) );
		// min
		f32Val[0] = m_rMin[0];
		f32Val[1] = m_rMin[1];
		f32Val[2] = m_rMin[2];
		ui32Error = pSave->write( &f32Val, sizeof( f32Val ) );
		// max
		f32Val[0] = m_rMax[0];
		f32Val[1] = m_rMax[1];
		f32Val[2] = m_rMax[2];
		ui32Error = pSave->write( &f32Val, sizeof( f32Val ) );
	pSave->end_chunk();

	return ui32Error;
}

uint32
ParamVector3C::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	float32	f32Val[3];

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_PARAMETER_VECTOR3:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					// const
					ui32Error = pLoad->read( &f32Val, sizeof( f32Val ) );
					m_rConst[0] = f32Val[0];
					m_rConst[1] = f32Val[1];
					m_rConst[2] = f32Val[2];
					// inc
					ui32Error = pLoad->read( &m_f32Inc, sizeof( m_f32Inc ) );
					// min
					ui32Error = pLoad->read( &f32Val, sizeof( f32Val ) );
					m_rMin[0] = f32Val[0];
					m_rMin[1] = f32Val[1];
					m_rMin[2] = f32Val[2];
					// max
					ui32Error = pLoad->read( &f32Val, sizeof( f32Val ) );
					m_rMax[0] = f32Val[0];
					m_rMax[1] = f32Val[1];
					m_rMax[2] = f32Val[2];
				}
			}
			break;

		case CHUNK_PARAMETER_BASE:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					ui32Error = ParamI::load( pLoad );
					// controller
					ui32Error = m_pCont->load( pLoad );
				}
			}
			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;

	return IO_OK;
}

//
// ParamColorC
// - color
//

ParamColorC::ParamColorC() :
	m_rConst( 0, 0, 0 )
{
	m_pCont = ControllerC::create_new( this, CONT_TYPE_COLOR );
}

ParamColorC::ParamColorC( EditableI* pOriginal ) :
	ParamI( pOriginal ),
	m_rConst( 0, 0, 0 )
{
	// empty
}

ParamColorC::ParamColorC( GizmoI* pParent, const char* name, const ColorC& value, uint32 id, uint32 style, bool animatable ) :
	ParamI( pParent, name, id, style, animatable ),
	m_rConst( value )
{
	m_pCont = ControllerC::create_new( this, CONT_TYPE_COLOR );
}

ParamColorC::~ParamColorC()
{
	if( get_original() )
		return;

	if( m_pCont )
		m_pCont->release();
}


ParamColorC*
ParamColorC::create_new( GizmoI* pParent, const char* name, const ColorC& value, uint32 id, uint32 style, bool animatable )
{
	return new ParamColorC( pParent, name, value, id, style, animatable );
}


DataBlockI*
ParamColorC::create()
{
	return new ParamColorC;
}

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

void
ParamColorC::copy( EditableI* pEditable )
{
	ParamI::copy( pEditable );
	ParamColorC*	pParam = (ParamColorC*)pEditable;
	m_rConst = pParam->m_rConst;
	m_pCont->copy( pParam->m_pCont );
	m_pCont->set_parent( this );
}

void
ParamColorC::restore( EditableI* pEditable )
{
	ParamI::restore( pEditable );
	ParamColorC*	pParam = (ParamColorC*)pEditable;
	m_rConst = pParam->m_rConst;
	m_pCont = pParam->m_pCont;
}

uint32
ParamColorC::get_type() const
{
	return PARAM_TYPE_COLOR;
}

void
ParamColorC::set_controller( ControllerC* cont )
{
	m_pCont = cont;
}

void
ParamColorC::set_increment( float32 inc )
{
	// empty
}

float32
ParamColorC::get_increment() const
{
	return 1.0f / 256.0f;
}

bool
ParamColorC::get_min_max( PajaTypes::float32* pMin, PajaTypes::float32* pMax )
{
	pMin[0] = 0;
	pMin[1] = 0;
	pMin[2] = 0;
	pMin[3] = 0;
	pMax[0] = 1;
	pMax[1] = 1;
	pMax[2] = 1;
	pMax[3] = 1;
	return true;	// allways clamp colors
}

ControllerC*
ParamColorC::get_controller()
{
	return m_pCont;
}


void
ParamColorC::set_style( uint32 style )
{
	m_ui32Style = style;
}

uint32
ParamColorC::get_style() const
{
	return m_ui32Style;
}


void
ParamColorC::set_id( uint32 id )
{
	m_ui32Id = id;
}

uint32
ParamColorC::get_id() const
{
	return m_ui32Id;
}


uint32
ParamColorC::set_val( int32 i32Time, const ColorC& rVal )
{
	m_rConst = rVal;

	if( m_rConst[0] < 0.0 ) m_rConst[0] = 0.0;
	if( m_rConst[0] > 1.0 ) m_rConst[0] = 1.0;
	if( m_rConst[1] < 0.0 ) m_rConst[1] = 0.0;
	if( m_rConst[1] > 1.0 ) m_rConst[1] = 1.0;
	if( m_rConst[2] < 0.0 ) m_rConst[2] = 0.0;
	if( m_rConst[2] > 1.0 ) m_rConst[2] = 1.0;
	if( m_rConst[3] < 0.0 ) m_rConst[3] = 0.0;
	if( m_rConst[3] > 1.0 ) m_rConst[3] = 1.0;

	if( get_flags() & ITEM_ANIMATED ) {
		// if animated also change the controller
		KeyC*	pKey = m_pCont->get_key_at_time( i32Time );

		UndoC*	pOldUndo = m_pCont->begin_editing( get_undo() );
		// if there's no key at this time, add new key
		if( !pKey ) {
			pKey = m_pCont->add_key_at_time( i32Time );
		}

		UndoC*	pOldKeyUndo = pKey->begin_editing( get_undo() );
		float32	f32Value[KEY_MAXCHANNELS];
		f32Value[0] = m_rConst[0];
		f32Value[1] = m_rConst[1];
		f32Value[2] = m_rConst[2];
		f32Value[3] = m_rConst[3];
		((FloatKeyC*)pKey)->set_value( f32Value );
//		pKey->set_value( (uint32)0, m_rConst[0] );
//		pKey->set_value( (uint32)1, m_rConst[1] );
//		pKey->set_value( (uint32)2, m_rConst[2] );
//		pKey->set_value( (uint32)3, m_rConst[3] );
		pKey->end_editing( pOldKeyUndo );

		m_pCont->prepare();

		m_pCont->end_editing( pOldUndo );
	}

	// notify parent
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

void
ParamColorC::get_val( int32 i32Time, ColorC& rVal )
{
	if( get_flags() & ITEM_ANIMATED && m_pCont->get_key_count() ) {
		m_pCont->get_value( rVal, i32Time );

		if( rVal[0] < 0.0 ) rVal[0] = 0.0;
		if( rVal[0] > 1.0 ) rVal[0] = 1.0;
		if( rVal[1] < 0.0 ) rVal[1] = 0.0;
		if( rVal[1] > 1.0 ) rVal[1] = 1.0;
		if( rVal[2] < 0.0 ) rVal[2] = 0.0;
		if( rVal[2] > 1.0 ) rVal[2] = 1.0;
		if( rVal[3] < 0.0 ) rVal[3] = 0.0;
		if( rVal[3] > 1.0 ) rVal[3] = 1.0;
	}
	else
		rVal = m_rConst;
}

uint32
ParamColorC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	float32	f32Val[4];

	// save the base of the parameter
	pSave->begin_chunk( CHUNK_PARAMETER_BASE, PARAMETER_VERSION );
		ui32Error = ParamI::save( pSave );
		// controller
		ui32Error = m_pCont->save( pSave );
	pSave->end_chunk();

	// save int parameter
	pSave->begin_chunk( CHUNK_PARAMETER_COLOR, PARAMETER_VERSION );
		// const
		f32Val[0] = m_rConst[0];
		f32Val[1] = m_rConst[1];
		f32Val[2] = m_rConst[2];
		f32Val[3] = m_rConst[3];
		ui32Error = pSave->write( &f32Val, sizeof( f32Val ) );
	pSave->end_chunk();

	return ui32Error;
}

uint32
ParamColorC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	float32	f32Val[4];

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_PARAMETER_COLOR:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					// const
					ui32Error = pLoad->read( &f32Val, sizeof( f32Val ) );
					m_rConst[0] = f32Val[0];
					m_rConst[1] = f32Val[1];
					m_rConst[2] = f32Val[2];
					m_rConst[3] = f32Val[3];
				}
			}
			break;

		case CHUNK_PARAMETER_BASE:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					ui32Error = ParamI::load( pLoad );
					// controller
					ui32Error = m_pCont->load( pLoad );
				}
			}
			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;

	return IO_OK;
}


//
// ParamTextC
// - text
//
// appearance:
//  Caption
//    [_textiä______]				(edit box)
//


ParamTextC::ParamTextC()
{
	// empty
}

ParamTextC::ParamTextC( EditableI* pOriginal ) :
	ParamI( pOriginal )
{
	// empty
}

ParamTextC::ParamTextC( GizmoI* pParent, const char* name, const char* text, uint32 id,
					   uint32 style ) :
	ParamI( pParent, name, id, style, false ),
	m_sText( text )
{
	// empty
}

ParamTextC::~ParamTextC()
{
	// empty
}


ParamTextC*
ParamTextC::create_new( GizmoI* pParent, const char* name, const char* text, uint32 id,
					   uint32 style )
{
	return new ParamTextC( pParent, name, text, id, style );
}

DataBlockI*
ParamTextC::create()
{
	return new ParamTextC;
}

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

void
ParamTextC::copy( EditableI* pEditable )
{
	ParamI::copy( pEditable );
	ParamTextC*	pParam = (ParamTextC*)pEditable;
	m_sText = pParam->m_sText;
}

void
ParamTextC::restore( EditableI* pEditable )
{
	ParamI::restore( pEditable );
	ParamTextC*	pParam = (ParamTextC*)pEditable;
	m_sText = pParam->m_sText;
}

uint32
ParamTextC::get_type() const
{
	return PARAM_TYPE_TEXT;
}

void
ParamTextC::set_controller( ControllerC* cnt )
{
	m_pCont = cnt;
}

ControllerC*
ParamTextC::get_controller()
{
	return m_pCont;
}

void
ParamTextC::set_style( uint32 style )
{
	m_ui32Style = style;
}

uint32
ParamTextC::get_style() const
{
	return m_ui32Style;
}

void
ParamTextC::set_id( uint32 id )
{
	m_ui32Id = id;
}

uint32
ParamTextC::get_id() const
{
	return m_ui32Id;
}

uint32
ParamTextC::set_val( int32 i32Time, const char* szVal )
{
	m_sText = szVal;

	// notify parent
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

const char*
ParamTextC::get_val( int32 i32Time, char* szVal, int32 i32Size )
{
	if( szVal ) 
		strncpy( szVal, m_sText.c_str(), i32Size );

	return m_sText.c_str();
}

void
ParamTextC::set_increment( float32 inc )
{
	// do nothign
}

float32
ParamTextC::get_increment() const
{
	return 1.0f;
}

bool
ParamTextC::get_min_max( PajaTypes::float32* pMin, PajaTypes::float32* pMax )
{
	return false;
}

uint32
ParamTextC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	std::string	sStr;

	// save the base of the parameter
	pSave->begin_chunk( CHUNK_PARAMETER_BASE, PARAMETER_VERSION );
		ui32Error = ParamI::save( pSave );
	pSave->end_chunk();

	// save int parameter
	pSave->begin_chunk( CHUNK_PARAMETER_TEXT, PARAMETER_VERSION );
		// text
		sStr = m_sText;
		if( sStr.size() > 1024 )
			sStr.resize( 1024 );
		ui32Error = pSave->write_str( sStr.c_str() );
	pSave->end_chunk();

	return ui32Error;
}

uint32
ParamTextC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	char	szStr[1026];	// some extra just for safety

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_PARAMETER_TEXT:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					// text
					ui32Error = pLoad->read_str( szStr );
					m_sText = szStr;
				}
			}
			break;

		case CHUNK_PARAMETER_BASE:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION )
					ui32Error = ParamI::load( pLoad );
			}
			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;

	return IO_OK;
}


//
// ParamFileC
// - file handle
//


ParamFileC::ParamFileC() :
	m_pHandle( 0 )
{
	add_flags( ITEM_EXPANDABLE );
	m_pCont = ControllerC::create_new( this, CONT_TYPE_FILE );
}

ParamFileC::ParamFileC( EditableI* pOriginal ) :
	ParamI( pOriginal ),
	m_pHandle( 0 )
{
	add_flags( ITEM_EXPANDABLE );
}

ParamFileC::ParamFileC( GizmoI* pParent, const char* name, SuperClassIdC rSuperClassFilter, ClassIdC rClassFilter,
					    PajaTypes::uint32 id, PajaTypes::uint32 style, bool bAnimatable ) :
	ParamI( pParent, name, id, style, bAnimatable ),
	m_rSuperClassFilter( rSuperClassFilter ),
	m_rClassFilter( rClassFilter ),
	m_i32TimeOffset( 0 ),
	m_f32TimeScale( 1 ),
	m_pHandle( 0 )
{
	add_flags( ITEM_EXPANDABLE );
	m_pCont = ControllerC::create_new( this, CONT_TYPE_FILE );
}

ParamFileC::~ParamFileC()
{
	if( get_original() )
		return;

	if( m_pCont )
		m_pCont->release();
}


ParamFileC*
ParamFileC::create_new( GizmoI* pParent, const char* name, SuperClassIdC rSuperClassFilter, ClassIdC rClassFilter,
						uint32 id, uint32 style, bool bAnimatable )
{
	return new ParamFileC( pParent, name, rSuperClassFilter, rClassFilter, id, style, bAnimatable );
}



DataBlockI*
ParamFileC::create()
{
	return new ParamFileC;
}

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

void
ParamFileC::copy( EditableI* pEditable )
{
	ParamI::copy( pEditable );
	ParamFileC*	pParam = (ParamFileC*)pEditable;
	m_pHandle = pParam->m_pHandle;
	m_rSuperClassFilter = pParam->m_rSuperClassFilter;
	m_rClassFilter = pParam->m_rClassFilter;
	m_i32TimeOffset = pParam->m_i32TimeOffset;
	m_f32TimeScale = pParam->m_f32TimeScale;
	m_pCont->copy( pParam->m_pCont );
	m_pCont->set_parent( this );
}

void
ParamFileC::restore( EditableI* pEditable )
{
	ParamI::restore( pEditable );
	ParamFileC*	pParam = (ParamFileC*)pEditable;
	m_pHandle = pParam->m_pHandle;
	m_rSuperClassFilter = pParam->m_rSuperClassFilter;
	m_rClassFilter = pParam->m_rClassFilter;
	m_i32TimeOffset = pParam->m_i32TimeOffset;
	m_f32TimeScale = pParam->m_f32TimeScale;
}

uint32
ParamFileC::get_type() const
{
	return PARAM_TYPE_FILE;
}

// if this parameter can be animated get_animatable should return true
/*
void
ParamFileC::set_animatable( bool state )
{
	// empty
}

bool
ParamFileC::get_animatable() const
{
	return false;
}
*/

void
ParamFileC::set_controller( ControllerC* cnt )
{
	m_pCont = cnt;
}

ControllerC*
ParamFileC::get_controller()
{
	return m_pCont;
}

void
ParamFileC::set_style( uint32 style )
{
	m_ui32Style = style;
}

uint32
ParamFileC::get_style() const
{
	return m_ui32Style;
}

void
ParamFileC::set_id( uint32 id )
{
	m_ui32Id = id;
}

uint32
ParamFileC::get_id() const
{
	return m_ui32Id;
}

void
ParamFileC::set_increment( float32 inc )
{
	// do nothign
}

float32
ParamFileC::get_increment() const
{
	return 1.0f;
}

bool
ParamFileC::get_min_max( PajaTypes::float32* pMin, PajaTypes::float32* pMax )
{
	return false;
}

FileHandleC*
ParamFileC::get_file( int32 t )
{
	if( (get_flags() & ITEM_ANIMATED) && m_pCont && m_pCont->get_key_count() ) {
		FileHandleC*	pHandle = 0;
		int32			i32FileTime = 0;
		m_pCont->get_value( pHandle, i32FileTime, t );
		return pHandle;
	}
	
	return m_pHandle;
}

void
ParamFileC::get_file( int32 t, FileHandleC*& pHandle, int32& i32FileTime )
{
	if( (get_flags() & ITEM_ANIMATED) && m_pCont && m_pCont->get_key_count() ) {
		m_pCont->get_value( pHandle, i32FileTime, t );
	}
	else {
		pHandle = m_pHandle;
		i32FileTime = (int32)((float32)(t + m_i32TimeOffset) * m_f32TimeScale);
	}
}

uint32
ParamFileC::set_file( int32 t, FileHandleC* pHandle )
{
	FileHandleC*	pCheckedHandle = 0;

	if( pHandle ) {
		ImportableI*	pImp = pHandle->get_importable();
		if( pImp && (m_rClassFilter == NULL_CLASSID || m_rClassFilter == pImp->get_class_id()) &&
			(m_rSuperClassFilter == NULL_SUPERCLASS || m_rSuperClassFilter == pImp->get_super_class_id()) )
			pCheckedHandle = pHandle;
	}

	if( (get_flags() & ITEM_ANIMATED) && m_pCont ) {
		// if animated also change the controller
		FileKeyC*	pKey = (FileKeyC*)m_pCont->get_key_at_time( t );

		UndoC*	pOldUndo = m_pCont->begin_editing( get_undo() );
		// if there's no key at this time, add new key
		if( !pKey ) {
			pKey = (FileKeyC*)m_pCont->add_key_at_time( t );
		}

		if( pKey ) {
			UndoC*	pOldKeyUndo = pKey->begin_editing( get_undo() );
			pKey->set_file_handle( pHandle );
			pKey->end_editing( pOldKeyUndo );
		}

		m_pCont->end_editing( pOldUndo );
	}

	m_pHandle = pCheckedHandle;

	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

SuperClassIdC
ParamFileC::get_super_class_filter()
{
	return m_rSuperClassFilter;
}

ClassIdC
ParamFileC::get_class_filter()
{
	return m_rClassFilter;
}

void
ParamFileC::set_super_class_filter( SuperClassIdC rSuperClassId )
{
	m_rSuperClassFilter = rSuperClassId;
}

void
ParamFileC::set_class_filter( ClassIdC rClassId )
{
	m_rClassFilter = rClassId;
}


uint32
ParamFileC::set_time_offset( int32 t, PajaTypes::int32 i32TimeOffset )
{
	if( (get_flags() & ITEM_ANIMATED) && m_pCont ) {
		// if animated also change the controller
		FileKeyC*	pKey = (FileKeyC*)m_pCont->get_key_at_time( t );

		UndoC*	pOldUndo = m_pCont->begin_editing( get_undo() );
		// if there's no key at this time, add new key
		if( !pKey ) {
			pKey = (FileKeyC*)m_pCont->add_key_at_time( t );
		}

		if( pKey ) {
			UndoC*	pOldKeyUndo = pKey->begin_editing( get_undo() );
			pKey->set_time_offset( i32TimeOffset );
			pKey->end_editing( pOldKeyUndo );
		}

		m_pCont->end_editing( pOldUndo );
	}
	else {
		m_i32TimeOffset = i32TimeOffset;
	}

	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

uint32
ParamFileC::set_time_scale( int32 t, PajaTypes::float32 f32TimeScale )
{
	if( (get_flags() & ITEM_ANIMATED) && m_pCont ) {
		// if animated also change the controller
		FileKeyC*	pKey = (FileKeyC*)m_pCont->get_key_at_time( t );

		UndoC*	pOldUndo = m_pCont->begin_editing( get_undo() );
		// if there's no key at this time, add new key
		if( !pKey ) {
			pKey = (FileKeyC*)m_pCont->add_key_at_time( t );
		}

		if( pKey ) {
			UndoC*	pOldKeyUndo = pKey->begin_editing( get_undo() );
			pKey->set_time_scale( f32TimeScale );
			pKey->end_editing( pOldKeyUndo );
		}

		m_pCont->end_editing( pOldUndo );
	}
	else {
		m_f32TimeScale = f32TimeScale;
	}

	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

float32
ParamFileC::get_time_scale( int32 t )
{
	if( (get_flags() & ITEM_ANIMATED) && m_pCont && m_pCont->get_key_count() ) {

		FileKeyC*	pCurKey = 0;
		FileKeyC*	pKey;

		// Is the requested time before the first key?
		pKey = (FileKeyC*)m_pCont->get_key( 0 );
		if( pKey && pKey->get_time() >= t ) {
			return pKey->get_time_scale();
		}

		// Is the requested time after the last key?
		pKey = (FileKeyC*)m_pCont->get_key( m_pCont->get_key_count() - 1 );
		if( pKey && pKey->get_time() <= t ) {
			return pKey->get_time_scale();
		}

		// interpolate
		for( int32 i = 1; i < m_pCont->get_key_count(); i++ ) {
			pKey = (FileKeyC*)m_pCont->get_key( i );
			if( pKey && pKey->get_time() > t ) {
				pCurKey = (FileKeyC*)m_pCont->get_key( i - 1 );
				break;
			}
		}

		if( pCurKey ) {
			return pCurKey->get_time_scale();
		}
	}

	return m_f32TimeScale;
}

int32
ParamFileC::get_time_offset( int32 t )
{
	if( (get_flags() & ITEM_ANIMATED) && m_pCont->get_key_count() ) {

		FileKeyC*	pCurKey = 0;
		FileKeyC*	pKey;

		// Is the requested time before the first key?
		pKey = (FileKeyC*)m_pCont->get_key( 0 );
		if( pKey && pKey->get_time() >= t ) {
			return pKey->get_time_offset();
		}

		// Is the requested time after the last key?
		pKey = (FileKeyC*)m_pCont->get_key( m_pCont->get_key_count() - 1 );
		if( pKey && pKey->get_time() <= t ) {
			return pKey->get_time_offset();
		}

		// interpolate
		for( int32 i = 1; i < m_pCont->get_key_count(); i++ ) {
			pKey = (FileKeyC*)m_pCont->get_key( i );
			if( pKey && pKey->get_time() > t ) {
				pCurKey = (FileKeyC*)m_pCont->get_key( i - 1 );
				break;
			}
		}

		if( pCurKey ) {
			return pCurKey->get_time_offset();
		}
	}

	return m_i32TimeOffset;
}

int32
ParamFileC::get_duration( int32 t )
{
	FileHandleC*	pHandle = 0;
	float32			f32TimeScale;
	int32			i32TimeOffset;

	if( (get_flags() & ITEM_ANIMATED) && m_pCont && m_pCont->get_key_count() ) {

		FileKeyC*	pCurKey = 0;
		FileKeyC*	pFirstKey = 0;
		FileKeyC*	pLastKey = 0;

		pFirstKey = (FileKeyC*)m_pCont->get_key( 0 );
		pLastKey = (FileKeyC*)m_pCont->get_key( m_pCont->get_key_count() - 1 );

		if( pFirstKey&& pFirstKey->get_time() >= t ) {
			// Is the requested time before the first key?
			pCurKey = pFirstKey;
		}
		else if( pLastKey && pLastKey->get_time() <= t ) {
			// Is the requested time after the last key?
			pCurKey = pLastKey;
		}
		else {
			// interpolate
			for( int32 i = 1; i < m_pCont->get_key_count(); i++ ) {
				FileKeyC*	pKey = (FileKeyC*)m_pCont->get_key( i );
				if( pKey && pKey->get_time() > t ) {
					pCurKey = (FileKeyC*)m_pCont->get_key( i - 1 );
					break;
				}
			}
		}

		if( pCurKey ) {
			pHandle = pCurKey->get_file_handle();
			f32TimeScale = pCurKey->get_time_scale();
			i32TimeOffset = pCurKey->get_time_offset();
		}
	}
	else {
		pHandle = m_pHandle;
		f32TimeScale = m_f32TimeScale;
		i32TimeOffset = m_i32TimeOffset;
	}

	ImportableI*	pImp = 0;

	if( pHandle )
		pImp = pHandle->get_importable();

	if( pImp ) {
		int32	i32Duration = pImp->get_duration();
		return (int32)((float32)i32Duration / f32TimeScale);
	}

	return -1;
}


float32
ParamFileC::get_start_label( int32 t )
{
	FileHandleC*	pHandle = 0;

	if( (get_flags() & ITEM_ANIMATED) && m_pCont && m_pCont->get_key_count() ) {
		int32			i32FileTime = 0;
		m_pCont->get_value( pHandle, i32FileTime, t );
	}
	else {
		pHandle = m_pHandle;
	}

	ImportableI*	pImp = 0;

	if( pHandle )
		pImp = pHandle->get_importable();

	if( pImp ) {
		return pImp->get_start_label();
	}

	return 0;
}

float32
ParamFileC::get_end_label( int32 t )
{
	FileHandleC*	pHandle = 0;

	if( (get_flags() & ITEM_ANIMATED) && m_pCont && m_pCont->get_key_count() ) {
		int32			i32FileTime = 0;
		m_pCont->get_value( pHandle, i32FileTime, t );
	}
	else {
		pHandle = m_pHandle;
	}

	ImportableI*	pImp = 0;

	if( pHandle )
		pImp = pHandle->get_importable();

	if( pImp ) {
		return pImp->get_end_label();
	}

	return 0;
}

uint32
ParamFileC::update_file_handles( FileHandleC* pNewHandle )
{
	bool	bNeedUpdate = false;
	int32	i32Time = 0;

	if( (get_flags() & ITEM_ANIMATED) && m_pCont && m_pCont->get_key_count() ) {
		// update based on the file
		FileKeyC*	pKey;
		for( int32 i = 0; i < m_pCont->get_key_count(); i++ ) {
			pKey = (FileKeyC*)m_pCont->get_key( i );
			if( pKey && pKey->get_file_handle() == pNewHandle ) {
				bNeedUpdate = true;
				i32Time = pKey->get_time();
				break;
			}
		}
	}
	else {
		if( m_pHandle == pNewHandle )
			bNeedUpdate = true;
	}

	if( bNeedUpdate && m_pParent )
		return m_pParent->update_notify( this );

	return 0;
}

void
ParamFileC::update_file_references()
{
	if( get_flags() & ITEM_ANIMATED && m_pCont->get_key_count() ) {
		// update based on the file
		FileKeyC*	pKey;
		for( int32 i = 0; i < m_pCont->get_key_count(); i++ ) {
			pKey = (FileKeyC*)m_pCont->get_key( i );
			if( pKey && pKey->get_file_handle() ) {
				pKey->get_file_handle()->add_reference( this );
			}
		}
	}
	else {
		if( m_pHandle )
			m_pHandle->add_reference( this );
	}
}


uint32
ParamFileC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	std::string	sStr;

	// save the base of the parameter
	pSave->begin_chunk( CHUNK_PARAMETER_BASE, PARAMETER_VERSION );
		ui32Error = ParamI::save( pSave );
	pSave->end_chunk();

	// controller
	if( m_pCont && m_pCont->get_key_count() ) {
		pSave->begin_chunk( CHUNK_PARAMETER_FILE_CONT, PARAMETER_VERSION );
			ui32Error = m_pCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pHandle ) {
		// save int parameter
		pSave->begin_chunk( CHUNK_PARAMETER_FILE, PARAMETER_VERSION );
			uint32	ui32Temp;
			// file handle id
			ui32Temp = m_pHandle->get_id();
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
			// superclass filter
			ui32Temp = m_rSuperClassFilter.get_class_a();
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
			ui32Temp = m_rSuperClassFilter.get_class_b();
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
			// class filter
			ui32Temp = m_rClassFilter.get_class_a();
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
			ui32Temp = m_rClassFilter.get_class_b();
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		pSave->end_chunk();

		pSave->begin_chunk( CHUNK_PARAMETER_FILE_TIME, PARAMETER_VERSION );
			// offset
			ui32Error = pSave->write( &m_i32TimeOffset, sizeof( m_i32TimeOffset ) );
			// scale
			ui32Error = pSave->write( &m_f32TimeScale, sizeof( m_f32TimeScale ) );
		pSave->end_chunk();
	}

	return ui32Error;
}

uint32
ParamFileC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp, ui32Temp2;

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_PARAMETER_FILE:
			{
				if( pLoad->get_chunk_version() == PARAMETER_VERSION_1 ) {
					// file handle id
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
					pLoad->add_file_handle_patch( (void**)&m_pHandle, ui32Temp );
					// superclass filter
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
					m_rSuperClassFilter = SuperClassIdC( 0, ui32Temp );
					// class filter
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
					ui32Error = pLoad->read( &ui32Temp2, sizeof( ui32Temp2 ) );
					m_rClassFilter = ClassIdC( ui32Temp, ui32Temp2 );
				}
				else if( pLoad->get_chunk_version() == PARAMETER_VERSION ) {
					// file handle id
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
					pLoad->add_file_handle_patch( (void**)&m_pHandle, ui32Temp );
					// superclass filter
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
					ui32Error = pLoad->read( &ui32Temp2, sizeof( ui32Temp2 ) );
					m_rSuperClassFilter = SuperClassIdC( ui32Temp, ui32Temp2 );
					// class filter
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
					ui32Error = pLoad->read( &ui32Temp2, sizeof( ui32Temp2 ) );
					m_rClassFilter = ClassIdC( ui32Temp, ui32Temp2 );
				}
			}
			break;

		case CHUNK_PARAMETER_FILE_TIME:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					// offset
					ui32Error = pLoad->read( &m_i32TimeOffset, sizeof( m_i32TimeOffset ) );
					// scale
					ui32Error = pLoad->read( &m_f32TimeScale, sizeof( m_f32TimeScale ) );
				}
			}
			break;

		case CHUNK_PARAMETER_FILE_CONT:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					ui32Error = m_pCont->load( pLoad );
				}
			}
			break;

		case CHUNK_PARAMETER_BASE:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					ui32Error = ParamI::load( pLoad );
				}
			}
			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;

	return IO_OK;
}



//
// ParamLinkC
// - file handle
//


ParamLinkC::ParamLinkC()
{
}

ParamLinkC::ParamLinkC( EditableI* pOriginal ) :
	ParamI( pOriginal )
{
}

ParamLinkC::ParamLinkC( GizmoI* pParent, const char* name, SuperClassIdC rSuperClassFilter, ClassIdC rClassFilter,
					    PajaTypes::uint32 id, PajaTypes::uint32 style ) :
	ParamI( pParent, name, id, style, false ),
	m_rSuperClassFilter( rSuperClassFilter ),
	m_rClassFilter( rClassFilter )
{
	if( style == PARAM_STYLE_MULTI_LINK )
		add_flags( ITEM_EXPANDABLE );
}

ParamLinkC::~ParamLinkC()
{
	if( get_original() )
		return;

	uint32	i;
	// Delete old links
	for( i = 0; i < m_vecLinks.size(); i++ )
		delete m_vecLinks[i];
	m_vecLinks.clear();
}


ParamLinkC*
ParamLinkC::create_new( GizmoI* pParent, const char* name, SuperClassIdC rSuperClassFilter, ClassIdC rClassFilter,
						uint32 id, uint32 style )
{
	return new ParamLinkC( pParent, name, rSuperClassFilter, rClassFilter, id, style );
}



DataBlockI*
ParamLinkC::create()
{
	return new ParamLinkC;
}

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

void
ParamLinkC::copy( EditableI* pEditable )
{
	ParamI::copy( pEditable );
	ParamLinkC*	pParam = (ParamLinkC*)pEditable;

	uint32	i;
	// Delete old links
	for( i = 0; i < m_vecLinks.size(); i++ )
		delete m_vecLinks[i];
	m_vecLinks.clear();

	for( uint32 i = 0; i < pParam->m_vecLinks.size(); i++ )
	{
		LinkS*	pLink = new LinkS;
		pLink->m_pEffect = pParam->m_vecLinks[i]->m_pEffect;
		pLink->m_ui32ID = pParam->m_vecLinks[i]->m_ui32ID;
		m_vecLinks.push_back( pLink );
	}

	m_rSuperClassFilter = pParam->m_rSuperClassFilter;
	m_rClassFilter = pParam->m_rClassFilter;
}

void
ParamLinkC::restore( EditableI* pEditable )
{
	ParamI::restore( pEditable );
	ParamLinkC*	pParam = (ParamLinkC*)pEditable;
	m_vecLinks = pParam->m_vecLinks;
	m_rSuperClassFilter = pParam->m_rSuperClassFilter;
	m_rClassFilter = pParam->m_rClassFilter;
}

uint32
ParamLinkC::get_type() const
{
	return PARAM_TYPE_LINK;
}

void
ParamLinkC::set_controller( ControllerC* cnt )
{
	// empty
}

ControllerC*
ParamLinkC::get_controller()
{
	return 0;
}

void
ParamLinkC::set_style( uint32 style )
{
	if( style == PARAM_STYLE_MULTI_LINK )
		add_flags( ITEM_EXPANDABLE );
	else
		del_flags( ITEM_EXPANDABLE );

	m_ui32Style = style;
}

uint32
ParamLinkC::get_style() const
{
	return m_ui32Style;
}

void
ParamLinkC::set_id( uint32 id )
{
	m_ui32Id = id;
}

uint32
ParamLinkC::get_id() const
{
	return m_ui32Id;
}

void
ParamLinkC::set_increment( float32 inc )
{
	// do nothign
}

float32
ParamLinkC::get_increment() const
{
	return 1.0f;
}

bool
ParamLinkC::get_min_max( PajaTypes::float32* pMin, PajaTypes::float32* pMax )
{
	return false;
}

uint32
ParamLinkC::add_link( EffectI* pEffect )
{
	EffectI*	pCheckedEffect = 0;

	if( pEffect )
	{
		if( (m_rClassFilter == NULL_CLASSID || m_rClassFilter == pEffect->get_class_id()) &&
			(m_rSuperClassFilter == NULL_SUPERCLASS || m_rSuperClassFilter == pEffect->get_super_class_id()) )
			pCheckedEffect = pEffect;
	}

	// Add effect to the list.
	LinkS*	pLink = new LinkS;
	pLink->m_pEffect = pCheckedEffect;
	pLink->m_ui32ID = 0;
	m_vecLinks.push_back( pLink );

	// Notify parent about the change.
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

uint32
ParamLinkC::insert_link( int32 i32InsAt, EffectI* pEffect )
{
	EffectI*	pCheckedEffect = 0;

	if( pEffect )
	{
		if( (m_rClassFilter == NULL_CLASSID || m_rClassFilter == pEffect->get_class_id()) &&
			(m_rSuperClassFilter == NULL_SUPERCLASS || m_rSuperClassFilter == pEffect->get_super_class_id()) )
			pCheckedEffect = pEffect;
	}

	// Add effect to the list.
	LinkS*	pLink = new LinkS;
	pLink->m_pEffect = pCheckedEffect;
	pLink->m_ui32ID = 0;
	m_vecLinks.push_back( pLink );

	if( i32InsAt >= 0 && i32InsAt < (int32)m_vecLinks.size() )
		m_vecLinks.insert( m_vecLinks.begin() + i32InsAt, pLink );
	else
		m_vecLinks.push_back( pLink );

	// Notify parent about the change.
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

uint32
ParamLinkC::del_link( PajaTypes::uint32 ui32Index )
{
	if( ui32Index >= 0 && ui32Index < m_vecLinks.size() )
	{
		delete m_vecLinks[ui32Index];
		m_vecLinks.erase( m_vecLinks.begin() + ui32Index );
	}

	// Notify parent about the change.
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

uint32
ParamLinkC::del_link( EffectI* pEffect )
{
	// Erase all occuraces to pEffect.
	for( std::vector<LinkS*>::iterator LiIt = m_vecLinks.begin(); LiIt != m_vecLinks.end(); )
	{
		if( (*LiIt)->m_pEffect == pEffect )
		{
			delete (*LiIt);
			LiIt = m_vecLinks.erase( LiIt );
		}
		else
			++LiIt;
	}

	// Notify parent about the change.
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

uint32
ParamLinkC::del_all_links()
{
	uint32	i;
	// Delete old links
	for( i = 0; i < m_vecLinks.size(); i++ )
		delete m_vecLinks[i];
	m_vecLinks.clear();

	// Notify parent about the change.
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

uint32
ParamLinkC::set_link( PajaTypes::uint32 ui32Index, EffectI* pEffect )
{
	EffectI*	pCheckedEffect = 0;

	if( pEffect )
	{
		if( (m_rClassFilter == NULL_CLASSID || m_rClassFilter == pEffect->get_class_id()) &&
			(m_rSuperClassFilter == NULL_SUPERCLASS || m_rSuperClassFilter == pEffect->get_super_class_id()) )
			pCheckedEffect = pEffect;
	}

	// Add effect to the list.
	if( ui32Index >= 0 && ui32Index < m_vecLinks.size() )
    m_vecLinks[ui32Index]->m_pEffect = pCheckedEffect;

	// Notify parent about the change.
	if( m_pParent )
		return m_pParent->update_notify( this );

	return PARAM_NOTIFY_NONE;
}

uint32
ParamLinkC::get_link_count() const
{
	return (int)m_vecLinks.size();
}

EffectI*
ParamLinkC::get_link( PajaTypes::uint32 ui32Index )
{
	if( ui32Index >= 0 && ui32Index < m_vecLinks.size() )
    return m_vecLinks[ui32Index]->m_pEffect;

	return 0;
}


SuperClassIdC
ParamLinkC::get_super_class_filter()
{
	return m_rSuperClassFilter;
}

ClassIdC
ParamLinkC::get_class_filter()
{
	return m_rClassFilter;
}

void
ParamLinkC::set_super_class_filter( SuperClassIdC rSuperClassId )
{
	m_rSuperClassFilter = rSuperClassId;
}

void
ParamLinkC::set_class_filter( ClassIdC rClassId )
{
	m_rClassFilter = rClassId;
}

void
ParamLinkC::update_link_references()
{
	for( uint32 i = 0; i < m_vecLinks.size(); i++ )
	{
		if( m_vecLinks[i]->m_pEffect )
			m_vecLinks[i]->m_pEffect->add_reference( this );
	}
}

uint32
ParamLinkC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	std::string	sStr;

	// save the base of the parameter
	pSave->begin_chunk( CHUNK_PARAMETER_BASE, PARAMETER_VERSION );
		ui32Error = ParamI::save( pSave );
	pSave->end_chunk();

	// Save links
	for( int32 i = 0; i < (int32)m_vecLinks.size(); i++ )
	{
		// save int parameter
		EffectI*	pEffect = m_vecLinks[i]->m_pEffect;
		if( pEffect )
		{
			// Find effects order in the parent layer.
			LayerC*	pLayer = pEffect->get_parent();

			uint32	ui32EffectID = 0xffffffff;

			if( pLayer )
			{
				for( int32 j = 0; j < pLayer->get_effect_count(); j++ )
				{
					if( pLayer->get_effect( j ) == pEffect )
					{
						ui32EffectID = j;
						break;
					}
				}
			}

			pSave->begin_chunk( CHUNK_PARAMETER_LINK, PARAMETER_VERSION );
				// file handle id
				ui32Error = pSave->write( &ui32EffectID, sizeof( ui32EffectID ) );
			pSave->end_chunk();
		}
	}

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_PARAMETER_LINK:
			{
				if( pLoad->get_chunk_version() == PARAMETER_VERSION ) {
					// file handle id
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );

					// Find layer
					GizmoI*	pGizmo = m_pParent;
					EffectI*	pEffect = 0;
					LayerC*	pLayer = 0;

					if( pGizmo )
						pEffect = pGizmo->get_parent();
					if( pEffect )
						pLayer = pEffect->get_parent();

					LinkS*	pLink = new LinkS;
					pLink->m_pEffect = 0;
					pLink->m_ui32ID = ui32Temp;
					m_vecLinks.push_back( pLink );

					if( pLayer )
						pLoad->add_effect_patch( (void**)&pLink->m_pEffect, (void*)pLayer, ui32Temp );
				}
			}
			break;

		case CHUNK_PARAMETER_BASE:
			{
				if( pLoad->get_chunk_version() <= PARAMETER_VERSION ) {
					ui32Error = ParamI::load( pLoad );
				}
			}
			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;

	return IO_OK;
}
