//
// SubSceneC.cpp
//
// Sub Scene Implementation
//
// Copyright (c) 2000 memon/moppi productions
//

#ifndef PAJAPLAYER

#include "stdafx.h"
#include "demopaja.h"
#include "demopajaDoc.h"

#include "ScenePropDlg.h"
#include "SceneToImageDlg.h"
#include "SharedBufferDlg.h"
#include "logger/logger.h"

#endif


// Demopaja headers
#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ImportableI.h"
#include "SubSceneC.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "ImportableImageI.h"
#include "SceneC.h"
#include "Composition.h"

#include "GraphicsDeviceI.h"
#include "GraphicsViewportI.h"
#include "GUIDrawInterfaceI.h"


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


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



//////////////////////////////////////////////////////////////////////////
//
//  scene importer class descriptor.
//

SceneImportDescC::SceneImportDescC()
{
	// empty
}

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

void*
SceneImportDescC::create()
{
	return SceneImportC::create_new();
}

int32
SceneImportDescC::get_classtype() const
{
	return CLASS_TYPE_INTERNAL; //CLASS_TYPE_FILEPROCEDURAL;
}

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

ClassIdC
SceneImportDescC::get_class_id() const
{
	return CLASS_SUBSCENE_IMPORTABLE;
}

const char*
SceneImportDescC::get_name() const
{
	return "Scene";
}

const char*
SceneImportDescC::get_desc() const
{
	return "Scene";
}

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

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

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

const char*
SceneImportDescC::get_help_filename() const
{
	return "help://create_scene.html";
}

uint32
SceneImportDescC::get_required_device_driver_count() const
{
	return 0;
}

const ClassIdC&
SceneImportDescC::get_required_device_driver( uint32 ui32Idx )
{
	return NULL_CLASSID;
}

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

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


//////////////////////////////////////////////////////////////////////////
//
//  Shared Buffer importer class descriptor.
//

SharedBufferImportDescC::SharedBufferImportDescC()
{
	// empty
}

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

void*
SharedBufferImportDescC::create()
{
	return SharedBufferImportC::create_new();
}

int32
SharedBufferImportDescC::get_classtype() const
{
	return CLASS_TYPE_FILEPROCEDURAL;
}

SuperClassIdC
SharedBufferImportDescC::get_super_class_id() const
{
	return SUPERCLASS_IMAGE;
}

ClassIdC
SharedBufferImportDescC::get_class_id() const
{
	return CLASS_SHAREDBUFFER_IMPORTABLE;
}

const char*
SharedBufferImportDescC::get_name() const
{
	return "Shared Buffer";
}

const char*
SharedBufferImportDescC::get_desc() const
{
	return "Shared Buffer";
}

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

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

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

const char*
SharedBufferImportDescC::get_help_filename() const
{
	return "help://shared_buffer.html";
}

uint32
SharedBufferImportDescC::get_required_device_driver_count() const
{
	return 0;
}

const ClassIdC&
SharedBufferImportDescC::get_required_device_driver( uint32 ui32Idx )
{
	return NULL_CLASSID;
}

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

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


//////////////////////////////////////////////////////////////////////////
//
//  scene to image importer class descriptor.
//

SceneToImageImportDescC::SceneToImageImportDescC()
{
	// empty
}

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

void*
SceneToImageImportDescC::create()
{
	return SceneToImageImportC::create_new();
}

int32
SceneToImageImportDescC::get_classtype() const
{
	return CLASS_TYPE_FILEPROCEDURAL;
}

SuperClassIdC
SceneToImageImportDescC::get_super_class_id() const
{
	return SUPERCLASS_IMAGE;
}

ClassIdC
SceneToImageImportDescC::get_class_id() const
{
	return CLASS_SCENETOIMAGE_IMPORTABLE;
}

const char*
SceneToImageImportDescC::get_name() const
{
	return "Scene to Image";
}

const char*
SceneToImageImportDescC::get_desc() const
{
	return "Scene to Image";
}

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

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

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

const char*
SceneToImageImportDescC::get_help_filename() const
{
	return "help://scene_to_image.html";
}

uint32
SceneToImageImportDescC::get_required_device_driver_count() const
{
	return 0;
}

const ClassIdC&
SceneToImageImportDescC::get_required_device_driver( uint32 ui32Idx )
{
	return NULL_CLASSID;
}

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

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


//////////////////////////////////////////////////////////////////////////
//
//  scene effect class descriptor.
//

SceneEffectDescC::SceneEffectDescC()
{
	// empty
}

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

void*
SceneEffectDescC::create()
{
	return (void*)SceneEffectC::create_new();
}

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

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

ClassIdC
SceneEffectDescC::get_class_id() const
{
	return CLASS_SUBSCENE;
};

const char*
SceneEffectDescC::get_name() const
{
	return "Scene Effect";
}

const char*
SceneEffectDescC::get_desc() const
{
	return "Scene Effect";
}

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

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

const char*
SceneEffectDescC::get_url() const
{
	return "help://create_scene.html";
}

const char*
SceneEffectDescC::get_help_filename() const
{
	return "int://SceneEffect.html";
}

uint32
SceneEffectDescC::get_required_device_driver_count() const
{
	return 0;
}

const ClassIdC&
SceneEffectDescC::get_required_device_driver( uint32 ui32Idx )
{
	return NULL_CLASSID;
}


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

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


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

SceneToImageImportDescC	g_rSceneToImageImportDesc;
SceneImportDescC		g_rSceneImportDesc;
SharedBufferImportDescC		g_rSharedBufferImportDesc;
SceneEffectDescC		g_rSceneEffectDesc;



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

SceneEffectC::SceneEffectC()
{
	//
	// 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, "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 );

	// File
	m_pAttGizmo->add_parameter(	ParamFileC::create_new( m_pAttGizmo, "Scene", NULL_SUPERCLASS, CLASS_SUBSCENE_IMPORTABLE, ID_ATTRIBUTE_FILE, PARAM_STYLE_FILE, PARAM_ANIMATABLE ) );

	ParamIntC*	pClearLayout = ParamIntC::create_new( m_pAttGizmo, "Clear Layout", 1, ID_ATTRIBUTE_CLEARLAYOUT, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	pClearLayout->add_label( 1, "On" );
	pClearLayout->add_label( 0, "Off" );
	m_pAttGizmo->add_parameter( pClearLayout );
}

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

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

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

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

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

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

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

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

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

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

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

GizmoI*
SceneEffectC::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;
	}

	return 0;
}

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

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

void
SceneEffectC::set_default_file( PajaTypes::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*
SceneEffectC::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 );
	return 0;
}

void
SceneEffectC::initialize( uint32 ui32Reason, DemoInterfaceC* pInterface )
{
	// Call base class implementation.
	EffectI::initialize( ui32Reason, pInterface );
}

void
SceneEffectC::eval_state( int32 i32Time )
{
	if( !m_pDemoInterface )
		return;
	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();

	int32 i;

	Matrix2C	rPosMat, rScaleMat;
	Vector2C	rScale;
	Vector2C	rPos;
	int32		i32ClearLayout = 0;

	// 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_SCALE ))->get_val( i32Time, rScale );
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_CLEARLAYOUT ))->get_val( i32Time, i32ClearLayout );

	// Calculate transformation matrix.
	rPosMat.set_trans( rPos );
	rScaleMat.set_scale( rScale ) ;
	m_rTM = 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.
	SceneImportC*	pImp = 0;
	int32			i32FileTime;
	FileHandleC*	pHandle;
	((ParamFileC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_FILE ))->get_file( i32Time, pHandle, i32FileTime );

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

	if( pImp ) {
		SceneC*	pScene = pImp->get_scene();
		f32Width = (float32)pScene->get_layout_width() * 0.5f;
		f32Height = (float32)pScene->get_layout_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( 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;


	if( !pImp )
		return;

	GraphicsDeviceI*	pGraphDevice = (GraphicsDeviceI*)pContext->query_interface( SUPERCLASS_GRAPHICSDEVICE );
	if( !pGraphDevice )
		return;

	GraphicsViewportI*	pViewport = (GraphicsViewportI*)pGraphDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pViewport )
		return;

	SceneC*	pScene = pImp->get_scene();

	// Save current layout and viewport
	BBox2C	rOldLayout = pViewport->get_layout();
	BBox2C	rOldViewport = pViewport->get_viewport();

	// Set new based on the scene effects bounding box
	BBox2C	rNewLayout( Vector2C( 0, 0 ), Vector2C( (float32)pScene->get_layout_width(), (float32)pScene->get_layout_height() ) );
	BBox2C	rNewViewport;
	BBox2C	rOldClipLayout;

	Vector2C	rOrigo = m_rBBox[0];
	float32		f32ScaleX = (float32)pScene->get_layout_width() / m_rBBox.width();
	float32		f32ScaleY = (float32)pScene->get_layout_height() / m_rBBox.height();


	rNewViewport[0] = rOldViewport[0] - rOrigo;
	rNewViewport[1] = rOldViewport[1] - rOrigo;
	rNewViewport[0][0] *= f32ScaleX;
	rNewViewport[0][1] *= f32ScaleY;
	rNewViewport[1][0] *= f32ScaleX;
	rNewViewport[1][1] *= f32ScaleY;


	// trim the new layout fit inside old layout
	rOldClipLayout[0] = rOldLayout[0] - rOrigo;
	rOldClipLayout[1] = rOldLayout[1] - rOrigo;
	rOldClipLayout[0][0] *= f32ScaleX;
	rOldClipLayout[0][1] *= f32ScaleY;
	rOldClipLayout[1][0] *= f32ScaleX;
	rOldClipLayout[1][1] *= f32ScaleY;

	rNewLayout = rNewLayout.trim( rOldClipLayout );

	// set newly calculated layout viewport
	pViewport->set_layout( rNewLayout );
	pViewport->set_viewport( rNewViewport );
	
	// clear layout if neccessary
	if( i32ClearLayout == 1 ) {
		GUIDrawInterfaceI*	pGUI = (GUIDrawInterfaceI*)pGraphDevice->query_interface( GRAPHICSDEVICE_GUIDRAW_INTERFACE );
		if( pGUI ) {
			pGUI->begin_layout();
			pGUI->draw_layout( pScene->get_layout_color() );
			pGUI->end_layout();
		}
	}


	//
	// use file time to render the effect
	//

	// wrap the time
	while( i32FileTime < 0 )
		i32FileTime += pScene->get_duration();
	while( i32FileTime > pScene->get_duration() )
		i32FileTime -= pScene->get_duration();

	for( i = pScene->get_layer_count() - 1; i >= 0 ; i-- ) {
		LayerC*	pLayer = pScene->get_layer( i );
		if( !pLayer )
			continue;
		if( !pLayer->get_timesegment()->is_visible( i32FileTime ) )
			continue;
		int32	i32TimeOffset;
		i32TimeOffset = pLayer->get_timesegment()->get_segment_start();
		for( int32 j = pLayer->get_effect_count() - 1; j >= 0; j-- ) {
			EffectI*	pEffect = pLayer->get_effect( j );
			if( !pEffect )
				continue;
			if( pEffect->get_timesegment()->is_visible( i32FileTime - i32TimeOffset ) ) {
				pEffect->eval_state( i32FileTime - (i32TimeOffset + pEffect->get_timesegment()->get_segment_start()) );
			}
		}
	}

	// restore old layout and viewport
	pViewport->set_layout( rOldLayout );
	pViewport->set_viewport( rOldViewport );
}

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

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

bool
SceneEffectC::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 SceneEffectChunksE {
	CHUNK_SCENEEFFECT_BASE =		0x1000,
	CHUNK_SCENEEFFECT_TRANSGIZMO =	0x2000,
	CHUNK_SCENEEFFECT_ATTRIBGIZMO =	0x3000,
};

const uint32	SCENEEFFECT_VERSION = 1;

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

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

	// Transform
	pSave->begin_chunk( CHUNK_SCENEEFFECT_TRANSGIZMO, SCENEEFFECT_VERSION );
		ui32Error = m_pTraGizmo->save( pSave );
	pSave->end_chunk();

	// Attribute
	pSave->begin_chunk( CHUNK_SCENEEFFECT_ATTRIBGIZMO, SCENEEFFECT_VERSION );
		ui32Error = m_pAttGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

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

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

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}


//////////////////////////////////////////////////////////////////////////
//
// sub scene class implementation.
//

SceneImportC::SceneImportC() :
	m_pScene( 0 )
{
	m_pScene = SceneC::create_new();
	m_pScene->set_parent( this );
}

SceneImportC::SceneImportC( EditableI* pOriginal ) :
	ImportableI( pOriginal )
{
	SceneImportC*	pOrigImp = (SceneImportC*)pOriginal;
	m_pScene = (SceneC*)pOrigImp->m_pScene->clone();
}

SceneImportC::~SceneImportC()
{
	if( m_pScene )
		m_pScene->release();
}

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

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

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

void
SceneImportC::copy( EditableI* pEditable )
{
	SceneImportC*	pFile = (SceneImportC*)pEditable;
	m_pScene->copy( pFile->m_pScene );
}

void
SceneImportC::restore( EditableI* pEditable )
{
	SceneImportC*	pFile = (SceneImportC*)pEditable;
	m_pScene->restore( pFile->m_pScene );

	if( !get_original() )
	{
		// Notify change
		if( m_pParent )
		{
			m_pParent->update_notify( this );
		}
	}
}

const char*
SceneImportC::get_filename()
{
	return m_pScene->get_name();
}

void
SceneImportC::set_filename( const char* szName )
{
	m_pScene->set_name( szName );
}

bool
SceneImportC::create_file( DemoInterfaceC* pInterface )
{
	m_pScene->set_name( "Scene" );
	m_pDemoInterface = pInterface;

	return true;
}


bool
SceneImportC::prompt_properties()
{

#ifndef PAJAPLAYER

	CScenePropDlg	rDlg;

	rDlg.m_sName = m_pScene->get_name();
	rDlg.m_iWidth = m_pScene->get_layout_width();
	rDlg.m_iHeight = m_pScene->get_layout_height();
	rDlg.m_rBGColor = m_pScene->get_layout_color();

	// Timing
	CString	sDuration;
	int32	i32Hours, i32Mins, i32Secs, i32Duration;
	i32Duration = m_pScene->get_duration();
	int32	i32TimeScale = m_pScene->get_beats_per_min() * m_pScene->get_qnotes_per_beat() * 256;
	i32Secs = (i32Duration * 60 / i32TimeScale) % 60;
	i32Mins = (i32Duration / i32TimeScale) % 60;
	i32Hours = i32Duration / 60 / i32TimeScale;
	sDuration.Format( "%d:%02d:%02d", i32Hours, i32Mins, i32Secs );
	rDlg.m_sDuration = sDuration;


	if( rDlg.DoModal() == IDOK ) {
		m_pScene->set_name( rDlg.m_sName );
		m_pScene->set_layout_width( rDlg.m_iWidth );
		m_pScene->set_layout_height( rDlg.m_iHeight );
		m_pScene->set_layout_color( rDlg.m_rBGColor );

		CDemopajaDoc*	pDoc = GetDoc();

		m_pScene->set_duration( pDoc->GetTimeFromString( rDlg.m_sDuration, TIME_FORMAT_HOUR_MIN_SEC ) );
	}
	else
		return false;

#endif

	return true;
}

bool
SceneImportC::has_properties()
{
	return true;
}

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

	m_pScene->initialize( ui32Reason, pInterface );
}

uint32
SceneImportC::update_notify( EditableI* pCaller )
{
	// Check if the scene was changed.
	if( pCaller->get_base_class_id() == BASECLASS_SCENE )
	{
		if( m_pParent )
			m_pParent->update_notify( this );
	}

	return 0;
}

uint32
SceneImportC::get_reference_file_count()
{
	uint32	ui32RefCount = 0;

	for( uint32 i = 0; i < m_pScene->get_layer_count(); i++ ) {
		LayerC*	pLayer = m_pScene->get_layer( i );
		if( !pLayer )
			continue;
		for( int32 j = 0; j < pLayer->get_effect_count(); j++ ) {
			EffectI*	pEffect = pLayer->get_effect( j );
			if( !pEffect )
				continue;
			for( int32 k = 0; k < pEffect->get_gizmo_count(); k++ ) {
				GizmoI*	pGizmo = pEffect->get_gizmo( k );
				if( !pGizmo )
					continue;
				for( int32 p = 0; p < pGizmo->get_parameter_count(); p++ ) {
					ParamI*	pParam = pGizmo->get_parameter( p );
					if( !pParam )
						continue;
					if( pParam->get_type() == PARAM_TYPE_FILE ) {
						ParamFileC*		pFileParam = (ParamFileC*)pParam;
						if( pFileParam->get_flags() & ITEM_ANIMATED && pFileParam->get_controller() ) {
							ControllerC*	pCont = pFileParam->get_controller();
							for( int32 m = 0; m < pCont->get_key_count(); m++ ) {
								if( ((FileKeyC*)pCont->get_key( m ))->get_file_handle() )
									ui32RefCount++;
							}
						}
						else {
							if( pFileParam->get_file( 0 ) )
								ui32RefCount++;
						}
					}
				}
			}
		}
	}

	return ui32RefCount;
}

FileHandleC*
SceneImportC::get_reference_file( uint32 ui32Index )
{
	uint32	ui32RefCount = 0;

	for( uint32 i = 0; i < m_pScene->get_layer_count(); i++ ) {
		LayerC*	pLayer = m_pScene->get_layer( i );
		if( !pLayer )
			continue;
		for( int32 j = 0; j < pLayer->get_effect_count(); j++ ) {
			EffectI*	pEffect = pLayer->get_effect( j );
			if( !pEffect )
				continue;
			for( int32 k = 0; k < pEffect->get_gizmo_count(); k++ ) {
				GizmoI*	pGizmo = pEffect->get_gizmo( k );
				if( !pGizmo )
					continue;
				for( int32 p = 0; p < pGizmo->get_parameter_count(); p++ ) {
					ParamI*	pParam = pGizmo->get_parameter( p );
					if( !pParam )
						continue;
					if( pParam->get_type() == PARAM_TYPE_FILE ) {
						ParamFileC*	pFileParam = (ParamFileC*)pParam;

						if( pFileParam->get_flags() & ITEM_ANIMATED && pFileParam->get_controller() ) {
							ControllerC*	pCont = pFileParam->get_controller();
							for( int32 m = 0; m < pCont->get_key_count(); m++ ) {
								if( ((FileKeyC*)pCont->get_key( m ))->get_file_handle() ) {
									if( ui32RefCount == ui32Index )
										return ((FileKeyC*)pCont->get_key( m ))->get_file_handle();
									ui32RefCount++;
								}
							}
						}
						else {
							if( pFileParam->get_file( 0 ) ) {
								if( ui32RefCount == ui32Index )
									return pFileParam->get_file( 0 );
								ui32RefCount++;
							}
						}
					}
				}
			}
		}
	}
	return 0;
}


void
SceneImportC::update_references()
{
	ImportableI::update_references();
	m_pScene->update_references();
}

	
ClassIdC
SceneImportC::get_class_id()
{
	return CLASS_SUBSCENE_IMPORTABLE;
}

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

const char*
SceneImportC::get_class_name()
{
	return "Scene";
}


const char*
SceneImportC::get_info()
{
	static char	szInfo[256];
	_snprintf( szInfo, 255, "%d x %d, %d layers", m_pScene->get_layout_width(), m_pScene->get_layout_height(), m_pScene->get_layer_count() );
	return szInfo;
}

ClassIdC
SceneImportC::get_default_effect()
{
	return CLASS_SUBSCENE;
}

bool
SceneImportC::equals( ImportableI* pImp )
{
	return false;
}

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

int32
SceneImportC::get_duration()
{
	return m_pScene->get_duration();
}

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

float32
SceneImportC::get_end_label()
{
	return (float32)(m_pScene->get_duration() * m_pScene->get_edit_accuracy() / 256);
}

SceneC*
SceneImportC::get_scene()
{
	return m_pScene;
}


enum SceneImportChunksE {
	CHUNK_SCENEIMPORT_BASE =	0x1000,
};

const uint32	SCENEIMPORT_VERSION = 1;


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

	// file base
	pSave->begin_chunk( CHUNK_SCENEIMPORT_BASE, SCENEIMPORT_VERSION );
		ui32Error = m_pScene->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_SCENEIMPORT_BASE:
			{
				if( pLoad->get_chunk_version() == SCENEIMPORT_VERSION ) {
					ui32Error = m_pScene->load( pLoad );
				}
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}


//////////////////////////////////////////////////////////////////////////
//
// scene to image class implementation.
//

SceneToImageImportC::SceneToImageImportC() :
	m_pSceneHandle( 0 ),
	m_pBufferHandle( 0 ),
	m_i32LastRenderedFrame( 0x7fffffff ),
	m_pGBuffer( 0 ),
	m_bCheckBuffer( false )
{
	// empty
}

SceneToImageImportC::SceneToImageImportC( EditableI* pOriginal ) :
	ImportableImageI( pOriginal ),
	m_pSceneHandle( 0 ),
	m_pBufferHandle( 0 ),
	m_i32LastRenderedFrame( 0x7fffffff ),
	m_pGBuffer( 0 ),
	m_bCheckBuffer( false )
{
	// empty
}

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

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

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

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

void
SceneToImageImportC::copy( EditableI* pEditable )
{
	SceneToImageImportC*	pFile = (SceneToImageImportC*)pEditable;
	m_pSceneHandle = pFile->m_pSceneHandle;
	m_sName = pFile->m_sName;
	m_pBufferHandle = pFile->m_pBufferHandle;
	m_pGBuffer = 0;
	// Check if the buffer needs to be updated.
	m_bCheckBuffer = true;
}

void
SceneToImageImportC::restore( EditableI* pEditable )
{
	SceneToImageImportC*	pFile = (SceneToImageImportC*)pEditable;
	m_pSceneHandle = pFile->m_pSceneHandle;
	m_sName = pFile->m_sName;
	m_pBufferHandle = pFile->m_pBufferHandle;
	m_pGBuffer = pFile->m_pGBuffer;
	// Check if the buffer needs to be updated.
	if( !get_original() )
	{
		update_gbuffer();
		// Force redraw.
		m_i32LastRenderedFrame = 0x7fffffff;
	}
}


	// overdriven method from datablock
void
SceneToImageImportC::set_alive( bool bState )
{
	DataBlockI::set_alive( bState );
	// Check if the buffer needs to be updated.
	update_gbuffer();
	// Force redraw.
	m_i32LastRenderedFrame = 0x7fffffff;
}

const char*
SceneToImageImportC::get_filename()
{
	return m_sName.c_str();
}

void
SceneToImageImportC::set_filename( const char* szName )
{
	m_sName = szName;
}

bool
SceneToImageImportC::create_file( DemoInterfaceC* pInterface )
{
	m_pDemoInterface = pInterface;
	m_sName = "Scene to Image";
	return true;
}


bool
SceneToImageImportC::prompt_properties()
{

#ifndef PAJAPLAYER

	CSceneToImageDlg	rDlg;

	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	rDlg.m_sName = m_sName.c_str();
	rDlg.m_pHandle = m_pSceneHandle;
	rDlg.m_pBufferHandle = m_pBufferHandle;
	rDlg.m_pFileList = pDoc->GetFileList();

	if( rDlg.DoModal() == IDOK ) {
		m_sName = rDlg.m_sName;
		m_pSceneHandle = rDlg.m_pHandle;
		m_pBufferHandle = rDlg.m_pBufferHandle;

		update_gbuffer();

		// Force redraw.
		m_i32LastRenderedFrame = 0x7fffffff;
	}
	else
		return false;

#endif

	return true;
}

bool
SceneToImageImportC::has_properties()
{
	return true;
}

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

	if( ui32Reason == INIT_INITIAL_UPDATE ) {
		update_gbuffer();

		// Force redraw.
		m_i32LastRenderedFrame = 0x7fffffff;
	}
}

uint32
SceneToImageImportC::update_notify( EditableI* pCaller )
{
	if( pCaller->get_base_class_id() == BASECLASS_FILEHANDLE )
	{
		FileHandleC*	pHandle = (FileHandleC*)pCaller;

		if( pHandle == m_pSceneHandle )
		{
			// Force redraw.
			m_i32LastRenderedFrame = 0x7fffffff;
		}
		else if( pHandle == m_pBufferHandle )
		{
			update_gbuffer();
			// Force redraw.
			m_i32LastRenderedFrame = 0x7fffffff;
		}
	}

	return 0;
}


uint32
SceneToImageImportC::get_reference_file_count()
{
	return 2;
}

FileHandleC*
SceneToImageImportC::get_reference_file( uint32 ui32Index )
{
	if( ui32Index == 0 )
		return m_pSceneHandle;
	else if( ui32Index == 1 )
		return m_pBufferHandle;
	return 0;
}

	
ClassIdC
SceneToImageImportC::get_class_id()
{
	return CLASS_SCENETOIMAGE_IMPORTABLE;
}

SuperClassIdC
SceneToImageImportC::get_super_class_id()
{
	return SUPERCLASS_IMAGE;
}

const char*
SceneToImageImportC::get_class_name()
{
	return "Scene to Image";
}


float32
SceneToImageImportC::get_width()
{
	if( m_pSceneHandle ) {
		SceneImportC*	pImp = (SceneImportC*)m_pSceneHandle->get_importable();
		SceneC*			pScene = pImp->get_scene();
		if( pScene ) {
			return (float32)pScene->get_layout_width();
		}
	}
	return 0;
}

float32
SceneToImageImportC::get_height()
{
	if( m_pSceneHandle ) {
		SceneImportC*	pImp = (SceneImportC*)m_pSceneHandle->get_importable();
		SceneC*			pScene = pImp->get_scene();
		if( pScene ) {
			return (float32)pScene->get_layout_height();
		}
	}
	return 0;
}

int32
SceneToImageImportC::get_data_width()
{
	return 0;
}

int32
SceneToImageImportC::get_data_height()
{
	return 0;
}

BBox2C&
SceneToImageImportC::get_tex_coord_bounds()
{
	return m_rTexBounds;
}

int32
SceneToImageImportC::get_data_pitch()
{
	return 0;
}

int32
SceneToImageImportC::get_data_bpp()
{
	return 0;
}

uint8*
SceneToImageImportC::get_data()
{
	return 0;
}

void
SceneToImageImportC::bind_texture( DeviceInterfaceI* pInterface, PajaTypes::uint32 ui32Stage, uint32 ui32Properties )
{
	GraphicsBufferI*	pGBuffer = 0;

	if( m_pBufferHandle )
	{
		SharedBufferImportC*	pImp = (SharedBufferImportC*)m_pBufferHandle->get_importable();
		if( pImp )
		{
			pGBuffer = pImp->get_gbuffer();
		}
	}
	else if( m_pGBuffer )
	{
		pGBuffer = m_pGBuffer;
	}

	if( pGBuffer )
		pGBuffer->bind_texture( pInterface, ui32Stage, ui32Properties );
}

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

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

	if( m_i32LastRenderedFrame == i32Time )
	{
		return;
	}

//	m_bCheckBuffer = false;

	m_i32LastRenderedFrame = i32Time;

	SceneImportC*	pImp = 0;
	if( m_pSceneHandle )
		pImp = (SceneImportC*)m_pSceneHandle->get_importable();

	if( pImp ) {
		// use render target instead
		GraphicsBufferI*	pOldGBuffer = 0;

		GraphicsBufferI*	pGBuffer = 0;

		if( m_pBufferHandle )
		{
			SharedBufferImportC*	pImp = (SharedBufferImportC*)m_pBufferHandle->get_importable();
			if( pImp )
			{
				pImp->eval_state( i32Time );
				pGBuffer = pImp->get_gbuffer();
			}
		}
		else if( m_pGBuffer )
		{
			pGBuffer = m_pGBuffer;
		}

		if( !pGBuffer )
			return;


		GraphicsDeviceI*	pGraphDevice = (GraphicsDeviceI*)pContext->query_interface( SUPERCLASS_GRAPHICSDEVICE );
		if( !pGraphDevice ) {
			return;
		}

		pOldGBuffer = pGraphDevice->set_render_target( pGBuffer );

		if( pGBuffer->get_state() == DEVICE_STATE_LOST ) {
			pGraphDevice->set_render_target( pOldGBuffer );
			return;
		}

		GraphicsViewportI*	pViewport = (GraphicsViewportI*)pGraphDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
		if( !pViewport ) {
			return;
		}

		SceneC*	pScene = pImp->get_scene();

		// Save current layout and viewport
		BBox2C	rOldLayout = pViewport->get_layout();
		BBox2C	rOldViewport = pViewport->get_viewport();
		float32	f32OldAspect = pViewport->get_pixel_aspect_ratio();

		// Set new based on the scene effects bounding box
		BBox2C	rNewLayout( Vector2C( 0, 0 ), Vector2C( (float32)pScene->get_layout_width(), (float32)pScene->get_layout_height() ) );
		BBox2C	rNewViewport( Vector2C( 0, 0 ), Vector2C( (float32)pScene->get_layout_width(), (float32)pScene->get_layout_height() ) );
		BBox2C	rOldClipLayout;


		pGraphDevice->begin_draw();

		// set newly calculated layout viewport
		// the viewport is set after begin_draw() because begin draw saves the viewport and projection matrices.
		pViewport->set_layout( rNewLayout );
		pViewport->set_viewport( rNewViewport );

		// Set correct pixels size.
		// It's often that the graphics buffer has different aspect ratio than the rendering
		// output is. Problems arise on perspective projection etc. The following code
		// handles the situation.
		float32	f32AspectVP = (float32)pViewport->get_height() / (float32)pViewport->get_width();
		float32	f32AspectHW = (float32)pScene->get_layout_height() / (float32)pScene->get_layout_width();
		pViewport->set_pixel_aspect_ratio( f32AspectHW / f32AspectVP  );


		// clear device
		pGraphDevice->clear_device( GRAPHICSDEVICE_ALLBUFFERS, pScene->get_layout_color() );

		// wrap the time
		while( i32Time < 0 )
			i32Time += pScene->get_duration();
		while( i32Time > pScene->get_duration() )
			i32Time -= pScene->get_duration();

		//
		// draw effects
		//

		pGraphDevice->begin_effects();

		for( int32 i = pScene->get_layer_count() - 1; i >= 0 ; i-- ) {
			LayerC*	pLayer = pScene->get_layer( i );
			if( !pLayer )
				continue;
			if( !pLayer->get_timesegment()->is_visible( i32Time ) )
				continue;
			int32	i32TimeOffset;
			i32TimeOffset = pLayer->get_timesegment()->get_segment_start();
			for( int32 j = pLayer->get_effect_count() - 1; j >= 0; j-- ) {
				EffectI*	pEffect = pLayer->get_effect( j );
				if( !pEffect )
					continue;
				if( pEffect->get_timesegment()->is_visible( i32Time - i32TimeOffset ) ) {
					pEffect->eval_state( i32Time - (i32TimeOffset + pEffect->get_timesegment()->get_segment_start()) );
				}
			}
		}

		// restore old layout and viewport
		pViewport->set_layout( rOldLayout );
		pViewport->set_viewport( rOldViewport );
		pViewport->set_pixel_aspect_ratio( f32OldAspect );

		pGraphDevice->end_effects();
		pGraphDevice->end_draw();

		// flush device (swap buffers)
		pGraphDevice->flush();

		pGraphDevice->set_render_target( pOldGBuffer );
	}
}



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

static
int32
nearest_power2( int32 i32Num )
{
	int32	i = ceil_power2( i32Num );	// bigger
	int32	j = i / 2;					// smaller

	int32	i32DiffI = i - i32Num;
	int32	i32DiffJ = i32Num - j;

	// diff J has to be twice as little as diffI to be chosen.
	i32DiffI /= 2;

	if( i32DiffJ < i32DiffI )
		return j;

	return i;
}


void
SceneToImageImportC::update_gbuffer()
{
	// Dont create buffers for copies.
	if( get_original() || !m_pDemoInterface )
		return;

	DeviceContextC*	pContext = m_pDemoInterface->get_device_context();

	GraphicsBufferI*	pGBuffer = 0;

	if( m_pBufferHandle )
	{
		// We have shared buffer handle, remove the internal buffer if it exists.
		if( m_pGBuffer )
		{
			m_pGBuffer->release();
			m_pGBuffer = 0;
		}
	}
	else 
	{
		if( !m_pSceneHandle )
			return;

		SceneImportC*	pImp = (SceneImportC*)m_pSceneHandle->get_importable();
		SceneC*			pScene = pImp->get_scene();

		int32	i32Width = pScene->get_layout_width();
		int32	i32Height = pScene->get_layout_height();
		int32	i32WidthPow2 = nearest_power2( i32Width );
		int32	i32HeightPow2 = nearest_power2( i32Height );

		if( m_pGBuffer )
		{
			// Resize if necessary
			GraphicsViewportI*	pViewport = (GraphicsViewportI*)m_pGBuffer->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
			if( pViewport )
			{

				if( i32WidthPow2 != pViewport->get_width() || i32HeightPow2 != pViewport->get_height() )
				{
					// Update
					if( !m_pGBuffer->init( GRAPHICSBUFFER_INIT_TEXTURE, i32WidthPow2, i32HeightPow2 ) )
					{
						m_pGBuffer->release();
						m_pGBuffer = 0;
					}

					if( m_pGBuffer )
						m_rTexBounds = m_pGBuffer->get_tex_coord_bounds();
				}
			}
		}
		else
		{
			GraphicsDeviceI*	pDev = (GraphicsDeviceI*)pContext->query_interface( SUPERCLASS_GRAPHICSDEVICE );

			if( !pDev ) {
				return;
			}

			// Create new
			m_pGBuffer = pDev->create_graphicsbuffer();

			if( !m_pGBuffer->init( GRAPHICSBUFFER_INIT_TEXTURE, i32WidthPow2, i32HeightPow2 ) )
			{
				m_pGBuffer->release();
				m_pGBuffer = 0;
			}

			if( m_pGBuffer )
				m_rTexBounds = m_pGBuffer->get_tex_coord_bounds();
		}
	}


	return;
}

const char*
SceneToImageImportC::get_info()
{
	static char	szInfo[256] = "";

	if( m_pSceneHandle ) {
		SceneImportC*	pImp = (SceneImportC*)m_pSceneHandle->get_importable();
		SceneC*			pScene = pImp->get_scene();
		if( pScene ) {
			_snprintf( szInfo, 255, "%d x %d", pScene->get_layout_width(), pScene->get_layout_height() );
		}
	}

	return szInfo;
}

ClassIdC
SceneToImageImportC::get_default_effect()
{
	const	PluginClass::ClassIdC	CLASS_IMAGE_EFFECT( 0, 100 );
	return CLASS_IMAGE_EFFECT;
}

bool
SceneToImageImportC::equals( ImportableI* pImp )
{
	return false;
}

int32
SceneToImageImportC::get_duration()
{
	if( m_pSceneHandle ) {
		SceneImportC*	pImp = (SceneImportC*)m_pSceneHandle->get_importable();
		SceneC*			pScene = pImp->get_scene();
		if( pScene ) {
			return pScene->get_duration();
		}
	}
	return 0;
}

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

float32
SceneToImageImportC::get_end_label()
{
	if( m_pSceneHandle ) {
		SceneImportC*	pImp = (SceneImportC*)m_pSceneHandle->get_importable();
		SceneC*			pScene = pImp->get_scene();
		if( pScene ) {
			return (float32)(pScene->get_duration() * pScene->get_edit_accuracy() / 256);
		}
	}
	return 0;
}


enum SceneToImageImportChunksE {
	CHUNK_SCENETOIMAGEIMPORT_BASE	=	0x1000,
};

const uint32	SCENETOIMAGEIMPORT_VERSION = 2;
const uint32	SCENETOIMAGEIMPORT_VERSION_1 = 1;


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

	// file base
	pSave->begin_chunk( CHUNK_SCENETOIMAGEIMPORT_BASE, SCENETOIMAGEIMPORT_VERSION );
		// Name
		sStr = m_sName;
		if( sStr.size() > 255 )
			sStr.resize( 255 );
		ui32Error = pSave->write_str( sStr.c_str() );
		// File handle
		uint32	ui32FileId = 0xffffffff;
		if( m_pSceneHandle )
			ui32FileId = m_pSceneHandle->get_id();
		ui32Error = pSave->write( &ui32FileId, sizeof( ui32FileId ) );
		// Buffer handle
		ui32FileId = 0xffffffff;
		if( m_pBufferHandle )
			ui32FileId = m_pBufferHandle->get_id();
		ui32Error = pSave->write( &ui32FileId, sizeof( ui32FileId ) );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_SCENETOIMAGEIMPORT_BASE:
			{
				if( pLoad->get_chunk_version() == SCENETOIMAGEIMPORT_VERSION_1 ) {
					// Name
					ui32Error = pLoad->read_str( szStr );
					m_sName = szStr;
					// File handle
					uint32	ui32FileId;
					ui32Error = pLoad->read( &ui32FileId, sizeof( ui32FileId ) );
					if( ui32FileId != 0xffffffff )
						pLoad->add_file_handle_patch( (void**)&m_pSceneHandle, ui32FileId );
				}
				else if( pLoad->get_chunk_version() == SCENETOIMAGEIMPORT_VERSION ) {
					// Name
					ui32Error = pLoad->read_str( szStr );
					m_sName = szStr;
					// File handle
					uint32	ui32FileId;
					ui32Error = pLoad->read( &ui32FileId, sizeof( ui32FileId ) );
					if( ui32FileId != 0xffffffff )
						pLoad->add_file_handle_patch( (void**)&m_pSceneHandle, ui32FileId );
					// Buffer handle
					ui32Error = pLoad->read( &ui32FileId, sizeof( ui32FileId ) );
					if( ui32FileId != 0xffffffff )
						pLoad->add_file_handle_patch( (void**)&m_pBufferHandle, ui32FileId );
				}
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}


//////////////////////////////////////////////////////////////////////////
//
// Shared Buffer class implementation.
//


SharedBufferImportC::SharedBufferImportC() :
	m_pSceneHandle( 0 ),
	m_pGBuffer( 0 ),
	m_i32Width( 0 ),
	m_i32Height( 0 ),
	m_bCheckBuffer( false )
{
	// empty
}

SharedBufferImportC::SharedBufferImportC( EditableI* pOriginal ) :
	ImportableImageI( pOriginal ),
	m_pSceneHandle( 0 ),
	m_pGBuffer( 0 ),
	m_i32Width( 0 ),
	m_i32Height( 0 ),
	m_bCheckBuffer( false )
{
	// empty
}

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

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

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

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

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

void
SharedBufferImportC::copy( EditableI* pEditable )
{
	SharedBufferImportC*	pFile = (SharedBufferImportC*)pEditable;
	m_pSceneHandle = pFile->m_pSceneHandle;
	m_sName = pFile->m_sName;
	m_i32Width = pFile->m_i32Width;
	m_i32Height = pFile->m_i32Height;
	m_pGBuffer = 0;
	// Force to check buffer change.
	update_gbuffer();
}

void
SharedBufferImportC::restore( EditableI* pEditable )
{
	SharedBufferImportC*	pFile = (SharedBufferImportC*)pEditable;
	m_pSceneHandle = pFile->m_pSceneHandle;
	m_sName = pFile->m_sName;
	m_i32Width = pFile->m_i32Width;
	m_i32Height = pFile->m_i32Height;
	m_pGBuffer = pFile->m_pGBuffer;
	// Force to check buffer change.
	if( !get_original() )
	{
		update_gbuffer();
		if( m_pParent )
			m_pParent->update_notify( this );
	}
}


	// overdriven method from datablock
void
SharedBufferImportC::set_alive( bool bState )
{
	DataBlockI::set_alive( bState );
	// Force to check buffer change.
	update_gbuffer();
}

const char*
SharedBufferImportC::get_filename()
{
	return m_sName.c_str();
}

void
SharedBufferImportC::set_filename( const char* szName )
{
	m_sName = szName;
}

bool
SharedBufferImportC::create_file( DemoInterfaceC* pInterface )
{
	m_pDemoInterface = pInterface;
	m_sName = "Shared Buffer";
	return true;
}


bool
SharedBufferImportC::prompt_properties()
{

#ifndef PAJAPLAYER

	CSharedBufferDlg	rDlg;

	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	rDlg.m_sName = m_sName.c_str();
	rDlg.m_iWidth = m_i32Width;
	rDlg.m_iHeight = m_i32Height;

	if( rDlg.DoModal() == IDOK ) {
		m_sName = rDlg.m_sName;
		m_i32Width = rDlg.m_iWidth;
		m_i32Height = rDlg.m_iHeight;
		
		update_gbuffer();
	}
	else
		return false;

#endif

	return true;
}

bool
SharedBufferImportC::has_properties()
{
	return true;
}

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

	if( ui32Reason == INIT_INITIAL_UPDATE )
	{
		update_gbuffer();
	}
}

uint32
SharedBufferImportC::get_reference_file_count()
{
	return 0;
}

FileHandleC*
SharedBufferImportC::get_reference_file( uint32 ui32Index )
{
	return 0;
}

	
ClassIdC
SharedBufferImportC::get_class_id()
{
	return CLASS_SHAREDBUFFER_IMPORTABLE;
}

SuperClassIdC
SharedBufferImportC::get_super_class_id()
{
	return SUPERCLASS_IMAGE;
}

const char*
SharedBufferImportC::get_class_name()
{
	return "Shared Buffer";
}


float32
SharedBufferImportC::get_width()
{
	return (float32)m_i32Width;
}

float32
SharedBufferImportC::get_height()
{
	return (float32)m_i32Height;
}

int32
SharedBufferImportC::get_data_width()
{
	return 0;
}

int32
SharedBufferImportC::get_data_height()
{
	return 0;
}

int32
SharedBufferImportC::get_data_pitch()
{
	return 0;
}

BBox2C&
SharedBufferImportC::get_tex_coord_bounds()
{
	return m_rTexBounds;
}

int32
SharedBufferImportC::get_data_bpp()
{
	return 0;
}

uint8*
SharedBufferImportC::get_data()
{
	return 0;
}

void
SharedBufferImportC::bind_texture( DeviceInterfaceI* pInterface, PajaTypes::uint32 ui32Stage, uint32 ui32Properties )
{
	if( m_pGBuffer ) {
		m_pGBuffer->bind_texture( pInterface, ui32Stage, ui32Properties );
	}
}

GraphicsBufferI*
SharedBufferImportC::get_gbuffer()
{
	return m_pGBuffer;
}

void
SharedBufferImportC::eval_state( int32 i32Time )
{
	// empty
	if( m_bCheckBuffer )
	{
		update_gbuffer();
		m_bCheckBuffer = false;
	}
}


void
SharedBufferImportC::update_gbuffer()
{
	// Dont create buffers for copies.
	if( get_original() || !m_pDemoInterface )
	{
		return;
	}

	DeviceContextC*	pContext = m_pDemoInterface->get_device_context();

	GraphicsDeviceI*	pDev = (GraphicsDeviceI*)pContext->query_interface( SUPERCLASS_GRAPHICSDEVICE );
	if( !pDev )
		return;

	if( !m_pGBuffer )
		m_pGBuffer = pDev->create_graphicsbuffer();

	if( !m_pGBuffer )
		return;

	if( !m_pGBuffer->init( GRAPHICSBUFFER_INIT_TEXTURE, m_i32Width, m_i32Height ) )
	{
		m_pGBuffer->release();
		m_pGBuffer = 0;
		return;
	}

	if( m_pGBuffer )
		m_rTexBounds = m_pGBuffer->get_tex_coord_bounds();

	return;
}

const char*
SharedBufferImportC::get_info()
{
	static char	szInfo[256] = "-";

	if( m_pGBuffer )
	{
		_snprintf( szInfo, 255, "%d x %d", m_i32Width, m_i32Height );
	}

	return szInfo;
}

ClassIdC
SharedBufferImportC::get_default_effect()
{
	const	PluginClass::ClassIdC	CLASS_IMAGE_EFFECT( 0, 100 );
	return CLASS_IMAGE_EFFECT;
}

bool
SharedBufferImportC::equals( ImportableI* pImp )
{
	return false;
}

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

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

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


enum SharedBufferImportChunksE {
	CHUNK_SHAREDBUFFERIMPORT_BASE	=	0x1000,
};

const uint32	SHAREDBUFFERIMPORT_VERSION = 1;


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

	// file base
	pSave->begin_chunk( CHUNK_SHAREDBUFFERIMPORT_BASE, SHAREDBUFFERIMPORT_VERSION );
		// Name
		sStr = m_sName;
		if( sStr.size() > 255 )
			sStr.resize( 255 );
		ui32Error = pSave->write_str( sStr.c_str() );
		// Width
		ui32Error = pSave->write( &m_i32Width, sizeof( m_i32Width ) );
		// Height
		ui32Error = pSave->write( &m_i32Height, sizeof( m_i32Height ) );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_SHAREDBUFFERIMPORT_BASE:
			{
				if( pLoad->get_chunk_version() == SHAREDBUFFERIMPORT_VERSION ) {
					// Name
					ui32Error = pLoad->read_str( szStr );
					m_sName = szStr;
					// Width
					ui32Error = pLoad->read( &m_i32Width, sizeof( m_i32Width ) );
					// Height
					ui32Error = pLoad->read( &m_i32Height, sizeof( m_i32Height ) );
				}
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}


