//
// FlashPlugin.cpp
//
// Flash player and importer Plugin
//
// Copyright (c) 2000 memon/moppi productions
//

//#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>

// Demopaja headers
#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ImportableI.h"
#include "OpenGLDeviceC.h"
#include "OpenGLViewportC.h"
#include "FlashPlugin.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "ImportableImageI.h"

#include "FlashScriptC.h"

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

using namespace FlashPlugin;


//////////////////////////////////////////////////////////////////////////
//
//  SWF importer class descriptor.
//

SWFImportDescC::SWFImportDescC()
{
	// empty
}

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

void*
SWFImportDescC::create()
{
	return SWFImportC::create_new();
}

int32
SWFImportDescC::get_classtype() const
{
	return CLASS_TYPE_FILEIMPORT;
}

SuperClassIdC
SWFImportDescC::get_super_class_id() const
{
	return SUPERCLASS_IMPORT;
}

ClassIdC
SWFImportDescC::get_class_id() const
{
	return CLASS_SWF_IMPORT;
}

const char*
SWFImportDescC::get_name() const
{
	return "Flash Animation";
}

const char*
SWFImportDescC::get_desc() const
{
	return "Importer for SWF animations";
}

const char*
SWFImportDescC::get_author_name() const
{
	return "Mikko \"memon\" Mononen";
}

const char*
SWFImportDescC::get_copyright_message() const
{
	return "Copyright (c) 2000 Moppi Productions";
}

const char*
SWFImportDescC::get_url() const
{
	return "http://moppi.inside.org/demopaja/";
}

const char*
SWFImportDescC::get_help_filename() const
{
	return "res://SWFhelp.html";
}

uint32
SWFImportDescC::get_required_device_driver_count() const
{
	return 1;
}

const ClassIdC&
SWFImportDescC::get_required_device_driver( uint32 ui32Idx )
{
	return PajaSystem::CLASS_OPENGL_DEVICEDRIVER;
}

uint32
SWFImportDescC::get_ext_count() const
{
	return 1;
}

const char*
SWFImportDescC::get_ext( uint32 ui32Index ) const
{
	if( ui32Index == 0 )
		return "SWF";
	return 0;
}


//////////////////////////////////////////////////////////////////////////
//
//  AVI player effect class descriptor.
//

SWFPlayerDescC::SWFPlayerDescC()
{
	// empty
}

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

void*
SWFPlayerDescC::create()
{
	return (void*)FlashPlayerEffectC::create_new();
}

int32
SWFPlayerDescC::get_classtype() const
{
	return CLASS_TYPE_EFFECT;
}

SuperClassIdC
SWFPlayerDescC::get_super_class_id() const
{
	return SUPERCLASS_EFFECT;
}

ClassIdC
SWFPlayerDescC::get_class_id() const
{
	return CLASS_SWFPLAYER_EFFECT;
};

const char*
SWFPlayerDescC::get_name() const
{
	return "Flash Player";
}

const char*
SWFPlayerDescC::get_desc() const
{
	return "Flash Player Effect";
}

const char*
SWFPlayerDescC::get_author_name() const
{
	return "Mikko \"memon\" Mononen";
}

const char*
SWFPlayerDescC::get_copyright_message() const
{
	return "Copyright (c) 2000 Moppi Productions";
}

const char*
SWFPlayerDescC::get_url() const
{
	return "http://moppi.inside.org/demopaja/";
}

const char*
SWFPlayerDescC::get_help_filename() const
{
	return "res://FlashplayerHelp.html";
}

uint32
SWFPlayerDescC::get_required_device_driver_count() const
{
	return 1;
}

const ClassIdC&
SWFPlayerDescC::get_required_device_driver( uint32 ui32Idx )
{
	return PajaSystem::CLASS_OPENGL_DEVICEDRIVER;
}


uint32
SWFPlayerDescC::get_ext_count() const
{
	return 0;
}

const char*
SWFPlayerDescC::get_ext( uint32 ui32Index ) const
{
	return 0;
}


//////////////////////////////////////////////////////////////////////////
//
// Global descriptors, which will be returned to the Demopaja.
//

SWFImportDescC	g_rSWFImportDesc;
SWFPlayerDescC	g_rSWFPlayerDesc;


//////////////////////////////////////////////////////////////////////////
//
// The DLL
//

#ifndef PAJAPLAYER

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


//
// Returns number of classes inside this plugin DLL.
//

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


//
// Returns class descriptors of the plugin classes.
//

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


//
// Returns the API version this DLL was made with.
//

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

//
// Returns the DLL name.
//

__declspec( dllexport )
char*
get_dll_name()
{
	return "SWFPlugin.dll - Flash Animation Plugin (c)2000 memon/moppi productions";
}

#endif



//////////////////////////////////////////////////////////////////////////
//
// The effect
//

FlashPlayerEffectC::FlashPlayerEffectC() :
	m_i32RenderMode( 0 )
{
	//
	// Create Transform gizmo.
	//
	m_pTraGizmo = AutoGizmoC::create_new( this, "Transform", ID_GIZMO_TRANS );

	m_pTraGizmo->add_parameter(	ParamVector2C::create_new( m_pTraGizmo, "Position", Vector2C(), ID_TRANSFORM_POS,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_ABS_POSITION, PARAM_ANIMATABLE ) );
	
	m_pTraGizmo->add_parameter(	ParamVector2C::create_new( m_pTraGizmo, "Pivot", Vector2C(), ID_TRANSFORM_PIVOT,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_WORLD_SPACE, PARAM_ANIMATABLE ) );
	
	m_pTraGizmo->add_parameter(	ParamVector2C::create_new( m_pTraGizmo, "Scale", Vector2C( 1, 1 ), ID_TRANSFORM_SCALE,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f ) );


	//
	// Create Attributes gizmo.
	//
	m_pAttGizmo = AutoGizmoC::create_new( this, "Attributes", ID_GIZMO_ATTRIB );

	// Color
	m_pAttGizmo->add_parameter(	ParamColorC::create_new( m_pAttGizmo, "Color XFrom", ColorC( 1, 1, 1, 1 ), ID_ATTRIBUTE_COLOR,
		PARAM_STYLE_COLORPICKER_RGBA, PARAM_ANIMATABLE ) );

	// Render mode
	ParamIntC*	pRenderMode = ParamIntC::create_new( m_pAttGizmo, "Render mode", 0, ID_ATTRIBUTE_RENDERMODE, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 2 );
	pRenderMode->add_label( 0, "Normal" );
	pRenderMode->add_label( 1, "Add" );
	m_pAttGizmo->add_parameter( pRenderMode );

	// Render mode
	ParamIntC*	pClearBG = ParamIntC::create_new( m_pAttGizmo, "Clear BG", 0, ID_ATTRIBUTE_CLEARBG, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	pClearBG->add_label( 0, "On" );
	pClearBG->add_label( 1, "Off" );
	m_pAttGizmo->add_parameter( pClearBG );

	// File
	m_pAttGizmo->add_parameter(	ParamFileC::create_new( m_pAttGizmo, "Movie", NULL_SUPERCLASS, CLASS_SWF_IMPORT, ID_ATTRIBUTE_FILE, PARAM_TYPE_FILE, PARAM_ANIMATABLE ) );


	//
	// Create Time gizmo.
	//
	m_pTimeGizmo = AutoGizmoC::create_new( this, "Timing", ID_GIZMO_TIME );
	
//	m_pTimeGizmo->add_parameter( ParamFloatC::create_new( m_pTimeGizmo, "FPS", 0, ID_TIME_FPS,
//						PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE, 0, 1000.0f, 1.0f ) );

	m_pTimeGizmo->add_parameter( ParamFloatC::create_new( m_pTimeGizmo, "Frame", 0, ID_TIME_FRAME,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 1.0f ) );

}

FlashPlayerEffectC::FlashPlayerEffectC( EditableI* pOriginal ) :
	EffectI( pOriginal ),
	m_pTraGizmo( 0 ),
	m_pAttGizmo( 0 ),
	m_pTimeGizmo( 0 ),
	m_i32RenderMode( 0 )
{
	// Empty. The parameters are not created in the clone constructor.
}

FlashPlayerEffectC::~FlashPlayerEffectC()
{
	// Return if this is a clone.
	if( get_original() )
		return;

	// Release gizmos.
	m_pTraGizmo->release();
	m_pAttGizmo->release();
	m_pTimeGizmo->release();
}

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

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

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

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

	// Make deep copy.
	FlashPlayerEffectC*	pEffect = (FlashPlayerEffectC*)pEditable;
	m_pTraGizmo->copy( pEffect->m_pTraGizmo );
	m_pAttGizmo->copy( pEffect->m_pAttGizmo );
	m_pTimeGizmo->copy( pEffect->m_pTimeGizmo );
}

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

	// Make shallow copy.
	FlashPlayerEffectC*	pEffect = (FlashPlayerEffectC*)pEditable;
	m_pTraGizmo = pEffect->m_pTraGizmo;
	m_pAttGizmo = pEffect->m_pAttGizmo;
	m_pTimeGizmo = pEffect->m_pTimeGizmo;
}

int32
FlashPlayerEffectC::get_gizmo_count()
{
	// Return number of gizmos inside this effect.
	return GIZMO_COUNT;
}

GizmoI*
FlashPlayerEffectC::get_gizmo( PajaTypes::int32 i32Index )
{
	// Returns specified gizmo.
	// Since the ID's are zero based, we can use them as indices.
	switch( i32Index ) {
	case ID_GIZMO_TRANS:
		return m_pTraGizmo;
	case ID_GIZMO_ATTRIB:
		return m_pAttGizmo;
	case ID_GIZMO_TIME:
		return m_pTimeGizmo;
	}

	return 0;
}

ClassIdC
FlashPlayerEffectC::get_class_id()
{
	// Return the class ID. Should be same as in the class descriptor.
	return CLASS_SWFPLAYER_EFFECT;
}

const char*
FlashPlayerEffectC::get_class_name()
{
	// Return the class name. Should be same as in the class descriptor.
	return "Flash Player";
}

void
FlashPlayerEffectC::set_default_file( int32 i32Time, FileHandleC* pHandle )
{
	// Sets the default file.

	// Get the file parameter.
	ParamFileC*	pParam = (ParamFileC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_FILE );

	// Begin Undo block.
	UndoC*	pOldUndo = pParam->begin_editing( get_undo() );
		// Set the file.
		pParam->set_file( i32Time, pHandle );
	// End undo block.
	pParam->end_editing( pOldUndo );
}

ParamI*
FlashPlayerEffectC::get_default_param( int32 i32Param )
{
	// Return specified default parameter.
	if( i32Param == DEFAULT_PARAM_POSITION )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_POS );
	else if( i32Param == DEFAULT_PARAM_SCALE )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_SCALE );
	else if( i32Param == DEFAULT_PARAM_PIVOT )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_PIVOT );
	return 0;
}

void
FlashPlayerEffectC::initialize( uint32 ui32Reason, DemoInterfaceC* pInterface )
{
	EffectI::initialize( ui32Reason, pInterface );
}

void
FlashPlayerEffectC::draw_sprite( FlashScriptC* pScript, SWFDisplayListC* pDispList, Matrix2C rTM, SWFCXFormC rCXForm )
{
	uint32	i, j;

	for( i = 0; i < pDispList->get_object_count(); i++ ) {
		SWFObjectRefC*	pObj = pDispList->get_object( i );
		if( pObj ) {

			SWFCharC*	pChar = pScript->get_dict_char_by_id( pObj->get_char_id() );

			if( pChar ) {

				if( pChar->get_type() == SWF_CHAR_SHAPE ) {
					SWFShapeC*	pShape = (SWFShapeC*)pChar;

					SWFCXFormC	rCXFormShape = pObj->get_cxform();
					rCXFormShape.set_color_mult( rCXFormShape.get_color_mult() * rCXForm.get_color_mult() );
					rCXFormShape.set_color_add( rCXFormShape.get_color_add() * rCXForm.get_color_mult() + rCXForm.get_color_add() );

					draw_shape( pShape->get_shape(), pObj->get_tm() * rTM, rCXFormShape );
				}
				else if( pChar->get_type() == SWF_CHAR_SPRITE ) {
					SWFSpriteC*			pSprite = (SWFSpriteC*)pChar;
					int32				i32Frame = pObj->get_frame() % pSprite->get_displaylist_count();
					SWFDisplayListC*	pList = pSprite->get_displaylist( i32Frame );

					SWFCXFormC	rCXFormShape = pObj->get_cxform();
					rCXFormShape.set_color_mult( rCXFormShape.get_color_mult() * rCXForm.get_color_mult() );
					rCXFormShape.set_color_add( rCXFormShape.get_color_add() * rCXForm.get_color_mult() + rCXForm.get_color_add() );

					if( pList )
						draw_sprite( pScript, pList, pObj->get_tm() * rTM, rCXFormShape );
				}
				else if( pChar->get_type() == SWF_CHAR_TEXT ) {

					SWFTextC*	pText = (SWFTextC*)pChar;
					SWFFontC*	pFont = 0;

					Matrix2C	rTextTM = pText->get_tm() * pObj->get_tm() * rTM;
					BBox2C		rBox = pText->get_bbox();
					Matrix2C	rRecTM;
					Matrix2C	rScaleTM;
					float32		f32Scale;
					ColorC		rColor;

					rRecTM = rTextTM;
					rRecTM[2] = Vector2C( 0, 0 );

					Vector2C	rVert[4];

					rVert[0] = rBox[0];
					rVert[1] = Vector2C( rBox[1][0], rBox[0][1] );
					rVert[2] = rBox[1];
					rVert[3] = Vector2C( rBox[0][0], rBox[1][1] );

					for( j = 0; j < 4; j++ )
						rVert[j] *= rTextTM;

					Vector2C	rOffset( 0, 0 );

					for( j = 0; j < pText->get_text_count(); j++ ) {
						SWFTextRecordC*	pRec = pText->get_text( j );

						if( !pRec )
							continue;

						if( pRec->is_format() ) {

							pFont = (SWFFontC*)pScript->get_dict_char_by_id( pRec->get_font_id() );

							rOffset = pRec->get_offset();

							f32Scale = pRec->get_font_height() / (1024.0f / 20.0f);
							rScaleTM.set_scale( Vector2C( f32Scale, f32Scale ) );

							rRecTM = rScaleTM * rTextTM;

							rColor = pRec->get_color();

							continue;
						}

						if( !pFont )
							continue;

						Vector2C	rTrOffset;
						SWFGlyphC*	pGlyph;

						for( uint32 k = 0; k < pRec->get_glyph_count(); k++ ) {
							rTrOffset = rOffset * rTextTM;

							rRecTM[2] = rTrOffset;

							pGlyph = pFont->get_glyph( pRec->get_glyph_index( k ) );

							
							SWFCXFormC	rCXFormShape = pObj->get_cxform();
							rCXFormShape.set_color_mult( rCXFormShape.get_color_mult() * rCXForm.get_color_mult() );
							rCXFormShape.set_color_add( rCXFormShape.get_color_add() * rCXForm.get_color_mult() + rCXForm.get_color_add() );

							if( pGlyph )
								draw_shape( pGlyph->get_shape(), rRecTM, rCXFormShape, rColor, true );

							rOffset += pRec->get_glyph_advance( k );
						}
					}
				}
				else {
					//TRACE( "unknown char: %d\n", pChar->get_type() );
				}
			}
		}
	}
}

void
FlashPlayerEffectC::draw_shape( SWFShapeRecordC* pShape, Matrix2C rDrawTM, SWFCXFormC rCXForm, ColorC rGlyphColor, bool bIsGlyph )
{
	uint32		i, j;

	SWFRenderShapeC*	pRenderShape = pShape->get_render_shape();

	if( !pRenderShape )
		return;

	// Calc matrix
	Matrix2C	rScaleMat, rTM;
	rScaleMat.set_scale( Vector2C( pShape->get_scale(), pShape->get_scale() ) );
	rScaleMat = rScaleMat.invert();

	rTM = rScaleMat * rDrawTM;

	glPushMatrix();

	float32	f32GlMat[16];

	f32GlMat[0] = rTM[0][0];
	f32GlMat[1] = rTM[0][1];
	f32GlMat[2] = 0;
	f32GlMat[3] = 0;

	f32GlMat[4] = rTM[1][0];
	f32GlMat[5] = rTM[1][1];
	f32GlMat[6] = 0;
	f32GlMat[7] = 0;

	f32GlMat[8] = 0;
	f32GlMat[9] = 0;
	f32GlMat[10] = 1;
	f32GlMat[11] = 0;

	f32GlMat[12] = rTM[2][0];
	f32GlMat[13] = rTM[2][1];
	f32GlMat[14] = 0;
	f32GlMat[15] = 1;

	glMultMatrixf( f32GlMat );

	for( i = 0; i < pRenderShape->get_displist_count(); i++ ) {
		SWFRenderDispListC*	pList = pRenderShape->get_displist( i );

		// Render path
		ColorC	rCol;
		if( bIsGlyph )
			rCol = rGlyphColor;
		else
			rCol = pList->get_color();

		rCol = (rCol * rCXForm.get_color_mult()) + rCXForm.get_color_add();
		glColor4f( rCol[0] * rCol[3], rCol[1] * rCol[3], rCol[2] * rCol[3], rCol[3] );

		if( pList->get_texture() ) {
			glBindTexture( GL_TEXTURE_2D, pList->get_texture() );
			glEnable( GL_TEXTURE_2D );
		}
		else
			glDisable( GL_TEXTURE_2D );

		if( m_i32RenderMode == RENDERMODE_NORMAL ) 
			glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
		else
			glBlendFunc( GL_ONE, GL_ONE );

		glLineWidth( 1 );

		if( pList->get_displist() )
			glCallList( pList->get_displist() );

		if( pList->get_texture() ) {
			glDisable( GL_TEXTURE_2D );
		}

		if( pList->get_edge_count() ) {

			if( m_i32RenderMode == RENDERMODE_NORMAL ) 
				glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			else
				glBlendFunc( GL_SRC_ALPHA, GL_ONE );
			
			// Render outline
			int32	i32LastStyle = 0;
			float32	f32TMScale = __max( rDrawTM[0].length(), rDrawTM[1].length() ) * m_f32ViewportScale;

			for( j = 0; j < pList->get_edge_count(); j++ ) {

				SWFRenderEdgeS*	pRendEdge = pList->get_edge( j );

				if( i32LastStyle != pRendEdge->ui32LineStyle ) {
					i32LastStyle = pRendEdge->ui32LineStyle;
					if( i32LastStyle ) {
						SWFLineStyleC*	pStyle = pShape->get_line_style( i32LastStyle - 1 );
						glLineWidth( pStyle->get_width() * f32TMScale );
						ColorC	rCol = (pStyle->get_color() * rCXForm.get_color_mult()) + rCXForm.get_color_add();
						glColor4f( rCol[0] * rCol[3], rCol[1] * rCol[3], rCol[2] * rCol[3], rCol[3] );
					}
				}

				if( !i32LastStyle )
					continue;

				glBegin( GL_LINES );
				glVertex2f( pRendEdge->rVec[0][0], pRendEdge->rVec[0][1] );
				glVertex2f( pRendEdge->rVec[1][0], pRendEdge->rVec[1][1] );
				glEnd();
			}
		}
	}

	glPopMatrix();
}

void
FlashPlayerEffectC::eval_state( 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;
	Vector2C	rPos;
	Vector2C	rPivot;

	// Get parameters which affect the transformation.
	((ParamVector2C*)m_pTraGizmo->get_parameter( ID_TRANSFORM_POS ))->get_val( i32Time, rPos );
	((ParamVector2C*)m_pTraGizmo->get_parameter( ID_TRANSFORM_PIVOT ))->get_val( i32Time, rPivot );
	((ParamVector2C*)m_pTraGizmo->get_parameter( ID_TRANSFORM_SCALE ))->get_val( i32Time, rScale );

	// Calculate transformation matrix.
	rPivotMat.set_trans( rPivot );
	rPosMat.set_trans( rPos );
	rScaleMat.set_scale( rScale ) ;
	m_rTM = rPivotMat * rScaleMat * rPosMat;

	float32		f32Width = 25;
	float32		f32Height = 25;
	Vector2C	rMin, rMax;
	Vector2C	rVec;

	// Get the size from the fiel or use the defautls if no file.
	SWFImportC*		pImp = 0;
	FileHandleC*	pHandle;
	int32			i32FileTime;
	
	((ParamFileC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_FILE ))->get_file( i32Time, pHandle, i32FileTime );

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

	if( pImp ) {


		// If "Frame" parameter is animated, use it instead of FPS setting.
		ParamFloatC*	pParamFrame = (ParamFloatC*)m_pTimeGizmo->get_parameter_by_id( ID_TIME_FRAME );
		ControllerC*	pCont = pParamFrame->get_controller();
		if( pCont && pCont->get_key_count() ) {
			float32	f32Frame;
			pParamFrame->get_val( i32Time, f32Frame );

			float32	f32StartFrame = pImp->get_start_label();
			float32	f32EndFrame = pImp->get_end_label();

			if( (f32EndFrame - f32StartFrame) >= 0 ) {
				i32FileTime = (int32)(((f32Frame - f32StartFrame) / (f32EndFrame - f32StartFrame)) * pImp->get_duration());
			}
		}

		pImp->eval_state( i32FileTime );

		f32Width = (float32)pImp->get_width() * 0.5f;
		f32Height = (float32)pImp->get_height() * 0.5f;
	}

	// Calcualte vertices of the rectangle.
	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;

	// Calculate bounding box
	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];
		}
	}

	// Store bounding box.
	m_rBBox[0] = rMin;
	m_rBBox[1] = rMax;

	// Get fill color.
	((ParamColorC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_COLOR ))->get_val( i32Time, m_rFillColor );

	// Get rendermode
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_RENDERMODE ))->get_val( i32Time, m_i32RenderMode );

	// Get clear background mode
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_CLEARBG ))->get_val( i32Time, m_i32ClearBG );

	//
	// Draw effect
	//

	// Get the OpenGL device.
	OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
	if( !pDevice )
		return;

	// Get the OpenGL viewport.
	OpenGLViewportC*	pViewport = (OpenGLViewportC*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pViewport )
		return;

	int32	i32Width = 50, i32Height = 50;
	FlashScriptC*	pScript = 0;

	if( pImp ) {
		i32Width = pImp->get_width();
		i32Height = pImp->get_height();
		pScript = pImp->get_script();
	}

	Vector2C	rSize = m_rBBox[1] - m_rBBox[0];
	rSize = pViewport->delta_layout_to_client( rSize );
	m_f32ViewportScale = rSize[0] / (float32)i32Width;

	// Set orthographic projection.
	pViewport->set_ortho( m_rBBox, 0, (float32)i32Width, 0, (float32)i32Height );


	if( !pScript ) {
		glClearColor( 0.6f, 0.6f, 0.6f, 1.0f );
		glClear( GL_COLOR_BUFFER_BIT );
		return;
	}

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();

	ColorC	rBG = pScript->get_bg_color();

	if( m_i32ClearBG == 0 ) {
		glClearColor( rBG[0], rBG[1], rBG[2], rBG[3] );
		glClear( GL_COLOR_BUFFER_BIT );
	}

	glDisable( GL_DEPTH_TEST );
	glDisable( GL_LIGHTING );

	glEnable( GL_LINE_SMOOTH );

	glEnable( GL_BLEND );
	glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );

	SWFDisplayListC*	pDispList = pScript->get_frame( pImp->get_frame() );
	Matrix2C			rTM;
	SWFCXFormC			rCXForm;

	rTM = rTM.set_identity();
	rCXForm.set_color_mult( m_rFillColor );

	if( pDispList )
		draw_sprite( pScript, pDispList, rTM, rCXForm );

	glEnable( GL_DEPTH_TEST );
}

BBox2C
FlashPlayerEffectC::get_bbox()
{
	// Return the bounding box.
	return m_rBBox;
}

const Matrix2C&
FlashPlayerEffectC::get_transform_matrix() const
{
	// Return the trnasformation matrix.
	return m_rTM;
}

bool
FlashPlayerEffectC::hit_test( const 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;
}


enum FlashPlayerEffectChunksE {
	CHUNK_FLASHPLAYER_BASE =		0x1000,
	CHUNK_FLASHPLAYER_TRANSGIZMO =	0x2000,
	CHUNK_FLASHPLAYER_ATTRIBGIZMO =	0x3000,
	CHUNK_FLASHPLAYER_TIMEGIZMO =	0x4000,
};

const uint32	FLASHPLAYER_VERSION = 1;

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

	// EffectI base class
	pSave->begin_chunk( CHUNK_FLASHPLAYER_BASE, FLASHPLAYER_VERSION );
		ui32Error = EffectI::save( pSave );
	pSave->end_chunk();

	// Transform
	pSave->begin_chunk( CHUNK_FLASHPLAYER_TRANSGIZMO, FLASHPLAYER_VERSION );
		ui32Error = m_pTraGizmo->save( pSave );
	pSave->end_chunk();

	// Attribute
	pSave->begin_chunk( CHUNK_FLASHPLAYER_ATTRIBGIZMO, FLASHPLAYER_VERSION );
		ui32Error = m_pAttGizmo->save( pSave );
	pSave->end_chunk();

	// Time
	pSave->begin_chunk( CHUNK_FLASHPLAYER_TIMEGIZMO, FLASHPLAYER_VERSION );
		ui32Error = m_pTimeGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_FLASHPLAYER_BASE:
			// EffectI base class
			if( pLoad->get_chunk_version() == FLASHPLAYER_VERSION )
				ui32Error = EffectI::load( pLoad );
			break;

		case CHUNK_FLASHPLAYER_TRANSGIZMO:
			// Transform
			if( pLoad->get_chunk_version() == FLASHPLAYER_VERSION )
				ui32Error = m_pTraGizmo->load( pLoad );
			break;

		case CHUNK_FLASHPLAYER_ATTRIBGIZMO:
			// Attribute
			if( pLoad->get_chunk_version() == FLASHPLAYER_VERSION )
				ui32Error = m_pAttGizmo->load( pLoad );
			break;

		case CHUNK_FLASHPLAYER_TIMEGIZMO:
			// Attribute
			if( pLoad->get_chunk_version() == FLASHPLAYER_VERSION )
				ui32Error = m_pTimeGizmo->load( pLoad );
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}


//////////////////////////////////////////////////////////////////////////
//
// SWF Importer class implementation.
//

SWFImportC::SWFImportC() :
	m_pRawData( 0 ),
	m_ui32RawSize( 0 ),
	m_pScript( 0 ),
	m_i32CurrentFrame( 0 )
{
	// empty
}

SWFImportC::SWFImportC( EditableI* pOriginal ) :
	ImportableI( pOriginal ),
	m_pRawData( 0 ),
	m_ui32RawSize( 0 ),
	m_pScript( 0 ),
	m_i32CurrentFrame( 0 )
{
	// empty
}

SWFImportC::~SWFImportC()
{
	// Return if this is a clone.
	if( get_original() )
		return;

	delete [] m_pRawData;
	delete m_pScript;
}

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

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

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

void
SWFImportC::copy( EditableI* pEditable )
{
	// Importables which loads the data from a file does
	// not have to implement this method, since they will
	// not be duplicated.
}

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

	m_sFileName = pFile->m_sFileName;

	m_pRawData = pFile->m_pRawData;
	m_ui32RawSize = pFile->m_ui32RawSize;
	m_pScript = pFile->m_pScript;
}

const char*
SWFImportC::get_filename()
{
	return m_sFileName.c_str();
}

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


bool
SWFImportC::load_file( const char* szName, DemoInterfaceC* pInterface )
{
	// Store interface pointer
	m_pDemoInterface = pInterface;

	FILE*	pStream;

	// Open file stream.
	if( (pStream = fopen( pInterface->get_absolute_path( szName ), "rb" )) == 0 ) {
		return false;
	}

	// Find file size
	fseek( pStream, 0, SEEK_END );
	m_ui32RawSize = ftell( pStream );
	fseek( pStream, 0, SEEK_SET );

	// Allocate data for whole file
	m_pRawData = new uint8[m_ui32RawSize];

	// Read the whole file in.
	fread( m_pRawData, 1, m_ui32RawSize, pStream );

	// Close file stream.
	fclose( pStream );

	// Store file name.
	m_sFileName = szName;

	// Parse file.
	delete m_pScript;

	m_pScript = new FlashScriptC;
	if( !m_pScript->parse_file( m_pRawData, m_ui32RawSize ) ) {
		return false;
	}

	// Prepare dictionary
	m_pScript->prepare();

	return true;
}

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

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

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

	if( ui32Reason == INIT_DEVICE_CHANGED ) {

		if( pDevice->get_state() == DEVICE_STATE_SHUTTINGDOWN ) {
			// Delete stuff
			if( m_pScript )
				m_pScript->unprepare();
		}

	}
	else if( ui32Reason == INIT_DEVICE_VALIDATE ) {
		if( m_pScript && !m_pScript->get_prepared() )
			m_pScript->prepare();
	}
	else if( ui32Reason == INIT_INITIAL_UPDATE ) {
		if( m_pScript && !m_pScript->get_prepared() )
			m_pScript->prepare();
	}
}

ClassIdC
SWFImportC::get_class_id()
{
	return CLASS_SWF_IMPORT;
}

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

const char*
SWFImportC::get_class_name()
{
	return "SWF Animation";
}

int32
SWFImportC::get_width()
{
	if( m_pScript )
		return m_pScript->get_width();
	return 0;
}

int32
SWFImportC::get_height()
{
	if( m_pScript )
		return m_pScript->get_height();
	return 0;
}

int32
SWFImportC::get_first_frame()
{
	return 0;
}

int32
SWFImportC::get_last_frame()
{
	if( m_pScript )
		return m_pScript->get_frame_count();
	return 0;
}

float32
SWFImportC::get_fps()
{
	if( m_pScript )
		return (float32)m_pScript->get_frame_rate();
	return 0;
}

FlashScriptC*
SWFImportC::get_script()
{
	return m_pScript;
}

int32
SWFImportC::get_frame() const
{
	return m_i32CurrentFrame;
}


const char*
SWFImportC::get_info()
{
	static char	szInfo[256];
	if( m_pScript ) {
		int32	i32Width = m_pScript->get_width();
		int32	i32Height = m_pScript->get_height();
		float32	f32Fps = (float32)m_pScript->get_frame_rate();
		_snprintf( szInfo, 255, "%d x %d @ %.1f FPS", i32Width, i32Height, f32Fps );
	}
	else {
		_snprintf( szInfo, 255, "No Player" );
	}
	return szInfo;
}

ClassIdC
SWFImportC::get_default_effect()
{
	return CLASS_SWFPLAYER_EFFECT;
}


int32
SWFImportC::get_duration()
{
	if( !m_pDemoInterface )
		return -1;
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();

	if( m_pScript )
		return (int32)pTimeContext->convert_fps_to_time( m_pScript->get_frame_count(), m_pScript->get_frame_rate() );
	return -1;
}

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

float32
SWFImportC::get_end_label()
{
	if( m_pScript )
		return (float32)m_pScript->get_frame_count();
	return 0;
}

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

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

	if( m_pScript && m_pScript->get_frame_count() ) {
		m_i32CurrentFrame = (int32)pTimeContext->convert_time_to_fps( i32Time, m_pScript->get_frame_rate() );
		m_i32CurrentFrame %= m_pScript->get_frame_count();

		int32	i32MovieLen = get_last_frame() - get_first_frame();

		if( i32MovieLen > 0 ) {
			while( m_i32CurrentFrame < get_first_frame() )
				m_i32CurrentFrame += i32MovieLen;
			while( m_i32CurrentFrame >= get_last_frame() )
				m_i32CurrentFrame -= i32MovieLen;
		}
		else {
			if( m_i32CurrentFrame < get_first_frame() )
				m_i32CurrentFrame = get_first_frame();
			if( m_i32CurrentFrame >= get_last_frame() )
				m_i32CurrentFrame = get_last_frame() - 1;
		}

	}
	else
		m_i32CurrentFrame = 0;
}


enum SWFImportChunksE {
	CHUNK_SWFIMPORT_BASE =	0x1000,
	CHUNK_SWFIMPORT_DATA =	0x2000,
};

const uint32	SWFIMPORT_VERSION = 1;


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

	// file base
	pSave->begin_chunk( CHUNK_SWFIMPORT_BASE, SWFIMPORT_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_SWFIMPORT_DATA, SWFIMPORT_VERSION );
		ui32Error = pSave->write( &m_ui32RawSize, sizeof( m_ui32RawSize ) );
		ui32Error = pSave->write( m_pRawData, m_ui32RawSize );
	pSave->end_chunk();


	return ui32Error;
}

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

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

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

		case CHUNK_SWFIMPORT_DATA:
			{
				if( pLoad->get_chunk_version() == SWFIMPORT_VERSION ) {
					// delete old data if any
					delete m_pRawData;
					// load new
					ui32Error = pLoad->read( &m_ui32RawSize, sizeof( m_ui32RawSize ) );
					m_pRawData = new uint8[m_ui32RawSize];
					ui32Error = pLoad->read( m_pRawData, m_ui32RawSize );

					// Parse file.
					delete m_pScript;

					m_pScript = new FlashScriptC;
					m_pScript->parse_file( m_pRawData, m_ui32RawSize );
				}
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}


