
// Insert your headers here
#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <stdio.h>
#include <string>
#include "TextPlugin.h"

#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ParamI.h"
#include "EditableI.h"
#include "UndoC.h"
#include "ImportableImageI.h"
#include "BBox2C.h"
#include "Matrix2C.h"
#include "DeviceContextC.h"
#include "DeviceInterfaceI.h"
#include "DemoInterfaceC.h"
#include "OpenGLViewportC.h"
#include "OpenGLDeviceC.h"

#include "minilzo.h"


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

// class descriptors
TextDescC		g_rTextDesc;
MOFOImportDescC	g_rMOFOImportDesc;



static
void
TRACE( const char* szFormat, ...  )
{
	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );
}


#ifndef PAJAPLAYER

//
// The DLL
//

BOOL APIENTRY
DllMain( HANDLE hModule, DWORD ulReasonForCall, LPVOID lpReserved )
{
    switch( ulReasonForCall )
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}



__declspec( dllexport )
int32
get_classdesc_count()
{
	return 2;
}

__declspec( dllexport )
ClassDescC*
get_classdesc( int32 i )
{
	if( i == 0 )
		return &g_rTextDesc;
	else if( i == 1 )
		return &g_rMOFOImportDesc;
	return 0;
}

__declspec( dllexport )
int32
get_api_version()
{
	return DEMOPAJA_VERSION;
}

__declspec( dllexport )
char*
get_dll_name()
{
	return "textplugin.dll - Text Effects (c)2000 memon/moppi productions";
}

#endif

//
// Old Gizmos
//

TextGizmoC::TextGizmoC()
{
	init();
}

TextGizmoC::TextGizmoC( EffectI* pParent, uint32 ui32Id ) :
	GizmoI( pParent, ui32Id )
{
	init();
}

TextGizmoC::TextGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
//	init();
}

void
TextGizmoC::init()
{
	set_name( "Transform" );

	m_pParamPos = ParamVector2C::create_new( this, "Position", Vector2C(), 1, PARAM_STYLE_EDITBOX | PARAM_STYLE_ABS_POSITION, PARAM_ANIMATABLE );
	m_pParamPivot = ParamVector2C::create_new( this, "Pivot", Vector2C(), 2, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_WORLD_SPACE, PARAM_ANIMATABLE );
	m_pParamScale = ParamVector2C::create_new( this, "Scale", Vector2C( 1, 1 ), 4, PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f );
	m_pParamRot = ParamFloatC::create_new( this, "Rotation", 0, 3, PARAM_STYLE_EDITBOX | PARAM_STYLE_ANGLE, PARAM_ANIMATABLE, 0, 0, 1.0f );
	m_pParamColor = ParamColorC::create_new( this, "Color", ColorC( 1, 1, 1, 1 ), 5, PARAM_STYLE_COLORPICKER_RGBA, PARAM_ANIMATABLE );
	m_pParamFile = ParamFileC::create_new( this, "Font", NULL_SUPERCLASS, CLASS_MOFO_IMPORT, 7 );
}

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

	m_pParamPos->release();
	m_pParamPivot->release();
	m_pParamScale->release();
	m_pParamRot->release();
	m_pParamColor->release();
	m_pParamFile->release();
}


TextGizmoC*
TextGizmoC::create_new( EffectI* pParent, uint32 ui32Id )
{
	return new TextGizmoC( pParent, ui32Id );
}

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

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

void
TextGizmoC::copy( EditableI* pEditable )
{
	GizmoI::copy( pEditable );

	TextGizmoC*	pGizmo = (TextGizmoC*)pEditable;
	m_pParamPos->copy( pGizmo->m_pParamPos );
	m_pParamPivot->copy( pGizmo->m_pParamPivot );
	m_pParamScale->copy( pGizmo->m_pParamScale );
	m_pParamColor->copy( pGizmo->m_pParamColor );
	m_pParamFile->copy( pGizmo->m_pParamFile );
	m_pParamRot->copy( pGizmo->m_pParamRot );
}

void
TextGizmoC::restore( EditableI* pEditable )
{
	GizmoI::restore( pEditable );

	TextGizmoC*	pGizmo = (TextGizmoC*)pEditable;
	m_pParamPos =  pGizmo->m_pParamPos;
	m_pParamPivot = pGizmo->m_pParamPivot;
	m_pParamScale = pGizmo->m_pParamScale;
	m_pParamColor = pGizmo->m_pParamColor;
	m_pParamFile = pGizmo->m_pParamFile;
	m_pParamRot = pGizmo->m_pParamRot;
}


PajaTypes::int32
TextGizmoC::get_parameter_count()
{
	return TEXT_PARAM_COUNT;
}

ParamI*
TextGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case TEXT_PARAM_POS:
		return m_pParamPos; break;
	case TEXT_PARAM_PIVOT:
		return m_pParamPivot; break;
	case TEXT_PARAM_SCALE:
		return m_pParamScale; break;
	case TEXT_PARAM_ROT:
		return m_pParamRot; break;
	case TEXT_PARAM_COLOR:
		return m_pParamColor; break;
	case TEXT_PARAM_FILE:
		return m_pParamFile; break;
	}
	return 0;
}


MOFOImportC*
TextGizmoC::get_file( int32 i32Time, DeviceContextC* pContext, TimeContextC* pTimeContext )
{
	FileHandleC*	pHandle = 0;
	int32			i32FileTime = 0;
	m_pParamFile->get_file( i32Time, pHandle, i32FileTime );
	if( pHandle ) {
		MOFOImportC*	pImp = (MOFOImportC*)pHandle->get_importable();
		pImp->eval_state( i32FileTime );
		return pImp;
	}
	return 0;

}


Vector2C
TextGizmoC::get_pos( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamPos->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
TextGizmoC::get_pivot( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamPivot->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
TextGizmoC::get_scale( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamScale->get_val( i32Time, rVal );
	return rVal;
}

float32
TextGizmoC::get_rot( int32 i32Time )
{
	float32	f32Val;
	m_pParamRot->get_val( i32Time, f32Val );
	return f32Val;
}

ColorC
TextGizmoC::get_color( int32 i32Time )
{
	ColorC	rVal;
	m_pParamColor->get_val( i32Time, rVal );
	return rVal;
}



enum TextGizmoChunksE {
	CHUNK_TEXTGIZMO_BASE				= 0x1000,
	CHUNK_TEXTGIZMO_PARAM_POS			= 0x2000,
	CHUNK_TEXTGIZMO_PARAM_PIVOT			= 0x3000,
	CHUNK_TEXTGIZMO_PARAM_SCALE			= 0x5000,
	CHUNK_TEXTGIZMO_PARAM_COLOR			= 0x6000,
	CHUNK_TEXTGIZMO_PARAM_FILE			= 0x8000,
	CHUNK_TEXTGIZMO_PARAM_ROT			= 0x9000,
};

const uint32	TEXTGIZMO_VERSION = 1;



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

	// GizmoI stuff
	pSave->begin_chunk( CHUNK_TEXTGIZMO_BASE, TEXTGIZMO_VERSION );
		ui32Error = GizmoI::save( pSave );
	pSave->end_chunk();

	// position
	pSave->begin_chunk( CHUNK_TEXTGIZMO_PARAM_POS, TEXTGIZMO_VERSION );
		ui32Error = m_pParamPos->save( pSave );
	pSave->end_chunk();

	// pivot
	pSave->begin_chunk( CHUNK_TEXTGIZMO_PARAM_PIVOT, TEXTGIZMO_VERSION );
		ui32Error = m_pParamPivot->save( pSave );
	pSave->end_chunk();

	// scale
	pSave->begin_chunk( CHUNK_TEXTGIZMO_PARAM_SCALE, TEXTGIZMO_VERSION );
		ui32Error = m_pParamScale->save( pSave );
	pSave->end_chunk();

	// rotation
	pSave->begin_chunk( CHUNK_TEXTGIZMO_PARAM_ROT, TEXTGIZMO_VERSION );
		ui32Error = m_pParamRot->save( pSave );
	pSave->end_chunk();

	// color
	pSave->begin_chunk( CHUNK_TEXTGIZMO_PARAM_COLOR, TEXTGIZMO_VERSION );
		ui32Error = m_pParamColor->save( pSave );
	pSave->end_chunk();

	// file
	pSave->begin_chunk( CHUNK_TEXTGIZMO_PARAM_FILE, TEXTGIZMO_VERSION );
		ui32Error = m_pParamFile->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_TEXTGIZMO_BASE:
			{
//				OutputDebugString( "        g_base\n" );
				if( pLoad->get_chunk_version() == TEXTGIZMO_VERSION )
					ui32Error = GizmoI::load( pLoad );
			}
			break;

		case CHUNK_TEXTGIZMO_PARAM_POS:
			{
//				OutputDebugString( "        g_pos\n" );
				if( pLoad->get_chunk_version() == TEXTGIZMO_VERSION )
					ui32Error = m_pParamPos->load( pLoad );
			}
			break;

		case CHUNK_TEXTGIZMO_PARAM_PIVOT:
			{
//				OutputDebugString( "        g_pivot\n" );
				if( pLoad->get_chunk_version() == TEXTGIZMO_VERSION )
					ui32Error = m_pParamPivot->load( pLoad );
			}
			break;

		case CHUNK_TEXTGIZMO_PARAM_SCALE:
			{
//				OutputDebugString( "        g_scale\n" );
				if( pLoad->get_chunk_version() == TEXTGIZMO_VERSION )
					ui32Error = m_pParamScale->load( pLoad );
			}
			break;

		case CHUNK_TEXTGIZMO_PARAM_ROT:
			{
				if( pLoad->get_chunk_version() == TEXTGIZMO_VERSION )
					ui32Error = m_pParamRot->load( pLoad );
			}
			break;

		case CHUNK_TEXTGIZMO_PARAM_COLOR:
			{
//				OutputDebugString( "        g_color\n" );
				if( pLoad->get_chunk_version() == TEXTGIZMO_VERSION )
					ui32Error = m_pParamColor->load( pLoad );
			}
			break;

		case CHUNK_TEXTGIZMO_PARAM_FILE:
			{
//				OutputDebugString( "        g_file\n" );
				if( pLoad->get_chunk_version() == TEXTGIZMO_VERSION )
					ui32Error = m_pParamFile->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;
}



TextAnimGizmoC::TextAnimGizmoC()
{
	init();
}

TextAnimGizmoC::TextAnimGizmoC( EffectI* pParent, uint32 ui32Id ) :
	GizmoI( pParent, ui32Id )
{
	init();
}

TextAnimGizmoC::TextAnimGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
//	init();
}

void
TextAnimGizmoC::init()
{
	set_name( "Text Anim" );

	m_pParamOffset = ParamVector2C::create_new( this, "Offset", Vector2C(), 1, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_OBJECT_SPACE, PARAM_ANIMATABLE );
	m_pParamScale = ParamVector2C::create_new( this, "Scale", Vector2C( 1, 1 ), 4, PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f );
	m_pParamColor = ParamColorC::create_new( this, "Color", ColorC( 1, 1, 1, 1 ), 5, PARAM_STYLE_COLORPICKER_RGBA, PARAM_ANIMATABLE );
	m_pParamText = ParamTextC::create_new( this, "Text", "Text", 6, PARAM_STYLE_EDITBOX );
	m_pParamDelay = ParamIntC::create_new( this, "Delay", 256, 7, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE );
}

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

	m_pParamOffset->release();
	m_pParamScale->release();
	m_pParamColor->release();
	m_pParamText->release();
	m_pParamDelay->release();
}


TextAnimGizmoC*
TextAnimGizmoC::create_new( EffectI* pParent, uint32 ui32Id )
{
	return new TextAnimGizmoC( pParent, ui32Id );
}

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

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

void
TextAnimGizmoC::copy( EditableI* pEditable )
{
	GizmoI::copy( pEditable );

	TextAnimGizmoC*	pGizmo = (TextAnimGizmoC*)pEditable;
	m_pParamOffset->copy( pGizmo->m_pParamOffset );
	m_pParamScale->copy( pGizmo->m_pParamScale );
	m_pParamColor->copy( pGizmo->m_pParamColor );
	m_pParamText->copy( pGizmo->m_pParamText );
	m_pParamDelay->copy( pGizmo->m_pParamDelay );
}

void
TextAnimGizmoC::restore( EditableI* pEditable )
{
	GizmoI::restore( pEditable );

	TextAnimGizmoC*	pGizmo = (TextAnimGizmoC*)pEditable;
	m_pParamOffset =  pGizmo->m_pParamOffset;
	m_pParamScale =  pGizmo->m_pParamScale;
	m_pParamColor =  pGizmo->m_pParamColor;
	m_pParamText =  pGizmo->m_pParamText;
	m_pParamDelay =  pGizmo->m_pParamDelay;
}


PajaTypes::int32
TextAnimGizmoC::get_parameter_count()
{
	return TEXTANIM_PARAM_COUNT;
}

ParamI*
TextAnimGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case TEXTANIM_PARAM_OFFSET:
		return m_pParamOffset; break;
	case TEXTANIM_PARAM_SCALE:
		return m_pParamScale; break;
	case TEXTANIM_PARAM_COLOR:
		return m_pParamColor; break;
	case TEXTANIM_PARAM_TEXT:
		return m_pParamText; break;
	case TEXTANIM_PARAM_DELAY:
		return m_pParamDelay; break;
	}
	return 0;
}


Vector2C
TextAnimGizmoC::get_offset( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamOffset->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
TextAnimGizmoC::get_scale( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamScale->get_val( i32Time, rVal );
	return rVal;
}

ColorC
TextAnimGizmoC::get_color( int32 i32Time )
{
	ColorC	rVal;
	m_pParamColor->get_val( i32Time, rVal );
	return rVal;
}

int32
TextAnimGizmoC::get_delay( int32 i32Time )
{
	int32	i32Val;
	m_pParamDelay->get_val( i32Time, i32Val );
	return i32Val;
}

const char*
TextAnimGizmoC::get_text( int32 i32Time )
{
	return m_pParamText->get_val( i32Time );
}

int32
TextAnimGizmoC::get_offset_range()
{
	ControllerC*	pCont = m_pParamOffset->get_controller();
	int32	i32Val = 0;
	if( pCont ) {
		i32Val = pCont->get_max_time() - pCont->get_min_time();
	}
	if( i32Val < 1 )
		i32Val = 1;
	return i32Val;
}

int32
TextAnimGizmoC::get_scale_range()
{
	ControllerC*	pCont = m_pParamScale->get_controller();
	int32	i32Val = 0;
	if( pCont ) {
		i32Val = pCont->get_max_time() - pCont->get_min_time();
	}
	if( i32Val < 1 )
		i32Val = 1;
	return i32Val;
}

int32
TextAnimGizmoC::get_color_range()
{
	ControllerC*	pCont = m_pParamColor->get_controller();
	int32	i32Val = 0;
	if( pCont ) {
		i32Val = pCont->get_max_time() - pCont->get_min_time();
	}
	if( i32Val < 1 )
		i32Val = 1;
	return i32Val;
}



enum TextAnimGizmoChunksE {
	CHUNK_TEXTANIMGIZMO_BASE				= 0x1000,
	CHUNK_TEXTANIMGIZMO_PARAM_OFFSET		= 0x2000,
	CHUNK_TEXTANIMGIZMO_PARAM_SCALE			= 0x3000,
	CHUNK_TEXTANIMGIZMO_PARAM_COLOR			= 0x4000,
	CHUNK_TEXTANIMGIZMO_PARAM_DELAY			= 0x5000,
	CHUNK_TEXTANIMGIZMO_PARAM_TEXT			= 0x6000,
};

const uint32	TEXTANIMGIZMO_VERSION = 1;



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

	// GizmoI stuff
	pSave->begin_chunk( CHUNK_TEXTANIMGIZMO_BASE, TEXTANIMGIZMO_VERSION );
		ui32Error = GizmoI::save( pSave );
	pSave->end_chunk();

	// offset
	pSave->begin_chunk( CHUNK_TEXTANIMGIZMO_PARAM_OFFSET, TEXTANIMGIZMO_VERSION );
		ui32Error = m_pParamOffset->save( pSave );
	pSave->end_chunk();

	// scale
	pSave->begin_chunk( CHUNK_TEXTANIMGIZMO_PARAM_SCALE, TEXTANIMGIZMO_VERSION );
		ui32Error = m_pParamScale->save( pSave );
	pSave->end_chunk();

	// color
	pSave->begin_chunk( CHUNK_TEXTANIMGIZMO_PARAM_COLOR, TEXTANIMGIZMO_VERSION );
		ui32Error = m_pParamColor->save( pSave );
	pSave->end_chunk();

	// delay
	pSave->begin_chunk( CHUNK_TEXTANIMGIZMO_PARAM_DELAY, TEXTANIMGIZMO_VERSION );
		ui32Error = m_pParamDelay->save( pSave );
	pSave->end_chunk();

	// text
	pSave->begin_chunk( CHUNK_TEXTANIMGIZMO_PARAM_TEXT, TEXTANIMGIZMO_VERSION );
		ui32Error = m_pParamText->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_TEXTANIMGIZMO_BASE:
			{
//				OutputDebugString( "        g_base\n" );
				if( pLoad->get_chunk_version() == TEXTANIMGIZMO_VERSION )
					ui32Error = GizmoI::load( pLoad );
			}
			break;

		case CHUNK_TEXTANIMGIZMO_PARAM_OFFSET:
			{
//				OutputDebugString( "        g_pivot\n" );
				if( pLoad->get_chunk_version() == TEXTANIMGIZMO_VERSION )
					ui32Error = m_pParamOffset->load( pLoad );
			}
			break;

		case CHUNK_TEXTANIMGIZMO_PARAM_SCALE:
			{
//				OutputDebugString( "        g_scale\n" );
				if( pLoad->get_chunk_version() == TEXTANIMGIZMO_VERSION )
					ui32Error = m_pParamScale->load( pLoad );
			}
			break;

		case CHUNK_TEXTANIMGIZMO_PARAM_COLOR:
			{
//				OutputDebugString( "        g_color\n" );
				if( pLoad->get_chunk_version() == TEXTANIMGIZMO_VERSION )
					ui32Error = m_pParamColor->load( pLoad );
			}
			break;

		case CHUNK_TEXTANIMGIZMO_PARAM_DELAY:
			{
//				OutputDebugString( "        g_color\n" );
				if( pLoad->get_chunk_version() == TEXTANIMGIZMO_VERSION )
					ui32Error = m_pParamDelay->load( pLoad );
			}
			break;

		case CHUNK_TEXTANIMGIZMO_PARAM_TEXT:
			{
//				OutputDebugString( "        g_color\n" );
				if( pLoad->get_chunk_version() == TEXTANIMGIZMO_VERSION )
					ui32Error = m_pParamText->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;
}


//
// transform gizmo
//

TransformGizmoC::TransformGizmoC()
{
	init();
}

TransformGizmoC::TransformGizmoC( EffectI* pParent, uint32 ui32Id ) :
	GizmoI( pParent, ui32Id )
{
	init();
}

TransformGizmoC::TransformGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	// empty
}

void
TransformGizmoC::init()
{
	set_name( "Transform" );

	m_pParamPos = ParamVector2C::create_new( this, "Position", Vector2C(), 1, PARAM_STYLE_EDITBOX | PARAM_STYLE_ABS_POSITION, PARAM_ANIMATABLE );
	m_pParamPivot = ParamVector2C::create_new( this, "Pivot", Vector2C(), 2, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_WORLD_SPACE, PARAM_ANIMATABLE );
	m_pParamScale = ParamVector2C::create_new( this, "Scale", Vector2C( 1, 1 ), 4, PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f );
	m_pParamRot = ParamFloatC::create_new( this, "Rotation", 0, 3, PARAM_STYLE_EDITBOX | PARAM_STYLE_ANGLE, PARAM_ANIMATABLE, 0, 0, 1.0f );
}

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

	m_pParamPos->release();
	m_pParamPivot->release();
	m_pParamScale->release();
	m_pParamRot->release();
}


TransformGizmoC*
TransformGizmoC::create_new( EffectI* pParent, uint32 ui32Id )
{
	return new TransformGizmoC( pParent, ui32Id );
}

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

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

void
TransformGizmoC::copy( EditableI* pEditable )
{
	GizmoI::copy( pEditable );

	TransformGizmoC*	pGizmo = (TransformGizmoC*)pEditable;
	m_pParamPos->copy( pGizmo->m_pParamPos );
	m_pParamPivot->copy( pGizmo->m_pParamPivot );
	m_pParamScale->copy( pGizmo->m_pParamScale );
	m_pParamRot->copy( pGizmo->m_pParamRot );
}

void
TransformGizmoC::restore( EditableI* pEditable )
{
	GizmoI::restore( pEditable );

	TransformGizmoC*	pGizmo = (TransformGizmoC*)pEditable;
	m_pParamPos =  pGizmo->m_pParamPos;
	m_pParamPivot = pGizmo->m_pParamPivot;
	m_pParamScale = pGizmo->m_pParamScale;
	m_pParamRot = pGizmo->m_pParamRot;
}


PajaTypes::int32
TransformGizmoC::get_parameter_count()
{
	return TRANSFORM_PARAM_COUNT;
}

ParamI*
TransformGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case TRANSFORM_PARAM_POS:
		return m_pParamPos; break;
	case TRANSFORM_PARAM_PIVOT:
		return m_pParamPivot; break;
	case TRANSFORM_PARAM_SCALE:
		return m_pParamScale; break;
	case TRANSFORM_PARAM_ROT:
		return m_pParamRot; break;
	}
	return 0;
}


Vector2C
TransformGizmoC::get_pos( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamPos->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
TransformGizmoC::get_pivot( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamPivot->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
TransformGizmoC::get_scale( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamScale->get_val( i32Time, rVal );
	return rVal;
}

float32
TransformGizmoC::get_rot( int32 i32Time )
{
	float32	f32Val;
	m_pParamRot->get_val( i32Time, f32Val );
	return f32Val;
}



enum TransformGizmoChunksE {
	CHUNK_TRANSFORMGIZMO_BASE				= 0x1000,
	CHUNK_TRANSFORMGIZMO_PARAM_POS			= 0x2000,
	CHUNK_TRANSFORMGIZMO_PARAM_PIVOT		= 0x3000,
	CHUNK_TRANSFORMGIZMO_PARAM_SCALE		= 0x4000,
	CHUNK_TRANSFORMGIZMO_PARAM_ROT			= 0x5000,
};

const uint32	TRANSFORMGIZMO_VERSION = 1;



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

	// GizmoI stuff
	pSave->begin_chunk( CHUNK_TRANSFORMGIZMO_BASE, TRANSFORMGIZMO_VERSION );
		ui32Error = GizmoI::save( pSave );
	pSave->end_chunk();

	// position
	pSave->begin_chunk( CHUNK_TRANSFORMGIZMO_PARAM_POS, TRANSFORMGIZMO_VERSION );
		ui32Error = m_pParamPos->save( pSave );
	pSave->end_chunk();

	// pivot
	pSave->begin_chunk( CHUNK_TRANSFORMGIZMO_PARAM_PIVOT, TRANSFORMGIZMO_VERSION );
		ui32Error = m_pParamPivot->save( pSave );
	pSave->end_chunk();

	// scale
	pSave->begin_chunk( CHUNK_TRANSFORMGIZMO_PARAM_SCALE, TRANSFORMGIZMO_VERSION );
		ui32Error = m_pParamScale->save( pSave );
	pSave->end_chunk();

	// rotation
	pSave->begin_chunk( CHUNK_TRANSFORMGIZMO_PARAM_ROT, TRANSFORMGIZMO_VERSION );
		ui32Error = m_pParamRot->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_TRANSFORMGIZMO_BASE:
			{
				if( pLoad->get_chunk_version() == TRANSFORMGIZMO_VERSION )
					ui32Error = GizmoI::load( pLoad );
			}
			break;

		case CHUNK_TRANSFORMGIZMO_PARAM_POS:
			{
				if( pLoad->get_chunk_version() == TRANSFORMGIZMO_VERSION )
					ui32Error = m_pParamPos->load( pLoad );
			}
			break;

		case CHUNK_TRANSFORMGIZMO_PARAM_PIVOT:
			{
				if( pLoad->get_chunk_version() == TRANSFORMGIZMO_VERSION )
					ui32Error = m_pParamPivot->load( pLoad );
			}
			break;

		case CHUNK_TRANSFORMGIZMO_PARAM_SCALE:
			{
				if( pLoad->get_chunk_version() == TRANSFORMGIZMO_VERSION )
					ui32Error = m_pParamScale->load( pLoad );
			}
			break;

		case CHUNK_TRANSFORMGIZMO_PARAM_ROT:
			{
				if( pLoad->get_chunk_version() == TRANSFORMGIZMO_VERSION )
					ui32Error = m_pParamRot->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;
}


//
// nimation gizmo
//

AnimationGizmoC::AnimationGizmoC()
{
	init();
}

AnimationGizmoC::AnimationGizmoC( EffectI* pParent, uint32 ui32Id ) :
	GizmoI( pParent, ui32Id )
{
	init();
}

AnimationGizmoC::AnimationGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	// empty
}

void
AnimationGizmoC::init()
{
	set_name( "Animation" );

	m_pParamOffset = ParamVector2C::create_new( this, "Offset", Vector2C(), 1, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_OBJECT_SPACE, PARAM_ANIMATABLE );
	m_pParamScale = ParamVector2C::create_new( this, "Scale", Vector2C( 1, 1 ), 4, PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f );
	m_pParamColor = ParamColorC::create_new( this, "Color", ColorC( 1, 1, 1, 1 ), 5, PARAM_STYLE_COLORPICKER_RGBA, PARAM_ANIMATABLE );
	m_pParamDelay = ParamIntC::create_new( this, "Delay", 256, 7, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE );
}

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

	m_pParamOffset->release();
	m_pParamScale->release();
	m_pParamColor->release();
	m_pParamDelay->release();
}


AnimationGizmoC*
AnimationGizmoC::create_new( EffectI* pParent, uint32 ui32Id )
{
	return new AnimationGizmoC( pParent, ui32Id );
}

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

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

void
AnimationGizmoC::copy( EditableI* pEditable )
{
	GizmoI::copy( pEditable );

	AnimationGizmoC*	pGizmo = (AnimationGizmoC*)pEditable;
	m_pParamOffset->copy( pGizmo->m_pParamOffset );
	m_pParamScale->copy( pGizmo->m_pParamScale );
	m_pParamColor->copy( pGizmo->m_pParamColor );
	m_pParamDelay->copy( pGizmo->m_pParamDelay );
}

void
AnimationGizmoC::restore( EditableI* pEditable )
{
	GizmoI::restore( pEditable );

	AnimationGizmoC*	pGizmo = (AnimationGizmoC*)pEditable;
	m_pParamOffset =  pGizmo->m_pParamOffset;
	m_pParamScale =  pGizmo->m_pParamScale;
	m_pParamColor =  pGizmo->m_pParamColor;
	m_pParamDelay =  pGizmo->m_pParamDelay;
}


PajaTypes::int32
AnimationGizmoC::get_parameter_count()
{
	return ANIMATION_PARAM_COUNT;
}

ParamI*
AnimationGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case ANIMATION_PARAM_OFFSET:
		return m_pParamOffset; break;
	case ANIMATION_PARAM_SCALE:
		return m_pParamScale; break;
	case ANIMATION_PARAM_COLOR:
		return m_pParamColor; break;
	case ANIMATION_PARAM_DELAY:
		return m_pParamDelay; break;
	}
	return 0;
}


Vector2C
AnimationGizmoC::get_offset( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamOffset->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
AnimationGizmoC::get_scale( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamScale->get_val( i32Time, rVal );
	return rVal;
}

ColorC
AnimationGizmoC::get_color( int32 i32Time )
{
	ColorC	rVal;
	m_pParamColor->get_val( i32Time, rVal );
	return rVal;
}

int32
AnimationGizmoC::get_delay( int32 i32Time )
{
	int32	i32Val;
	m_pParamDelay->get_val( i32Time, i32Val );
	return i32Val;
}

int32
AnimationGizmoC::get_offset_range()
{
	ControllerC*	pCont = m_pParamOffset->get_controller();
	int32	i32Val = 0;
	if( pCont ) {
		i32Val = pCont->get_max_time() - pCont->get_min_time();
	}
	if( i32Val < 1 )
		i32Val = 1;
	return i32Val;
}

int32
AnimationGizmoC::get_scale_range()
{
	ControllerC*	pCont = m_pParamScale->get_controller();
	int32	i32Val = 0;
	if( pCont ) {
		i32Val = pCont->get_max_time() - pCont->get_min_time();
	}
	if( i32Val < 1 )
		i32Val = 1;
	return i32Val;
}

int32
AnimationGizmoC::get_color_range()
{
	ControllerC*	pCont = m_pParamColor->get_controller();
	int32	i32Val = 0;
	if( pCont ) {
		i32Val = pCont->get_max_time() - pCont->get_min_time();
	}
	if( i32Val < 1 )
		i32Val = 1;
	return i32Val;
}



enum AnimationGizmoChunksE {
	CHUNK_ANIMATIOGIZMO_BASE				= 0x1000,
	CHUNK_ANIMATIOGIZMO_PARAM_OFFSET		= 0x2000,
	CHUNK_ANIMATIOGIZMO_PARAM_SCALE			= 0x3000,
	CHUNK_ANIMATIOGIZMO_PARAM_COLOR			= 0x4000,
	CHUNK_ANIMATIOGIZMO_PARAM_DELAY			= 0x5000,
};

const uint32	ANIMATIOGIZMO_VERSION = 1;



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

	// GizmoI stuff
	pSave->begin_chunk( CHUNK_ANIMATIOGIZMO_BASE, ANIMATIOGIZMO_VERSION );
		ui32Error = GizmoI::save( pSave );
	pSave->end_chunk();

	// offset
	pSave->begin_chunk( CHUNK_ANIMATIOGIZMO_PARAM_OFFSET, ANIMATIOGIZMO_VERSION );
		ui32Error = m_pParamOffset->save( pSave );
	pSave->end_chunk();

	// scale
	pSave->begin_chunk( CHUNK_ANIMATIOGIZMO_PARAM_SCALE, ANIMATIOGIZMO_VERSION );
		ui32Error = m_pParamScale->save( pSave );
	pSave->end_chunk();

	// color
	pSave->begin_chunk( CHUNK_ANIMATIOGIZMO_PARAM_COLOR, ANIMATIOGIZMO_VERSION );
		ui32Error = m_pParamColor->save( pSave );
	pSave->end_chunk();

	// delay
	pSave->begin_chunk( CHUNK_ANIMATIOGIZMO_PARAM_DELAY, ANIMATIOGIZMO_VERSION );
		ui32Error = m_pParamDelay->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_ANIMATIOGIZMO_BASE:
			{
//				OutputDebugString( "        g_base\n" );
				if( pLoad->get_chunk_version() == ANIMATIOGIZMO_VERSION )
					ui32Error = GizmoI::load( pLoad );
			}
			break;

		case CHUNK_ANIMATIOGIZMO_PARAM_OFFSET:
			{
//				OutputDebugString( "        g_pivot\n" );
				if( pLoad->get_chunk_version() == ANIMATIOGIZMO_VERSION )
					ui32Error = m_pParamOffset->load( pLoad );
			}
			break;

		case CHUNK_ANIMATIOGIZMO_PARAM_SCALE:
			{
//				OutputDebugString( "        g_scale\n" );
				if( pLoad->get_chunk_version() == ANIMATIOGIZMO_VERSION )
					ui32Error = m_pParamScale->load( pLoad );
			}
			break;

		case CHUNK_ANIMATIOGIZMO_PARAM_COLOR:
			{
//				OutputDebugString( "        g_color\n" );
				if( pLoad->get_chunk_version() == ANIMATIOGIZMO_VERSION )
					ui32Error = m_pParamColor->load( pLoad );
			}
			break;

		case CHUNK_ANIMATIOGIZMO_PARAM_DELAY:
			{
//				OutputDebugString( "        g_color\n" );
				if( pLoad->get_chunk_version() == ANIMATIOGIZMO_VERSION )
					ui32Error = m_pParamDelay->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;
}

//
// attribute gizmo
//

AttributeGizmoC::AttributeGizmoC()
{
	init();
}

AttributeGizmoC::AttributeGizmoC( EffectI* pParent, uint32 ui32Id ) :
	GizmoI( pParent, ui32Id )
{
	init();
}

AttributeGizmoC::AttributeGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	// empty
}

void
AttributeGizmoC::init()
{
	set_name( "Attributes" );

	m_pParamText = ParamTextC::create_new( this, "Text", "Text", 6, PARAM_STYLE_EDITBOX );
	m_pParamColor = ParamColorC::create_new( this, "Color", ColorC( 1, 1, 1, 1 ), 5, PARAM_STYLE_COLORPICKER_RGBA, PARAM_ANIMATABLE );
	m_pParamFile = ParamFileC::create_new( this, "Font", NULL_SUPERCLASS, CLASS_MOFO_IMPORT, 7, PARAM_ANIMATABLE );
}

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

	m_pParamColor->release();
	m_pParamFile->release();
	m_pParamText->release();
}


AttributeGizmoC*
AttributeGizmoC::create_new( EffectI* pParent, uint32 ui32Id )
{
	return new AttributeGizmoC( pParent, ui32Id );
}

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

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

void
AttributeGizmoC::copy( EditableI* pEditable )
{
	GizmoI::copy( pEditable );

	AttributeGizmoC*	pGizmo = (AttributeGizmoC*)pEditable;
	m_pParamColor->copy( pGizmo->m_pParamColor );
	m_pParamFile->copy( pGizmo->m_pParamFile );
	m_pParamText->copy( pGizmo->m_pParamText );
}

void
AttributeGizmoC::restore( EditableI* pEditable )
{
	GizmoI::restore( pEditable );

	AttributeGizmoC*	pGizmo = (AttributeGizmoC*)pEditable;
	m_pParamColor = pGizmo->m_pParamColor;
	m_pParamFile = pGizmo->m_pParamFile;
	m_pParamText = pGizmo->m_pParamText;
}


PajaTypes::int32
AttributeGizmoC::get_parameter_count()
{
	return ATTRIBUTE_PARAM_COUNT;
}

ParamI*
AttributeGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case ATTRIBUTE_PARAM_TEXT:
		return m_pParamText; break;
	case ATTRIBUTE_PARAM_COLOR:
		return m_pParamColor; break;
	case ATTRIBUTE_PARAM_FILE:
		return m_pParamFile; break;
	}
	return 0;
}


const char*
AttributeGizmoC::get_text( int32 i32Time )
{
	return m_pParamText->get_val( i32Time );
}

MOFOImportC*
AttributeGizmoC::get_file( int32 i32Time, DeviceContextC* pContext, TimeContextC* pTimeContext  )
{
	FileHandleC*	pHandle = 0;
	int32			i32FileTime = 0;
	m_pParamFile->get_file( i32Time, pHandle, i32FileTime );
	if( pHandle ) {
		MOFOImportC*	pImp = (MOFOImportC*)pHandle->get_importable();
		pImp->eval_state( i32FileTime );
		return pImp;
	}
	return 0;
}

ColorC
AttributeGizmoC::get_color( int32 i32Time )
{
	ColorC	rVal;
	m_pParamColor->get_val( i32Time, rVal );
	return rVal;
}



enum AttributeGizmoChunksE {
	CHUNK_ATTRIBUTEGIZMO_BASE				= 0x1000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_TEXT			= 0x2000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_COLOR		= 0x3000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_FILE			= 0x4000,
};

const uint32	ATTRIBUTEGIZMO_VERSION = 1;



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

	// GizmoI stuff
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_BASE, ATTRIBUTEGIZMO_VERSION );
		ui32Error = GizmoI::save( pSave );
	pSave->end_chunk();

	// text
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_TEXT, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamText->save( pSave );
	pSave->end_chunk();

	// color
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_COLOR, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamColor->save( pSave );
	pSave->end_chunk();

	// file
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_FILE, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamFile->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_ATTRIBUTEGIZMO_BASE:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = GizmoI::load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_TEXT:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamText->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_COLOR:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamColor->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_FILE:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamFile->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;
}


//
// The effect
//


TextEffectC::TextEffectC() :
	m_pTextGizmo( 0 ),
	m_pTextAnimGizmo( 0 )
{
	m_pTransGizmo = TransformGizmoC::create_new( this, 0 );
	m_pAttribGizmo = AttributeGizmoC::create_new( this, 1 );
	m_pAnimGizmo = AnimationGizmoC::create_new( this, 2 );
}

TextEffectC::TextEffectC( EditableI* pOriginal ) :
	EffectI( pOriginal ),
	m_pTextGizmo( 0 ),
	m_pTextAnimGizmo( 0 ),
	m_pTransGizmo( 0 ),
	m_pAttribGizmo( 0 ),
	m_pAnimGizmo( 0 )
{
	// empty
}

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

	m_pTransGizmo->release();
	m_pAttribGizmo->release();
	m_pAnimGizmo->release();
}

TextEffectC*
TextEffectC::create_new()
{
	return new TextEffectC;
}

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

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

void
TextEffectC::copy( EditableI* pEditable )
{
	EffectI::copy( pEditable );

	TextEffectC*	pEffect = (TextEffectC*)pEditable;
	m_pTransGizmo->copy( pEffect->m_pTransGizmo );
	m_pAttribGizmo->copy( pEffect->m_pAttribGizmo );
	m_pAnimGizmo->copy( pEffect->m_pAnimGizmo );
}

void
TextEffectC::restore( EditableI* pEditable )
{
	EffectI::restore( pEditable );

	TextEffectC*	pEffect = (TextEffectC*)pEditable;
	m_pTransGizmo = pEffect->m_pTransGizmo;
	m_pAttribGizmo = pEffect->m_pAttribGizmo;
	m_pAnimGizmo = pEffect->m_pAnimGizmo;
}

const char*
TextEffectC::get_class_name()
{
	return "Text Effects";
}

int32
TextEffectC::get_gizmo_count()
{
	return 3;
}

GizmoI*
TextEffectC::get_gizmo( PajaTypes::int32 i32Index )
{
	if( i32Index == 0 )
		return m_pTransGizmo;
	else if( i32Index == 1 )
		return m_pAttribGizmo;
	else if( i32Index == 2 )
		return m_pAnimGizmo;
	return 0;
}

ClassIdC
TextEffectC::get_class_id()
{
	return CLASS_TEXT_EFFECT;
}

void
TextEffectC::set_default_file( int32 i32Time, FileHandleC* pHandle )
{
	ParamFileC*	pParam = (ParamFileC*)m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_FILE );

	UndoC*	pOldUndo;
	if( get_undo() ) {
		pOldUndo = pParam->begin_editing( get_undo() );
	}

	pParam->set_file( i32Time, pHandle );

	if( get_undo() ) {
		pParam->end_editing( pOldUndo );
	}
}

ParamI*
TextEffectC::get_default_param( PajaTypes::int32 i32Param )
{
	if( i32Param == DEFAULT_PARAM_POSITION )
		return m_pTransGizmo->get_parameter( TRANSFORM_PARAM_POS );
	else if( i32Param == DEFAULT_PARAM_SCALE )
		return m_pTransGizmo->get_parameter( TRANSFORM_PARAM_SCALE );
	else if( i32Param == DEFAULT_PARAM_PIVOT )
		return m_pTransGizmo->get_parameter( TRANSFORM_PARAM_PIVOT );
	else if( i32Param == DEFAULT_PARAM_ROTATION )
		return m_pTransGizmo->get_parameter( TRANSFORM_PARAM_ROT );
	return 0;
}

BBox2C
TextEffectC::get_bbox()
{
	return m_rBBox;
}

void
TextEffectC::initialize( uint32 ui32Reason, PajaSystem::DemoInterfaceC* pInterface )
{
	EffectI::initialize( ui32Reason, pInterface );

	if( ui32Reason == INIT_INITIAL_UPDATE ) {
		if( m_pTextGizmo ) {

			// we have the old gizmo loaded.
			// convert it to the new structure and delete it
			// the parameters belongs to the TextGizmo so we must change the parents too!

			ParamI*	pParam;

			// copy transform
			pParam = m_pTransGizmo->get_parameter( TRANSFORM_PARAM_POS );
			pParam->copy( m_pTextGizmo->get_parameter( TEXT_PARAM_POS ) );
			pParam->set_parent( m_pTransGizmo );

			pParam = m_pTransGizmo->get_parameter( TRANSFORM_PARAM_PIVOT );
			pParam->copy( m_pTextGizmo->get_parameter( TEXT_PARAM_PIVOT ) );
			pParam->set_parent( m_pTransGizmo );

			pParam = m_pTransGizmo->get_parameter( TRANSFORM_PARAM_ROT );
			pParam->copy( m_pTextGizmo->get_parameter( TEXT_PARAM_ROT ) );
			pParam->set_parent( m_pTransGizmo );

			pParam = m_pTransGizmo->get_parameter( TRANSFORM_PARAM_SCALE );
			pParam->copy( m_pTextGizmo->get_parameter( TEXT_PARAM_SCALE ) );
			pParam->set_parent( m_pTransGizmo );

			// copy attributes
			pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_COLOR );
			pParam->copy( m_pTextGizmo->get_parameter( TEXT_PARAM_COLOR ) );
			pParam->set_parent( m_pAttribGizmo );

			pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_FILE );
			pParam->copy( m_pTextGizmo->get_parameter( TEXT_PARAM_FILE ) );
			pParam->set_parent( m_pAttribGizmo );

			m_pTextGizmo->release();
			m_pTextGizmo = 0;
		}

		if( m_pTextAnimGizmo ) {

			// we have the old gizmo loaded.
			// convert it to the new structure and delete it
			// the parameters belongs to the testgizmo so we must change the parents too!

			ParamI*	pParam;

			// copy animation
			pParam = m_pAnimGizmo->get_parameter( ANIMATION_PARAM_OFFSET );
			pParam->copy( m_pTextAnimGizmo->get_parameter( TEXTANIM_PARAM_OFFSET ) );
			pParam->set_parent( m_pAnimGizmo );

			pParam = m_pAnimGizmo->get_parameter( ANIMATION_PARAM_SCALE );
			pParam->copy( m_pTextAnimGizmo->get_parameter( TEXTANIM_PARAM_SCALE ) );
			pParam->set_parent( m_pAnimGizmo );

			pParam = m_pAnimGizmo->get_parameter( ANIMATION_PARAM_COLOR );
			pParam->copy( m_pTextAnimGizmo->get_parameter( TEXTANIM_PARAM_COLOR ) );
			pParam->set_parent( m_pAnimGizmo );

			pParam = m_pAnimGizmo->get_parameter( ANIMATION_PARAM_DELAY );
			pParam->copy( m_pTextAnimGizmo->get_parameter( TEXTANIM_PARAM_DELAY ) );
			pParam->set_parent( m_pAnimGizmo );

			// copy attributes
			pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_TEXT );
			pParam->copy( m_pTextAnimGizmo->get_parameter( TEXTANIM_PARAM_TEXT ) );
			pParam->set_parent( m_pAttribGizmo );

			m_pTextAnimGizmo->release();
			m_pTextAnimGizmo = 0;
		}
	}
}

void
TextEffectC::eval_state( PajaTypes::int32 i32Time )
{
	if( !m_pDemoInterface )
		return;

	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();

	Matrix2C	rPosMat, rRotMat, rScaleMat, rPivotMat;

	Vector2C	rScale = m_pTransGizmo->get_scale( i32Time );
	Vector2C	rPos = m_pTransGizmo->get_pos( i32Time );
	Vector2C	rPivot = m_pTransGizmo->get_pivot( i32Time );
	float32		f32Rot = m_pTransGizmo->get_rot( i32Time );

	f32Rot = f32Rot / 180.0f * (float32)M_PI;

	rPivotMat.set_trans( rPivot );
	rPosMat.set_trans( rPos );
	rRotMat.set_rot( f32Rot );
	rScaleMat.set_scale( rScale ) ;

	m_rTM = rPivotMat * rRotMat * rScaleMat * rPosMat;


	//
	// calc bounding box
	//
	float32		f32Width = 10;
	float32		f32Height = 10;
	Vector2C	rMin, rMax;
	Vector2C	rVec;

	m_i32Time = i32Time;

	MOFOImportC*	pImp = m_pAttribGizmo->get_file( i32Time, pContext, pTimeContext );
	if( pImp ) {

		// calc the length of the text
		const char*	szText = m_pAttribGizmo->get_text( m_i32Time );

		int32	i32Width = 0;

		uint32	ui32TextLength = strlen( szText );

		if( szText )
			for( uint32 i = 0; i < ui32TextLength; i++ ) {
				i32Width += pImp->get_glyph_advance( szText[i] );
			}

		m_f32Width = (float32)i32Width * 0.5f;
		m_f32Height = (float32)pImp->get_font_height() * 0.5f;


		// calc actual bbox of transformed characters
		float32	f32CurX = -m_f32Width;

		int32	i32Phase = 0;
		int32	i32Delay = m_pAnimGizmo->get_delay( m_i32Time );

		Vector2C	rVert[4];
		bool		bFirst = true;

		if( m_rVertCache.size() < ui32TextLength * 4 )
			m_rVertCache.resize( ui32TextLength * 4 );

		for( uint32 i = 0; i < ui32TextLength; i++ ) {

			Vector2C	rOffset = m_pAnimGizmo->get_offset( m_i32Time + i32Phase );
			Vector2C	rScale = m_pAnimGizmo->get_scale( m_i32Time + i32Phase );
			ColorC		rColor = m_pAnimGizmo->get_color( m_i32Time + i32Phase );

			float32		f32GlyphWidth = (float32)pImp->get_glyph_width( szText[i] );
			float32		f32HalfGlyphWidth = f32GlyphWidth * 0.5f;

			Vector2C	rCenter( f32CurX + f32HalfGlyphWidth, 0 );

			// left bottom
			rVert[0][0] = rCenter[0] + (-f32HalfGlyphWidth * rScale[0]) + rOffset[0];
			rVert[0][1] = rCenter[1] + (-m_f32Height * rScale[1]) + rOffset[1];

			// right bottom
			rVert[1][0] = rCenter[0] + (f32HalfGlyphWidth * rScale[0]) + rOffset[0];
			rVert[1][1] = rCenter[1] + (-m_f32Height * rScale[1]) + rOffset[1];

			// right top
			rVert[2][0] = rCenter[0] + (f32HalfGlyphWidth * rScale[0]) + rOffset[0];
			rVert[2][1] = rCenter[1] + (m_f32Height * rScale[1]) + rOffset[1];

			// left top
			rVert[3][0] = rCenter[0] + (-f32HalfGlyphWidth * rScale[0]) + rOffset[0];
			rVert[3][1] = rCenter[1] + (m_f32Height * rScale[1]) + rOffset[1];

			// transform
			for( uint32 j = 0; j < 4; j++ ) {

				Vector2C	rVec = rVert[j] * m_rTM;

				if( bFirst ) {
					rMin = rMax = rVec;
					bFirst = false;
				}
				else {
					if( rVec[0] < rMin[0] ) rMin[0] = rVec[0];
					if( rVec[1] < rMin[1] ) rMin[1] = rVec[1];
					if( rVec[0] > rMax[0] ) rMax[0] = rVec[0];
					if( rVec[1] > rMax[1] ) rMax[1] = rVec[1];
				}

				// save calcuated vertices to cache
				m_rVertCache[i * 4 + j] = rVec;
			}


			// advance to next glyph
			f32CurX += (float32)pImp->get_glyph_advance( szText[i] );
			i32Phase -= i32Delay;
		}
	
	}

	// set bbox
	m_rBBox[0] = rMin;
	m_rBBox[1] = rMax;

	// get color
	m_rColor = m_pAttribGizmo->get_color( i32Time );


	//
	// draw effect
	//

	OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
	if( !pDevice )
		return;

	OpenGLViewportC*	pViewport = (OpenGLViewportC*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pViewport )
		return;

	if( !m_rVertCache.size() )
		return;

	pViewport->set_ortho( m_rBBox, m_rBBox[0][0], m_rBBox[1][0], m_rBBox[1][1], m_rBBox[0][1] );

	if( !pImp )
		return;

	pImp->bind_texture( pDevice );

	glEnable( GL_TEXTURE_2D );
	glDisable( GL_DEPTH_TEST );
	glEnable( GL_BLEND );

	// normal
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );


	const char*	szText = m_pAttribGizmo->get_text( m_i32Time );


	if( !szText || strlen( szText ) < 1 )
		return;


	glDepthMask( GL_FALSE );


	float32	f32CurX = -m_f32Width;

	int32	i32Phase = 0;
	int32	i32Delay = m_pAnimGizmo->get_delay( m_i32Time );

	Vector2C	rVert[4];

	for( uint32 i = 0; i < strlen( szText ); i++ ) {

		float32	f32U0, f32V0, f32U1, f32V1;
		pImp->get_glyph_coords( (uint32)szText[i], f32U0, f32V0, f32U1, f32V1 );

		ColorC		rColor = m_pAnimGizmo->get_color( m_i32Time + i32Phase );
		glColor4f( m_rColor[0] * rColor[0], m_rColor[1] * rColor[1], m_rColor[2] * rColor[2], m_rColor[3] * rColor[3] );

		uint32	j = i * 4;

		glBegin( GL_QUADS );

		glTexCoord2f( f32U0, f32V1 );
		glVertex2f( m_rVertCache[j][0], m_rVertCache[j][1] );
		
		glTexCoord2f( f32U1, f32V1 );
		glVertex2f( m_rVertCache[j + 1][0], m_rVertCache[j + 1][1] );
		
		glTexCoord2f( f32U1, f32V0 );
		glVertex2f( m_rVertCache[j + 2][0], m_rVertCache[j + 2][1] );

		glTexCoord2f( f32U0, f32V0 );
		glVertex2f( m_rVertCache[j + 3][0], m_rVertCache[j + 3][1] );

		glEnd();

		i32Phase -= i32Delay;
		f32CurX += (float32)pImp->get_glyph_advance( szText[i] );
	}

	glDepthMask( GL_TRUE );
}


bool
TextEffectC::hit_test( const PajaTypes::Vector2C& rPoint )
{
	// point in polygon test
	// from c.g.a FAQ
/*	int		i, j;
	bool	bInside = false;

	for( i = 0, j = 4 - 1; i < 4; j = i++ ) {
		if( ( ((m_rVertices[i][1] <= rPoint[1]) && (rPoint[1] < m_rVertices[j][1])) ||
			((m_rVertices[j][1] <= rPoint[1]) && (rPoint[1] < m_rVertices[i][1])) ) &&
			(rPoint[0] < (m_rVertices[j][0] - m_rVertices[i][0]) * (rPoint[1] - m_rVertices[i][1]) / (m_rVertices[j][1] - m_rVertices[i][1]) + m_rVertices[i][0]) )

			bInside = !bInside;
	}

	return bInside;
*/

	if( m_rBBox.contains( rPoint ) )
		return true;

	return false;
}

/*
void
TextEffectC::do_frame( PajaSystem::DeviceContextC* pContext, PajaSystem::TimeContextC* pTimeContext )
{

	OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
	if( !pDevice )
		return;

	OpenGLViewportC*	pViewport = (OpenGLViewportC*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pViewport )
		return;

	if( !m_rVertCache.size() )
		return;

	pViewport->set_ortho( m_rBBox, m_rBBox[0][0], m_rBBox[1][0], m_rBBox[1][1], m_rBBox[0][1] );

	MOFOImportC*	pImp = m_pAttribGizmo->get_file( m_i32Time, pContext, pTimeContext );

	if( !pImp )
		return;

	pImp->bind_texture( pDevice );

	glEnable( GL_TEXTURE_2D );
	glDisable( GL_DEPTH_TEST );
	glEnable( GL_BLEND );

	// normal
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );


	const char*	szText = m_pAttribGizmo->get_text( m_i32Time );


	if( !szText || strlen( szText ) < 1 )
		return;


	glDepthMask( GL_FALSE );


	float32	f32CurX = -m_f32Width;

	int32	i32Phase = 0;
	int32	i32Delay = m_pAnimGizmo->get_delay( m_i32Time );

	Vector2C	rVert[4];

	for( uint32 i = 0; i < strlen( szText ); i++ ) {

		float32	f32U0, f32V0, f32U1, f32V1;
		pImp->get_glyph_coords( (uint32)szText[i], f32U0, f32V0, f32U1, f32V1 );

		ColorC		rColor = m_pAnimGizmo->get_color( m_i32Time + i32Phase );
		glColor4f( m_rColor[0] * rColor[0], m_rColor[1] * rColor[1], m_rColor[2] * rColor[2], m_rColor[3] * rColor[3] );

		uint32	j = i * 4;

		glBegin( GL_QUADS );

		glTexCoord2f( f32U0, f32V1 );
		glVertex2f( m_rVertCache[j][0], m_rVertCache[j][1] );
		
		glTexCoord2f( f32U1, f32V1 );
		glVertex2f( m_rVertCache[j + 1][0], m_rVertCache[j + 1][1] );
		
		glTexCoord2f( f32U1, f32V0 );
		glVertex2f( m_rVertCache[j + 2][0], m_rVertCache[j + 2][1] );

		glTexCoord2f( f32U0, f32V0 );
		glVertex2f( m_rVertCache[j + 3][0], m_rVertCache[j + 3][1] );

		glEnd();

		i32Phase -= i32Delay;
		f32CurX += (float32)pImp->get_glyph_advance( szText[i] );
	}

	glDepthMask( GL_TRUE );
}
*/

const Matrix2C&
TextEffectC::get_transform_matrix() const
{
	return m_rTM;
}


enum TextEffectChunksE {
	CHUNK_TEXTEFFECT_BASE =				0x1000,
	CHUNK_TEXTEFFECT_GIZMO =			0x2000,	// OBSOLETE
	CHUNK_TEXTEFFECT_TEXTANIMGIZMO =	0x3000,	// OBSOLETE
	CHUNK_TEXTEFFECT_TRANSGIZMO =		0x4000,
	CHUNK_TEXTEFFECT_ATTRIBGIZMO =		0x5000,
	CHUNK_TEXTEFFECT_ANIMGIZMO =		0x6000,
};

const uint32	TEXTEFFECT_VERSION = 1;

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

	// EffectI stuff
	pSave->begin_chunk( CHUNK_TEXTEFFECT_BASE, TEXTEFFECT_VERSION );
		ui32Error = EffectI::save( pSave );
	pSave->end_chunk();

	// transform
	pSave->begin_chunk( CHUNK_TEXTEFFECT_TRANSGIZMO, TEXTEFFECT_VERSION );
		ui32Error = m_pTransGizmo->save( pSave );
	pSave->end_chunk();

	// attributes
	pSave->begin_chunk( CHUNK_TEXTEFFECT_ATTRIBGIZMO, TEXTEFFECT_VERSION );
		ui32Error = m_pAttribGizmo->save( pSave );
	pSave->end_chunk();

	// animation
	pSave->begin_chunk( CHUNK_TEXTEFFECT_ANIMGIZMO, TEXTEFFECT_VERSION );
		ui32Error = m_pAnimGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_TEXTEFFECT_BASE:
			{
//				OutputDebugString( "      effect base\n" );
				if( pLoad->get_chunk_version() == TEXTEFFECT_VERSION )
					ui32Error = EffectI::load( pLoad );
//				OutputDebugString( "      /effect base\n" );
			}
			break;

		case CHUNK_TEXTEFFECT_GIZMO:
			{
				// for backward compatibility
				if( pLoad->get_chunk_version() == TEXTEFFECT_VERSION ) {
					m_pTextGizmo = TextGizmoC::create_new( this, 0 );
					ui32Error = m_pTextGizmo->load( pLoad );
				}
			}
			break;

		case CHUNK_TEXTEFFECT_TEXTANIMGIZMO:
			{
				// for backward compatibility
				if( pLoad->get_chunk_version() == TEXTEFFECT_VERSION ) {
					m_pTextAnimGizmo = TextAnimGizmoC::create_new( this, 0 );
					ui32Error = m_pTextAnimGizmo->load( pLoad );
				}
			}
			break;

		case CHUNK_TEXTEFFECT_TRANSGIZMO:
			{
				if( pLoad->get_chunk_version() == TEXTEFFECT_VERSION )
					ui32Error = m_pTransGizmo->load( pLoad );
			}
			break;

		case CHUNK_TEXTEFFECT_ATTRIBGIZMO:
			{
				if( pLoad->get_chunk_version() == TEXTEFFECT_VERSION )
					ui32Error = m_pAttribGizmo->load( pLoad );
			}
			break;

		case CHUNK_TEXTEFFECT_ANIMGIZMO:
			{
				if( pLoad->get_chunk_version() == TEXTEFFECT_VERSION )
					ui32Error = m_pAnimGizmo->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;
}


//
// TARGA import
//


MOFOImportC::MOFOImportC() :
	m_pData( 0 ),
	m_pWidths( 0 ),
	m_pCoords( 0 ),
	m_ui32TextureId( 0 )
{
	// empty
}

MOFOImportC::MOFOImportC( EditableI* pOriginal ) :
	ImportableI( pOriginal ),
	m_pData( 0 ),
	m_pWidths( 0 ),
	m_pCoords( 0 ),
	m_ui32TextureId( 0 )
{
	// empty
}

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

	// delete creted textures
	if( m_ui32TextureId )
		glDeleteTextures( 1, &m_ui32TextureId );

	delete m_pData;
	delete m_pCoords;
	delete m_pWidths;
}

MOFOImportC*
MOFOImportC::create_new()
{
	return new MOFOImportC;
}

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

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

void
MOFOImportC::copy( EditableI* pEditable )
{
	// empty
}

void
MOFOImportC::restore( EditableI* pEditable )
{
	MOFOImportC*	pFile = (MOFOImportC*)pEditable;

	m_ui32TextureId = pFile->m_ui32TextureId;

	m_pData = pFile->m_pData;
	m_pWidths = pFile->m_pWidths;
	m_pCoords = pFile->m_pCoords;
	
	m_sFileName = pFile->m_sFileName;
}


// the interface

// returns the name of the file this importable refers to
const char*
MOFOImportC::get_filename()
{
	return m_sFileName.c_str();
}

void
MOFOImportC::set_filename( const char* szName )
{
	m_sFileName = szName;
}


inline
uint32
lowest_bit_mask( uint32 v )
{
	return (v & -v);
}

static
uint32
ceil_power2( uint32 ui32Num )
{
	uint32	i = lowest_bit_mask( ui32Num );
	while( i < ui32Num )
		i <<= 1;
	return i;
}


// loads the file
bool
MOFOImportC::load_file( const char* szName, DemoInterfaceC* pInterface )
{
	// Store interface pointer.
	m_pDemoInterface = pInterface;

	FILE*		fp;

	TRACE( "joo\n" );

	if( (fp = fopen( szName, "rb" )) == 0 ) {
		TRACE( "MOFO: Cannot open file." );
		return false;
	}

	m_sFileName = szName;

	uint8	ui8Header[4];
	uint8	ui8Tmp;
	uint16	ui16Tmp;

	// Header
	fread( ui8Header, sizeof( ui8Header ), 1, fp );
	if( ui8Header[0] != 'm' || ui8Header[1] != 'o' || ui8Header[2] != 'f' || ui8Header[3] != 'o' ) {
		TRACE( "header not match\n" );
		return false;
	}

	// First char
	fread( &ui8Tmp, sizeof( ui8Tmp ), 1, fp );
	m_ui32FirstGlyph = ui8Tmp;

//	TRACE( "first: %d\n", m_ui32FirstGlyph );

	// Last char
	fread( &ui8Tmp, sizeof( ui8Tmp ), 1, fp );
	m_ui32LastGlyph = ui8Tmp;

//	TRACE( "last: %d\n", m_ui32LastGlyph );

	m_ui32GlyphCount = m_ui32LastGlyph - m_ui32FirstGlyph;
	m_pWidths = new uint16[m_ui32GlyphCount];
	m_pCoords = new uint16[m_ui32GlyphCount * 4];

	// Font height
	fread( &ui16Tmp, sizeof( ui16Tmp ), 1, fp );
	m_ui32FontHeight = ui16Tmp;

//	TRACE( "height: %d\n", m_ui32FontHeight );

	// Texture size
	fread( &ui16Tmp, sizeof( ui16Tmp ), 1, fp );
	m_ui32TexWidth = ui16Tmp;
	fread( &ui16Tmp, sizeof( ui16Tmp ), 1, fp );
	m_ui32TexHeight = ui16Tmp;

//	TRACE( "size: %d x %d\n", m_ui32TexWidth, m_ui32TexHeight );

	// Widths
	fread( m_pWidths, m_ui32GlyphCount * sizeof( uint16 ), 1, fp );

//	for( i = 0; i < m_ui32GlyphCount; i++ )
//		TRACE( "%3d: %3d\n", i, m_pWidths[i] );
//	TRACE( "max: %d\n", m_ui32GlyphCount );

	// Tex coords
	fread( m_pCoords, m_ui32GlyphCount * 4 * sizeof( uint16 ), 1, fp );

//	for( i = 0; i < m_ui32GlyphCount; i++ )
//		TRACE( "%3d: %3d %3d %3d %3d\n", i, m_pCoords[i * 4 + 0], m_pCoords[i * 4 + 1], m_pCoords[i * 4 + 2], m_pCoords[i * 4 + 3] );

//	TRACE( "max: %d\n", m_ui32GlyphCount );

	uint32	ui32DataSize = m_ui32TexWidth * ceil_power2( m_ui32TexHeight );
	delete m_pData;
	m_pData = new uint8[ui32DataSize];
	if( !m_pData ) {
		fclose( fp );
		return false;
	}

	memset( m_pData, 0, ui32DataSize );
	fread( m_pData, m_ui32TexWidth * m_ui32TexHeight, 1, fp );

	fclose( fp );

	m_ui32TexHeight = ceil_power2( m_ui32TexHeight );

//	TRACE( "tex height: %d\n", m_ui32TexHeight );

	return true;
}


void
MOFOImportC::initialize( uint32 ui32Reason, PajaSystem::DemoInterfaceC* pInterface )
{
	ImportableI::initialize( ui32Reason, pInterface );

	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();

	if( ui32Reason == INIT_DEVICE_CHANGED ) {

		OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
		if( !pDevice )
			return;

		if( pDevice->get_state() == DEVICE_STATE_SHUTTINGDOWN ) {
			// Delete textures
			for( uint32 i = 0; i < 4; i++ ) {
				if( m_ui32TextureId ) {
					glDeleteTextures( 1, &m_ui32TextureId );
					m_ui32TextureId = 0;
				}
			}
		}

	}
}


ClassIdC
MOFOImportC::get_class_id()
{
	return CLASS_MOFO_IMPORT;
}

SuperClassIdC
MOFOImportC::get_super_class_id()
{
	return SUPERCLASS_IMPORT;
}

const char*
MOFOImportC::get_class_name()
{
	return "Moppi Font";
}


// interface for this class

int32
MOFOImportC::get_width()
{
	return m_ui32TexWidth;
}

int32
MOFOImportC::get_height()
{
	return m_ui32TexHeight;
}


uint16
MOFOImportC::get_glyph_width( uint32 ui32Index )
{
	if( ui32Index >= m_ui32FirstGlyph && ui32Index < m_ui32LastGlyph ) {
		uint32	ui32Offset = (ui32Index - m_ui32FirstGlyph) * 4;
		return m_pCoords[ui32Offset + 2] - m_pCoords[ui32Offset + 0];
	}
	return 0;
}

uint16
MOFOImportC::get_glyph_advance( uint32 ui32Index )
{
	if( ui32Index >= m_ui32FirstGlyph && ui32Index < m_ui32LastGlyph )
		return m_pWidths[ui32Index - m_ui32FirstGlyph];
	return m_ui32FontHeight / 2;
}

uint16
MOFOImportC::get_font_height()
{
	return m_ui32FontHeight;
}


void
MOFOImportC::get_glyph_coords( uint32 ui32Index, float32& f32U0, float32& f32V0, float32& f32U1, float32& f32V1 )
{
	if( ui32Index >= m_ui32FirstGlyph && ui32Index < m_ui32LastGlyph ) {
		float32	f32MultX = 1.0f / (float)m_ui32TexWidth;
		float32	f32MultY = 1.0f / (float)m_ui32TexHeight;
		uint32	ui32Offset = (ui32Index - m_ui32FirstGlyph) * 4;
		f32U0 =  (float32)m_pCoords[ui32Offset] * f32MultX;
		f32V0 =  (float32)m_pCoords[ui32Offset + 1] * f32MultY;
		f32U1 =  (float32)m_pCoords[ui32Offset + 2] * f32MultX;
		f32V1 =  (float32)m_pCoords[ui32Offset + 3] * f32MultY;
	}
	else
		f32U0 = f32V0 = f32U1 = f32V1 = 0;
}

void
MOFOImportC::bind_texture( DeviceInterfaceI* pInterface )
{
	if( pInterface->get_class_id() != CLASS_OPENGL_DEVICEDRIVER )
		return;

	if( !m_ui32TextureId ) {

		glGenTextures( 1, &m_ui32TextureId );
		glBindTexture( GL_TEXTURE_2D, m_ui32TextureId );
		glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

		glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA , m_ui32TexWidth, m_ui32TexHeight, 0, GL_ALPHA , GL_UNSIGNED_BYTE, m_pData );
	}
	else
		glBindTexture( GL_TEXTURE_2D, m_ui32TextureId );

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
}

void
MOFOImportC::eval_state( int32 i32Time )
{
	// empty
}

const char*
MOFOImportC::get_info()
{
	static char	szInfo[256];
	_snprintf( szInfo, 255, "Size: %d", m_ui32FontHeight );
	return szInfo;
}

ClassIdC
MOFOImportC::get_default_effect()
{
	return CLASS_TEXT_EFFECT;
}

int32
MOFOImportC::get_duration()
{
	return -1;
}

float32
MOFOImportC::get_start_label()
{
	return 0;
}

float32
MOFOImportC::get_end_label()
{
	return 0;
}


enum MOFOImportChunksE {
	CHUNK_MOFOIMPORT_BASE =	0x1000,
	CHUNK_MOFOIMPORT_DATA =	0x2000,
};

const uint32	MOFOIMPORT_VERSION_1 = 1;
const uint32	MOFOIMPORT_VERSION = 2;


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

	// file base
	pSave->begin_chunk( CHUNK_MOFOIMPORT_BASE, MOFOIMPORT_VERSION );
		sStr = m_sFileName;
		if( sStr.size() > 255 )
			sStr.resize( 255 );
		ui32Error = pSave->write_str( sStr.c_str() );
	pSave->end_chunk();

	// file data
	pSave->begin_chunk( CHUNK_MOFOIMPORT_DATA, MOFOIMPORT_VERSION );
		// First glyph
		ui8Tmp = (uint8)m_ui32FirstGlyph;
		ui32Error = pSave->write( &ui8Tmp, sizeof( ui8Tmp ) );

		// Last glyph
		ui8Tmp = (uint8)m_ui32LastGlyph;
		ui32Error = pSave->write( &ui8Tmp, sizeof( ui8Tmp ) );

		// Font height
		ui32Error = pSave->write( &m_ui32FontHeight, sizeof( m_ui32FontHeight ) );

		// Texture size
		ui32Error = pSave->write( &m_ui32TexWidth, sizeof( m_ui32TexWidth ) );
		ui32Error = pSave->write( &m_ui32TexHeight, sizeof( m_ui32TexHeight ) );

		// Advances
		ui32Error = pSave->write( m_pWidths, m_ui32GlyphCount * sizeof( uint16 ) );

		// Tex coords
		ui32Error = pSave->write( m_pCoords, m_ui32GlyphCount * 4 * sizeof( uint16 ) );

		//
		// compress
		//
		int32	i32DataSize = m_ui32TexWidth * m_ui32TexHeight;
		uint32	ui32CompressedSize;
		uint8*	pCompressed = new uint8[i32DataSize + i32DataSize / 64 + 16 + 3];
		long*	pWorkmem = new long[((LZO1X_1_MEM_COMPRESS) + (sizeof(long) - 1)) / sizeof(long)];

		if( lzo1x_1_compress( m_pData, i32DataSize, pCompressed, &ui32CompressedSize, pWorkmem ) != LZO_E_OK )
			ui32Error = IO_ERROR_WRITE;

		// write compressed data size
		ui32Error = pSave->write( &ui32CompressedSize, sizeof( ui32CompressedSize ) );

		// write compressed data
		ui32Error = pSave->write( pCompressed, ui32CompressedSize );

		delete [] pWorkmem;
		delete [] pCompressed;

	pSave->end_chunk();

	return ui32Error;
}

uint32
MOFOImportC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	char	szStr[256];
	uint8	ui8Tmp;

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_MOFOIMPORT_BASE:
			{
				if( pLoad->get_chunk_version() <= MOFOIMPORT_VERSION ) {
					ui32Error = pLoad->read_str( szStr );
					m_sFileName = szStr;
				}
			}
			break;

		case CHUNK_MOFOIMPORT_DATA:
			{
				if( pLoad->get_chunk_version() == MOFOIMPORT_VERSION_1 ) {

					// delete old data if any
					delete [] m_pData;
					delete [] m_pWidths;
					delete [] m_pCoords;

					// Old format
					m_ui32FirstGlyph = 32;
					m_ui32LastGlyph = 128;
					m_ui32GlyphCount = 96;
					m_ui32TexWidth = 512;
					m_ui32TexHeight = 512;

					m_pWidths = new uint16[m_ui32GlyphCount];
					m_pCoords = new uint16[m_ui32GlyphCount * 4];

					uint16		ui16Size[2];

					ui32Error = pLoad->read( ui16Size, sizeof( ui16Size ) );
					ui32Error = pLoad->read( m_pWidths, m_ui32GlyphCount * sizeof( uint16 ) );
					ui32Error = pLoad->read( m_pCoords, m_ui32GlyphCount * 4 * sizeof( uint16 ) );

					m_ui32FontHeight = ui16Size[1];


					//
					// decompress
					//

					uint32	ui32DataSize = m_ui32TexWidth * m_ui32TexHeight;
					uint32	ui32DecompressedSize;
					m_pData = new uint8[ui32DataSize];
					uint32	ui32CompressedSize;

					// read compressed data size
					ui32Error = pLoad->read( &ui32CompressedSize, sizeof( ui32CompressedSize ) );

					uint8*	pCompressed = new uint8[ui32CompressedSize];

					// write compressed data
					ui32Error = pLoad->read( pCompressed, ui32CompressedSize );

					if( lzo1x_decompress( pCompressed, ui32CompressedSize, m_pData, &ui32DecompressedSize, NULL ) != LZO_E_OK )
						ui32Error = IO_ERROR_READ;

					if( ui32DecompressedSize != ui32DataSize )
						OutputDebugString( "data size mismatch\n" );

					delete [] pCompressed;
				}
				else if( pLoad->get_chunk_version() == MOFOIMPORT_VERSION ) {
					// delete old data if any
					delete [] m_pData;
					delete [] m_pWidths;
					delete [] m_pCoords;

					// First glyph
					ui32Error = pLoad->read( &ui8Tmp, sizeof( ui8Tmp ) );
					m_ui32FirstGlyph = ui8Tmp;

					// Last glyph
					ui32Error = pLoad->read( &ui8Tmp, sizeof( ui8Tmp ) );
					m_ui32LastGlyph = ui8Tmp;

					m_ui32GlyphCount = m_ui32LastGlyph - m_ui32FirstGlyph;
					m_pWidths = new uint16[m_ui32GlyphCount];
					m_pCoords = new uint16[m_ui32GlyphCount * 4];

					// Font height
					ui32Error = pLoad->read( &m_ui32FontHeight, sizeof( m_ui32FontHeight ) );

					// Texture size
					ui32Error = pLoad->read( &m_ui32TexWidth, sizeof( m_ui32TexWidth ) );
					ui32Error = pLoad->read( &m_ui32TexHeight, sizeof( m_ui32TexHeight ) );

					// Advances
					ui32Error = pLoad->read( m_pWidths, m_ui32GlyphCount * sizeof( uint16 ) );

					// Tex coords
					ui32Error = pLoad->read( m_pCoords, m_ui32GlyphCount * 4 * sizeof( uint16 ) );

					//
					// decompress
					//

					uint32	ui32DataSize = m_ui32TexWidth * m_ui32TexHeight;
					uint32	ui32DecompressedSize;
					m_pData = new uint8[ui32DataSize];
					uint32	ui32CompressedSize;

					// read compressed data size
					ui32Error = pLoad->read( &ui32CompressedSize, sizeof( ui32CompressedSize ) );

					uint8*	pCompressed = new uint8[ui32CompressedSize];

					// write compressed data
					ui32Error = pLoad->read( pCompressed, ui32CompressedSize );

					if( lzo1x_decompress( pCompressed, ui32CompressedSize, m_pData, &ui32DecompressedSize, NULL ) != LZO_E_OK )
						ui32Error = IO_ERROR_READ;

					if( ui32DecompressedSize != ui32DataSize )
						OutputDebugString( "data size mismatch\n" );

					delete [] pCompressed;
				}
			}
			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;
}
