
// 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 "glext.h"
#include <stdio.h>
#include <string>
#include <stdarg.h>
#include "flare3d.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 "OpenGLInterfaceC.h"
#include "..\3dengine\ASELoaderC.h"
#include "..\3dengine\CameraC.h"
#include "..\3dengine\ScenegraphItemI.h"
#include "..\3dengine\MeshRefC.h"

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



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


// class descriptors
FlareDescC		g_rFlareDesc;


//
// 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 1;
}

__declspec( dllexport )
ClassDescC*
get_classdesc( int32 i )
{
	if( i == 0 )
		return &g_rFlareDesc;
	return 0;
}

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

__declspec( dllexport )
char*
get_dll_name()
{
	return "flare3d.dll - Flare renderer for Demopaja (c)2000 memon/moppi productions";
}


//
// transform gizmo
//

TransGizmoC::TransGizmoC()
{
	init();
}

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

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

void
TransGizmoC::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 ), 3, PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f );
}

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

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


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

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

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

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

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

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

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


PajaTypes::int32
TransGizmoC::get_parameter_count()
{
	return TRANS_PARAM_COUNT;
}

ParamI*
TransGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case TRANS_PARAM_POS:
		return m_pParamPos; break;
	case TRANS_PARAM_PIVOT:
		return m_pParamPivot; break;
	case TRANS_PARAM_SCALE:
		return m_pParamScale; break;
	}
	return 0;
}


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

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

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


enum TransGizmoChunksE {
	CHUNK_TRANSGIZMO_BASE				= 0x1000,
	CHUNK_TRANSGIZMO_PARAM_POS			= 0x2000,
	CHUNK_TRANSGIZMO_PARAM_PIVOT		= 0x3000,
	CHUNK_TRANSGIZMO_PARAM_SCALE		= 0x4000,
};

const uint32	TRANSGIZMO_VERSION = 1;



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

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

	// position
	pSave->begin_chunk( CHUNK_TRANSGIZMO_PARAM_POS, TRANSGIZMO_VERSION );
		ui32Error = m_pParamPos->save( pSave );
	pSave->end_chunk();

	// position
	pSave->begin_chunk( CHUNK_TRANSGIZMO_PARAM_PIVOT, TRANSGIZMO_VERSION );
		ui32Error = m_pParamPivot->save( pSave );
	pSave->end_chunk();

	// scale
	pSave->begin_chunk( CHUNK_TRANSGIZMO_PARAM_SCALE, TRANSGIZMO_VERSION );
		ui32Error = m_pParamScale->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

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

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

		case CHUNK_TRANSGIZMO_PARAM_SCALE:
			{
				if( pLoad->get_chunk_version() == TRANSGIZMO_VERSION )
					ui32Error = m_pParamScale->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
//

AttribGizmoC::AttribGizmoC()
{
	init();
}

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

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

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

	m_pParamWidth = ParamIntC::create_new( this, "Width", 160, ATTRIB_PARAM_WIDTH, PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE, 1, 10000 );
	m_pParamHeight = ParamIntC::create_new( this, "Height", 120, ATTRIB_PARAM_HEIGHT, PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE, 1, 10000 );
	m_pParamRenderMode = ParamIntC::create_new( this, "Render mode", 0, ATTRIB_PARAM_RENDERMODE, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 2 );
	m_pParamRenderMode->add_label( 0, "Normal" );
	m_pParamRenderMode->add_label( 1, "Add" );
	m_pParamRenderMode->add_label( 2, "Mult" );
	m_pParamFps = ParamIntC::create_new( this, "FPS", 30, ATTRIB_PARAM_FPS, PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE );
	m_pParamCamera = ParamIntC::create_new( this, "Camera", 0, ATTRIB_PARAM_CAMERA, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	m_pParamTestZ = ParamIntC::create_new( this, "Test Z-Buffer", 1, ATTRIB_PARAM_CLEARZ, PARAM_STYLE_COMBOBOX, PARAM_NOT_ANIMATABLE, 0, 1 );
	m_pParamTestZ->add_label( 0, "Off" );
	m_pParamTestZ->add_label( 1, "On" );
	m_pParamFile = ParamFileC::create_new( this, "Scene", NULL_SUPERCLASS, CLASS_MAS_IMPORT, ATTRIB_PARAM_FILE );
	m_pParamImage = ParamFileC::create_new( this, "Flare image", SUPERCLASS_IMAGE, NULL_CLASSID, ATTRIB_PARAM_IMAGE );
	m_pParamColor = ParamColorC::create_new( this, "Flare color", ColorC( 1, 1, 1, 1 ), ATTRIB_PARAM_COLOR, PARAM_STYLE_COLORPICKER, PARAM_ANIMATABLE );
	m_pParamFlareSize = ParamFloatC::create_new( this, "Flare size", 1, ATTRIB_PARAM_FLARESIZE, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 0.1f );
}

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

	m_pParamWidth->release();
	m_pParamHeight->release();
	m_pParamFile->release();
	m_pParamRenderMode->release();
	m_pParamFps->release();
	m_pParamTestZ->release();
	m_pParamCamera->release();
	m_pParamImage->release();
	m_pParamColor->release();
	m_pParamFlareSize->release();
}


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

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

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

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

	AttribGizmoC*	pGizmo = (AttribGizmoC*)pEditable;
	m_pParamWidth->copy( pGizmo->m_pParamWidth );
	m_pParamHeight->copy( pGizmo->m_pParamHeight );
	m_pParamFile->copy( pGizmo->m_pParamFile );
	m_pParamRenderMode->copy( pGizmo->m_pParamRenderMode );
	m_pParamFps->copy( pGizmo->m_pParamFps );
	m_pParamTestZ->copy( pGizmo->m_pParamTestZ );
	m_pParamCamera->copy( pGizmo->m_pParamCamera );
	m_pParamImage->copy( pGizmo->m_pParamImage );
	m_pParamColor->copy( pGizmo->m_pParamColor );
	m_pParamFlareSize->copy( pGizmo->m_pParamFlareSize );
}

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

	AttribGizmoC*	pGizmo = (AttribGizmoC*)pEditable;
	m_pParamWidth = pGizmo->m_pParamWidth;
	m_pParamHeight = pGizmo->m_pParamHeight;
	m_pParamFile = pGizmo->m_pParamFile;
	m_pParamRenderMode = pGizmo->m_pParamRenderMode;
	m_pParamFps = pGizmo->m_pParamFps;
	m_pParamTestZ = pGizmo->m_pParamTestZ;
	m_pParamCamera = pGizmo->m_pParamCamera;
	m_pParamImage = pGizmo->m_pParamImage;
	m_pParamColor = pGizmo->m_pParamColor;
	m_pParamFlareSize = pGizmo->m_pParamFlareSize;
}


PajaTypes::int32
AttribGizmoC::get_parameter_count()
{
	return ATTRIB_PARAM_COUNT;
}

ParamI*
AttribGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case ATTRIB_PARAM_WIDTH:
		return m_pParamWidth; break;
	case ATTRIB_PARAM_HEIGHT:
		return m_pParamHeight; break;
	case ATTRIB_PARAM_RENDERMODE:
		return m_pParamRenderMode; break;
	case ATTRIB_PARAM_CAMERA:
		return m_pParamCamera; break;
	case ATTRIB_PARAM_FILE:
		return m_pParamFile; break;
	case ATTRIB_PARAM_FPS:
		return m_pParamFps; break;
	case ATTRIB_PARAM_CLEARZ:
		return m_pParamTestZ; break;
	case ATTRIB_PARAM_IMAGE:
		return m_pParamImage; break;
	case ATTRIB_PARAM_COLOR:
		return m_pParamColor; break;
	case ATTRIB_PARAM_FLARESIZE:
		return m_pParamFlareSize; break;
	}
	return 0;
}


void
AttribGizmoC::update_notify( uint32 ui32Id, int32 i32Time )
{
	if( ui32Id == ATTRIB_PARAM_FILE ) {
		// the file has changed
		// update labels
		m_pParamCamera->clear_labels();

		FileHandleC*	pHandle = 0;
		MASImportC*		pImp = 0;

		pHandle = m_pParamFile->get_file();
		if( pHandle )
			pImp = (MASImportC*)pHandle->get_importable();

		if( pImp ) {
			// get cameras to show in camera param
			for( uint32 i = 0; i < pImp->get_camera_count(); i++ ) {
				CameraC*	pCam = pImp->get_camera( i );
				if( !pCam )
					continue;
				m_pParamCamera->add_label( i, pCam->get_name() );
			}
			m_pParamCamera->set_min_max( 0, pImp->get_camera_count() );

			// get time
			int32	i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame;
			pImp->get_time_parameters( i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame );

			m_pParamFps->set_val( 0, i32FPS );

			m_pParamFile->set_time_offset( 0 );
			m_pParamFile->set_time_scale( 1 );
		}
	}
	else if( ui32Id == ATTRIB_PARAM_FPS ) {
		// FPS changed
		FileHandleC*	pHandle = 0;
		MASImportC*		pImp = 0;

		pHandle = m_pParamFile->get_file();
		if( pHandle )
			pImp = (MASImportC*)pHandle->get_importable();

		if( pImp ) {
			// get time
			int32	i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame;
			pImp->get_time_parameters( i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame );

			int32	i32AnimFPS = get_fps( i32Time );

			if( i32AnimFPS < 1 )
				m_pParamFile->set_time_scale( 1 );
			else
				m_pParamFile->set_time_scale( (float32)i32FPS / (float32)i32AnimFPS );
		}
	}

	GizmoI::update_notify( ui32Id, i32Time );
}

float32
AttribGizmoC::get_flare_size( int32 i32Time )
{
	float32	f32Val;
	m_pParamFlareSize->get_val( i32Time, f32Val );
	return f32Val;
}

MASImportC*
AttribGizmoC::get_file()
{
	FileHandleC*	pHandle = 0;
	pHandle = m_pParamFile->get_file();
	if( pHandle )
		return (MASImportC*)pHandle->get_importable();
	return 0;
}

ImportableImageI*
AttribGizmoC::get_image()
{
	FileHandleC*	pHandle = 0;
	pHandle = m_pParamImage->get_file();
	if( pHandle )
		return (ImportableImageI*)pHandle->get_importable();
	return 0;
}

int32
AttribGizmoC::get_render_mode( PajaTypes::int32 i32Time )
{
	int32	i32Val;
	m_pParamRenderMode->get_val( i32Time, i32Val );
	return i32Val;
}

int32
AttribGizmoC::get_width( PajaTypes::int32 i32Time )
{
	int32	i32Val;
	m_pParamWidth->get_val( i32Time, i32Val );
	return i32Val;
}

int32
AttribGizmoC::get_height( PajaTypes::int32 i32Time )
{
	int32	i32Val;
	m_pParamHeight->get_val( i32Time, i32Val );
	return i32Val;
}

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

CameraC*
AttribGizmoC::get_camera( int32 i32Time )
{
	FileHandleC*	pHandle = 0;
	MASImportC*		pImp = 0;

	pHandle = m_pParamFile->get_file();

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

	if( pImp ) {
		int32	i32Val;
		m_pParamCamera->get_val( i32Time, i32Val );
		return pImp->get_camera( i32Val );
	}
	
	return 0;
}

int32
AttribGizmoC::get_fps( int32 i32Time )
{
	int32	i32Val;
	m_pParamFps->get_val( i32Time, i32Val );
	return i32Val;
}

int32
AttribGizmoC::get_test_z( int32 i32Time )
{
	int32	i32Val;
	m_pParamTestZ->get_val( i32Time, i32Val );
	return i32Val;
}


enum AttribGizmoChunksE {
	CHUNK_ATTRIBGIZMO_BASE				= 0x1000,
	CHUNK_ATTRIBGIZMO_PARAM_WIDTH		= 0x2000,
	CHUNK_ATTRIBGIZMO_PARAM_HEIGHT		= 0x3000,
	CHUNK_ATTRIBGIZMO_PARAM_RENDERMODE	= 0x4000,
	CHUNK_ATTRIBGIZMO_PARAM_FPS			= 0x5000,
	CHUNK_ATTRIBGIZMO_PARAM_CLEARZ		= 0x6000,
	CHUNK_ATTRIBGIZMO_PARAM_FILE		= 0x7000,
	CHUNK_ATTRIBGIZMO_PARAM_CAMERA		= 0x8000,
	CHUNK_ATTRIBGIZMO_PARAM_IMAGE		= 0x9000,
	CHUNK_ATTRIBGIZMO_PARAM_COLOR		= 0xA000,
	CHUNK_ATTRIBGIZMO_PARAM_FLARESIZE	= 0xB000,
};

const uint32	ATTRIBGIZMO_VERSION = 1;



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

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

	// width
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_WIDTH, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamWidth->save( pSave );
	pSave->end_chunk();

	// height
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_HEIGHT, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamHeight->save( pSave );
	pSave->end_chunk();

	// render
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_RENDERMODE, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamRenderMode->save( pSave );
	pSave->end_chunk();

	// FPS
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_FPS, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamFps->save( pSave );
	pSave->end_chunk();

	// clearz
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_CLEARZ, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamTestZ->save( pSave );
	pSave->end_chunk();

	// file
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_FILE, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamFile->save( pSave );
	pSave->end_chunk();

	// camera
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_CAMERA, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamCamera->save( pSave );
	pSave->end_chunk();

	// image
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_IMAGE, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamImage->save( pSave );
	pSave->end_chunk();

	// color
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_COLOR, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamColor->save( pSave );
	pSave->end_chunk();

	// flaresize
	pSave->begin_chunk( CHUNK_ATTRIBGIZMO_PARAM_FLARESIZE, ATTRIBGIZMO_VERSION );
		ui32Error = m_pParamFlareSize->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

		case CHUNK_ATTRIBGIZMO_PARAM_WIDTH:
			{
				if( pLoad->get_chunk_version() == ATTRIBGIZMO_VERSION )
					ui32Error = m_pParamWidth->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBGIZMO_PARAM_HEIGHT:
			{
				if( pLoad->get_chunk_version() == ATTRIBGIZMO_VERSION )
					ui32Error = m_pParamHeight->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBGIZMO_PARAM_RENDERMODE:
			{
				if( pLoad->get_chunk_version() == ATTRIBGIZMO_VERSION )
					ui32Error = m_pParamRenderMode->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBGIZMO_PARAM_FPS:
			{
				if( pLoad->get_chunk_version() == ATTRIBGIZMO_VERSION )
					ui32Error = m_pParamFps->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBGIZMO_PARAM_CLEARZ:
			{
				if( pLoad->get_chunk_version() == ATTRIBGIZMO_VERSION )
					ui32Error = m_pParamTestZ->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBGIZMO_PARAM_FILE:
			{
				if( pLoad->get_chunk_version() == ATTRIBGIZMO_VERSION )
					ui32Error = m_pParamFile->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBGIZMO_PARAM_CAMERA:
			{
				if( pLoad->get_chunk_version() == ATTRIBGIZMO_VERSION )
					ui32Error = m_pParamCamera->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBGIZMO_PARAM_IMAGE:
			{
				if( pLoad->get_chunk_version() == ATTRIBGIZMO_VERSION )
					ui32Error = m_pParamImage->load( pLoad );
			}
			break;

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

		case CHUNK_ATTRIBGIZMO_PARAM_FLARESIZE:
			{
				if( pLoad->get_chunk_version() == ATTRIBGIZMO_VERSION )
					ui32Error = m_pParamFlareSize->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
//


FlareEffectC::FlareEffectC() :
	m_i32Frame( 0 ),
	m_i32Time( 0 ),
	m_bHasTexture( false )
{
	m_pTransGizmo = TransGizmoC::create_new( this, 0 );
	m_pAttribGizmo = AttribGizmoC::create_new( this, 1 );
}

FlareEffectC::FlareEffectC( EditableI* pOriginal ) :
	EffectI( pOriginal ),
	m_i32Frame( 0 ),
	m_i32Time( 0 ),
	m_pTransGizmo( 0 ),
	m_pAttribGizmo( 0 ),
	m_bHasTexture( false )
{
	// empty
}

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

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

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

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

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

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

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

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

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

const char*
FlareEffectC::get_class_name()
{
	return "Flare 3D";
}

int32
FlareEffectC::get_gizmo_count()
{
	return 2;
}

GizmoI*
FlareEffectC::get_gizmo( PajaTypes::int32 i32Index )
{
	if( i32Index == 0 )
		return m_pTransGizmo;
	if( i32Index == 1 )
		return m_pAttribGizmo;
	return 0;
}

ClassIdC
FlareEffectC::get_class_id()
{
	return CLASS_FLARE3D_EFFECT;
}

void
FlareEffectC::set_default_file( FileHandleC* pHandle )
{
	ParamFileC*	pParam = (ParamFileC*)m_pAttribGizmo->get_parameter( ATTRIB_PARAM_FILE );

	UndoC*	pOldUndo;
	pOldUndo = pParam->begin_editing( get_undo() );
	pParam->set_file( pHandle );
	pParam->end_editing( pOldUndo );
}

ParamI*
FlareEffectC::get_default_param( PajaTypes::int32 i32Param )
{
	if( i32Param == DEFAULT_PARAM_POSITION )
		return m_pTransGizmo->get_parameter( TRANS_PARAM_POS );
	else if( i32Param == DEFAULT_PARAM_SCALE )
		return m_pTransGizmo->get_parameter( TRANS_PARAM_SCALE );
	else if( i32Param == DEFAULT_PARAM_PIVOT )
		return m_pTransGizmo->get_parameter( TRANS_PARAM_PIVOT );
	return 0;
}

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

void
FlareEffectC::initialize( DeviceContextC* pContext, TimeContextC* pTimeContext )
{
}

void
FlareEffectC::eval_state( int32 i32Time, TimeContextC* pTimeContext )
{

	Matrix2C	rPosMat, rScaleMat, rPivotMat;

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

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

	m_rTM = rPivotMat * rScaleMat * rPosMat;

	//
	// calc bounding box
	//
	float32		f32Width = m_pAttribGizmo->get_width( i32Time ) / 2;
	float32		f32Height = m_pAttribGizmo->get_height( i32Time ) / 2;
	Vector2C	rMin, rMax;
	Vector2C	rVec;

	m_rVertices[0][0] = -f32Width;		// top-left
	m_rVertices[0][1] = -f32Height;

	m_rVertices[1][0] =  f32Width;		// top-right
	m_rVertices[1][1] = -f32Height;

	m_rVertices[2][0] =  f32Width;		// bottom-right
	m_rVertices[2][1] =  f32Height;

	m_rVertices[3][0] = -f32Width;		// bottom-left
	m_rVertices[3][1] =  f32Height;

	for( uint32 i = 0; i < 4; i++ ) {
		rVec = m_rTM * m_rVertices[i];
		m_rVertices[i] = rVec;

		if( !i ) {
			rMin = rMax = rVec;
		}
		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];
		}
	}

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

	// get rendermode
	m_i32RenderMode = m_pAttribGizmo->get_render_mode( i32Time );
	// get flare size
	m_f32FlareSize = m_pAttribGizmo->get_flare_size( i32Time );
	// get render color
	m_rColor = m_pAttribGizmo->get_color( i32Time );

	// calc frame
	MASImportC*	pImp = (MASImportC*)m_pAttribGizmo->get_file();
	if( pImp ) {

		int32	i32FPS;
		int32	i32TicksPerFrame;
		int32	i32FirstFrame;
		int32	i32LastFrame;

		pImp->get_time_parameters( i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame );

		m_i32Frame = (int32)(pTimeContext->convert_time_to_fps( i32Time, m_pAttribGizmo->get_fps( i32Time ) ) * (float32)i32TicksPerFrame);
		m_i32Frame += i32FirstFrame * i32TicksPerFrame;
	}
	else
		m_i32Frame = 0;

	// store time
	m_i32Time = i32Time;
}


bool
FlareEffectC::hit_test( const PajaTypes::Vector2C& rPoint )
{
	if( rPoint[0] >= m_rBBox[0][0] && rPoint[0] <= m_rBBox[1][0] &&
		rPoint[1] >= m_rBBox[0][1] && rPoint[1] <= m_rBBox[1][1] )
		return true;
	return false;
}



void
FlareEffectC::render_normal( OpenGLInterfaceC*	pInterface, GeomListC* pGList )
{

	glDisable( GL_LIGHTING );


	// vertex color
	uint8*	pColors = 0;
	uint16*	pColorIndices = 0;
/*
		if( ui16Flags & MtlLayerC::LAYER_VERTEX_COLOR ) {
			glEnable( GL_COLOR_MATERIAL );
			glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE );
			pColors = pGList->get_color_pointer();
			pColorIndices = pGList->get_color_index_pointer();
		}
*/

		// Blending
//	glBlendFunc( GL_ONE, GL_ONE );


//		float32*	pTexCoords = 0;
//		uint16*		pTexIndices = 0;
		
//	glColor3ub( 8, 8, 8 );


	float32*	pVertices = pGList->get_vertex_pointer();
	float32*	pNormals = pGList->get_normal_pointer();

	uint16*		pIndices = pGList->get_index_pointer();
	uint16*		pNormalIndices = pGList->get_normal_index_pointer();

	assert( pVertices && pNormals && pIndices && pNormalIndices );


	float32	f32Mat[16];
	float32	f32MatTrans[16];
	glGetFloatv( GL_MODELVIEW_MATRIX, f32Mat );

	// transpose matrix (rotation only)
//	float32	f32Tmp;

//	f32Tmp = f32Mat[0, 1]; f32Mat[0, 1] = f32Mat[]
/*
  tmp=(*a)[0][1]; (*a)[0][1]=(*a)[1][0]; (*a)[1][0]=tmp;
  tmp=(*a)[0][2]; (*a)[0][2]=(*a)[2][0]; (*a)[2][0]=tmp;
  tmp=(*a)[1][0]; (*a)[1][0]=(*a)[0][1]; (*a)[0][1]=tmp;
  tmp=(*a)[1][2]; (*a)[1][2]=(*a)[2][1]; (*a)[2][1]=tmp;
  tmp=(*a)[2][0]; (*a)[2][0]=(*a)[0][2]; (*a)[0][2]=tmp;
  tmp=(*a)[2][1]; (*a)[2][1]=(*a)[1][2]; (*a)[1][2]=tmp;
*/

/*
  for (i = 0; i < 3; i++)
    for (j = 0; j < 3; j++)
      temp[i][j] = a[j][i];
*/

	for( uint32 ui32Row = 0; ui32Row < 3; ui32Row++ )
		for( uint32 ui32Col = 0; ui32Col < 3; ui32Col++ )
			f32MatTrans[ui32Row * 4 + ui32Col] = f32Mat[ui32Col * 4 + ui32Row];

	// use transposed axii
	Vector3C	rAxisX( f32MatTrans[0], f32MatTrans[1], f32MatTrans[2] );
	Vector3C	rAxisY( f32MatTrans[4], f32MatTrans[5], f32MatTrans[6] );

	rAxisX *= m_f32FlareSize;
	rAxisY *= m_f32FlareSize;


	glBegin( GL_QUADS );

	Vector3C	rVec, rV[4], rOffset[4];

	rOffset[0] = -rAxisX - rAxisY;
	rOffset[1] = rAxisX - rAxisY;
	rOffset[2] = rAxisX + rAxisY;
	rOffset[3] = -rAxisX + rAxisY;

	if( m_bHasTexture ) {
		for( uint32 i = 0; i < pGList->get_vertex_count(); i++ ) {

			rVec[0] = pVertices[0];
			rVec[1] = pVertices[1];
			rVec[2] = pVertices[2];

			rV[0] = rVec + rOffset[0];
			rV[1] = rVec + rOffset[1];
			rV[2] = rVec + rOffset[2];
			rV[3] = rVec + rOffset[3];

			glTexCoord2f( 0, 1 );
			glVertex3f( rV[0][0], rV[0][1], rV[0][2] );

			glTexCoord2f( 1, 1 );
			glVertex3f( rV[1][0], rV[1][1], rV[1][2] );

			glTexCoord2f( 1, 0 );
			glVertex3f( rV[2][0], rV[2][1], rV[2][2] );

			glTexCoord2f( 0, 0 );
			glVertex3f( rV[3][0], rV[3][1], rV[3][2] );

			pVertices += 3;
		}
	}
	else {
		for( uint32 i = 0; i < pGList->get_vertex_count(); i++ ) {

			rVec[0] = pVertices[0];
			rVec[1] = pVertices[1];
			rVec[2] = pVertices[2];

			rV[0] = rVec + rOffset[0];
			rV[1] = rVec + rOffset[1];
			rV[2] = rVec + rOffset[2];
			rV[3] = rVec + rOffset[3];

			glVertex3f( rV[0][0], rV[0][1], rV[0][2] );
			glVertex3f( rV[1][0], rV[1][1], rV[1][2] );
			glVertex3f( rV[2][0], rV[2][1], rV[2][2] );
			glVertex3f( rV[3][0], rV[3][1], rV[3][2] );

			pVertices += 3;
		}
	}

	glEnd();

}


void
FlareEffectC::render_item( OpenGLInterfaceC* pInterface, ScenegraphItemI* pItem, int32 i32Frame )
{
	if( !pItem )
		return;

//	if( !pMesh )
//		return;

	pItem->eval_state( i32Frame );

	if( !pItem->get_visibility() )
		return;

	glPushMatrix();

	float32		f32X, f32Y, f32Z, f32A;
	Vector3C	rPos = pItem->get_position();
	QuatC		rRot = pItem->get_rotation();
	Vector3C	rScale = pItem->get_scale();
	QuatC		rScaleRot = pItem->get_scale_rotation();
	QuatC		rInvScaleRot = rScaleRot.unit_inverse();


//	char	szMsg[256];
//	_snprintf( szMsg, 255, "rot: %f %f %f %f\n", rRot[0], rRot[1], rRot[2], rRot[3] );
//	OutputDebugString( szMsg );

//	if( pItem->get_scale_controller() )
//		OutputDebugString( "scale anim\n" );

	glTranslatef( rPos[0], rPos[1], rPos[2] );

	rRot.to_axis_angle( f32X, f32Y, f32Z, f32A );
	glRotatef( -f32A / (float32)M_PI * 180.0f, f32X, f32Y, f32Z );

	rInvScaleRot.to_axis_angle( f32X, f32Y, f32Z, f32A );
	glRotatef( -f32A / (float32)M_PI * 180.0f, f32X, f32Y, f32Z );

	glScalef( rScale[0], rScale[1], rScale[2] );

	rScaleRot.to_axis_angle( f32X, f32Y, f32Z, f32A );
	glRotatef( -f32A / (float32)M_PI * 180.0f, f32X, f32Y, f32Z );

	Mesh3DC*	pMesh = pItem->get_mesh();
	if( pMesh ) {
		for( uint32 j = 0; j < pMesh->get_geomlist_count(); j++ ) {

			GeomListC*	pGList = pMesh->get_geomlist( j );

			if( !pGList )
				continue;

			render_normal( pInterface, pGList );
		}
	}

	// draw childs
	for( uint32 i = 0; i < pItem->get_child_count(); i++ )
		render_item( pInterface, pItem->get_child( i ), i32Frame );

	glPopMatrix();

}


void
FlareEffectC::do_frame( DeviceContextC* pContext )
{
	uint32	i;
	OpenGLInterfaceC*	pInterface = (OpenGLInterfaceC*)pContext->query_interface( INTERFACE_OPENGL );

	if( !pInterface ) {
		OutputDebugString( "!!! no interface\n" );
		return;
	}


	MASImportC*	pImp = (MASImportC*)m_pAttribGizmo->get_file();

	if( !pImp )
		return;

	CameraC*	pCam = m_pAttribGizmo->get_camera( m_i32Time );

	if( !pCam )
		return;

	Vector3C	rPos, rTgt;
	float		f32Roll, f32FOV;

	pCam->eval_state( m_i32Frame );

	rPos = pCam->get_position();
	rTgt = pCam->get_target_position();
	f32Roll = pCam->get_roll();
	f32FOV = pCam->get_fov();

	// test z-buffer
	if( m_pAttribGizmo->get_test_z( m_i32Time ) )
		glEnable( GL_DEPTH_TEST );
	else
		glDisable( GL_DEPTH_TEST );


	pInterface->set_perspective( m_rBBox, f32FOV / M_PI * 180.0f, 1, pCam->get_near_plane() + 1.0f, pCam->get_far_plane() );


	glMatrixMode( GL_MODELVIEW );
	gluLookAt( rPos[0], rPos[1], rPos[2], rTgt[0], rTgt[1], rTgt[2], 0, 1, 0 );
//	glTranslatef( 0, -50, -200 );
	glRotatef( -f32Roll / M_PI * 180.0f, 0, 0, 1 );


//	glColor3ub( 255, 0, 255 );
//	glBegin( GL_LINES );

//	glVertex3f( rPos[0], rPos[1], rPos[2] );
//	glVertex3f( rTgt[0], rTgt[1], rTgt[2] );

//	gluLookAt( rPos[0], rPos[1], rPos[2], rTgt[0], rTgt[1], rTgt[2], 0, 1, 0 );

//	glEnd();

	GLfloat	f32White[] = { 1, 1, 1, 1 };

	glShadeModel( GL_SMOOTH );

//	glEnable( GL_NORMALIZE  );
//	glEnable( GL_DEPTH_TEST );
	glEnable( GL_BLEND );
//	glEnable( GL_LIGHTING );

	glDepthMask( GL_FALSE );

/*	for( i = 0; i < pImp->get_light_count(); i++ ) {
		LightC*	pLight = pImp->get_light( i );

		pLight->eval_state( m_i32Frame );

		Vector3C	rPos = pLight->get_position();
		ColorC		rColor = pLight->get_color();
		float32		f32Mult = pLight->get_multiplier();
		GLfloat		f32Diffuse[] = { 1, 1, 1, 1 };
		GLfloat		f32Pos[] = { 1, 1, 1, 1 };

		f32Diffuse[0] = rColor[0] * f32Mult;
		f32Diffuse[1] = rColor[1] * f32Mult;
		f32Diffuse[2] = rColor[2] * f32Mult;

		f32Pos[0] = rPos[0];
		f32Pos[1] = rPos[1];
		f32Pos[2] = rPos[2];

		float32	f32Decay = 1.0f / pLight->get_decay_start();
		float32	f32Zero = 0;
		float32	f32One = 1;

		if( pLight->get_do_decay() ) {
			glLightfv( GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, &f32Zero );
			glLightfv( GL_LIGHT0 + i, GL_LINEAR_ATTENUATION , &f32Decay );
			glLightfv( GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION , &f32Zero );
		}
		else {
			glLightfv( GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, &f32One );
			glLightfv( GL_LIGHT0 + i, GL_LINEAR_ATTENUATION , &f32Zero );
			glLightfv( GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION , &f32Zero );
		}

		glEnable( GL_LIGHT0 + i );
		glLightfv( GL_LIGHT0 + i, GL_DIFFUSE, f32Diffuse );
		glLightfv( GL_LIGHT0 + i, GL_SPECULAR, f32White );
		glLightfv( GL_LIGHT0 + i, GL_POSITION , f32Pos );

	}
*/

	glDepthFunc( GL_LEQUAL );

//	glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );


/*	if( m_i32RenderMode == FLARE_RENDER_NORMAL ) {
		glEnable( GL_DEPTH_TEST );
		glEnable( GL_ALPHA_TEST );
		glAlphaFunc( GL_GREATER, 0.1f );
	}
*/
/*	else if( m_i32RenderMode == RENDER_ADDITIVE ) {
		glDisable( GL_DEPTH_TEST );
		glDisable( GL_CULL_FACE );
		glDisable( GL_ALPHA_TEST );
	}
	else if( m_i32RenderMode == RENDER_CARTOON ) {
		glEnable( GL_DEPTH_TEST );
		glEnable( GL_ALPHA_TEST );
		glAlphaFunc( GL_GREATER, 0.1f );
	}
*/

	glMatrixMode( GL_MODELVIEW );


	// texture
	ImportableImageI*	pImpImage = m_pAttribGizmo->get_image();

	if( pImpImage ) {
		pImpImage->bind_texture( pInterface, IMAGE_CLAMP | IMAGE_LINEAR );
		glEnable( GL_TEXTURE_2D );
		m_bHasTexture = true;
	}
	else {
		glDisable( GL_TEXTURE_2D );
		m_bHasTexture = false;
	}



	if( m_i32RenderMode == 0 ) {
		// normal
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	}
	else if( m_i32RenderMode == 1 ) {
		// add
		glBlendFunc( GL_SRC_ALPHA, GL_ONE );
	}
	else {
		// mult
		glBlendFunc( GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA );
	}


	if( m_i32RenderMode == 2 )	// mult
		glColor4f( m_rColor[0] * m_rColor[3], m_rColor[1] * m_rColor[3], m_rColor[2] * m_rColor[3], m_rColor[3] );
	else
		glColor4f( m_rColor[0], m_rColor[1], m_rColor[2], m_rColor[3] );


	for( i = 0; i < pImp->get_scenegraphitem_count(); i++ )
		render_item( pInterface, pImp->get_scenegraphitem( i ), m_i32Frame );

	for( i = 0; i < pImp->get_light_count(); i++ )
		glDisable( GL_LIGHT0 + i );

	glDepthMask( GL_TRUE );

	glDisable( GL_CULL_FACE );
	glDisable( GL_LIGHTING );
	glDisable( GL_NORMALIZE  );
	glDisable( GL_ALPHA_TEST );

}

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


enum FlareEffectChunksE {
	CHUNK_FLAREEFFECT_BASE =		0x1000,
	CHUNK_FLAREEFFECT_TRANSGIZMO =	0x2000,
	CHUNK_FLAREEFFECT_ATTRIBGIZMO =	0x3000,
};

const uint32	FLAREEFFECT_VERSION = 1;

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

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

	// transform
	pSave->begin_chunk( CHUNK_FLAREEFFECT_TRANSGIZMO, FLAREEFFECT_VERSION );
		ui32Error = m_pTransGizmo->save( pSave );
	pSave->end_chunk();

	// attributes
	pSave->begin_chunk( CHUNK_FLAREEFFECT_ATTRIBGIZMO, FLAREEFFECT_VERSION );
		ui32Error = m_pAttribGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_FLAREEFFECT_BASE:
			{
				if( pLoad->get_chunk_version() == FLAREEFFECT_VERSION )
					ui32Error = EffectI::load( pLoad );
			}
			break;

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

		case CHUNK_FLAREEFFECT_ATTRIBGIZMO:
			{
				if( pLoad->get_chunk_version() == FLAREEFFECT_VERSION )
					ui32Error = m_pAttribGizmo->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;
}
