#include <windows.h>

#include "LayerC.h"
#include "PajaTypes.h"
#include "DataBlockI.h"
#include "EditableI.h"
#include "UndoC.h"
#include "TimeSegmentC.h"
#include "EffectI.h"
#include "GizmoI.h"
#include "ParamI.h"
#include "ImportableI.h"
#include "FileIO.h"
#include "TimeContextC.h"
#include "DeviceContextC.h"
#include "Composition.h"
#include "ClassIdC.h"
#include <string>
#include <vector>


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


enum LayerChunksE {
	CHUNK_LAYER_BASE =		0x1000,
	CHUNK_LAYER_EFFECT =	0x2000,
};

const uint32	LAYER_VERSION = 1;


LayerC::LayerC( SceneC* pParent ) :
	m_i32Flags( ITEM_LAYER | ITEM_VISIBLE ),
	m_pParent( pParent )
{
	m_pTimeSegment = TimeSegmentC::create_new();
}

LayerC::LayerC( EditableI* pOriginal ) :
	EditableI( pOriginal ),
	m_i32Flags( ITEM_LAYER | ITEM_VISIBLE ),
	m_pParent( 0 )
{
//	m_pTimeSegment = TimeSegmentC::create_new();
}

LayerC::~LayerC()
{
	// if this is a clone from the original, dont delete the data.
	if( get_original() )
		return;

	m_pTimeSegment->release();

	uint32	i;
	for( i = 0; i < m_rEffects.size(); i++ )
		m_rEffects[i]->release();
}

LayerC*
LayerC::create_new( SceneC* pParent )
{
	return new LayerC( pParent );
}

DataBlockI*
LayerC::create()
{
	return new LayerC( (SceneC*)0 );
}

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

void
LayerC::copy( EditableI* pEditable )
{
	uint32	i;
	LayerC*	pLayer = (LayerC*)pEditable;
	m_sName = pLayer->m_sName;

	// remove old effects
	for( i = 0; i < m_rEffects.size(); i++ )
		m_rEffects[i]->release();
	m_rEffects.clear();

	for( i = 0; i < pLayer->m_rEffects.size(); i++ )
		m_rEffects.push_back( (EffectI*)pLayer->m_rEffects[i]->duplicate() );

	m_i32Flags = pLayer->m_i32Flags;
	m_pTimeSegment->copy( pLayer->m_pTimeSegment );
	m_pParent = pLayer->m_pParent;
}

void
LayerC::restore( EditableI* pEditable )
{
	LayerC*	pLayer = (LayerC*)pEditable;
	m_sName = pLayer->m_sName;
	m_rEffects = pLayer->m_rEffects;
	m_i32Flags = pLayer->m_i32Flags;
	m_pTimeSegment = pLayer->m_pTimeSegment;
	m_pParent = pLayer->m_pParent;

	// Let the parent know that we were changed.
	if( !get_original() )
	{
		if( m_pParent )
			m_pParent->update_notify( this );
	}
}

PluginClass::SuperClassIdC
LayerC::get_base_class_id() const
{
	return BASECLASS_LAYER;
}

PajaTypes::int32
LayerC::add_effect( EffectI* pEffect )
{
	if( get_undo() )
		get_undo()->add_discardable_data( pEffect, DATA_CREATED );
	m_rEffects.insert( m_rEffects.begin(), pEffect );
	pEffect->set_parent( this );
	return m_rEffects.size() - 1;
}

void
LayerC::del_effect( PajaTypes::int32 i32Index )
{
	if( i32Index >= 0 && i32Index < (int32)m_rEffects.size() ) {
		EffectI*	pEffect = m_rEffects[i32Index];
		m_rEffects.erase( m_rEffects.begin() + i32Index );
		if( get_undo() )
			get_undo()->add_discardable_data( pEffect, DATA_REMOVED );
	}
}

TimeSegmentC*
LayerC::get_timesegment()
{
	return m_pTimeSegment;
}

void
LayerC::set_name( const char* szName )
{
	m_sName = szName;
}

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

PajaTypes::int32
LayerC::get_effect_count()
{
	return m_rEffects.size();
}

EffectI*
LayerC::get_effect( int32 i32Index )
{
	return m_rEffects[i32Index];
}

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

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

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

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

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

void
LayerC::set_parent( SceneC* pScene )
{
	m_pParent = pScene;
}

SceneC*
LayerC::get_parent() const
{
	return m_pParent;
}

EffectI*
LayerC::remove_effect( PajaTypes::int32 i32Index )
{
	if( i32Index >= 0 && i32Index < (int32)m_rEffects.size() ) {
		EffectI*	pEffect = m_rEffects[i32Index];
		m_rEffects.erase( m_rEffects.begin() + i32Index );
		return pEffect;
	}
	return 0;
}

void
LayerC::insert_effect( PajaTypes::int32 i32IndexBefore, EffectI* pEffect  )
{
	pEffect->set_parent( this );
	if( i32IndexBefore >= (int32)m_rEffects.size() )
		m_rEffects.push_back( pEffect );
	else
		m_rEffects.insert( m_rEffects.begin() + i32IndexBefore, pEffect );
}

uint32
LayerC::update_notify( EditableI* pCaller )
{
	// Let the parent know that we were changed.
	if( m_pParent )
		return m_pParent->update_notify( pCaller );
	// do nothing
	return PARAM_NOTIFY_NONE;
}

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

	//
	// save layer base information
	//

	// make sure this string fits into the buffer when it's loaded back
	std::string	sStr = m_sName;
	if( sStr.size() > 255 )
		sStr.resize( 255 );

	pSave->begin_chunk( CHUNK_LAYER_BASE, LAYER_VERSION );
		// name
		ui32Error = pSave->write_str( sStr.c_str() );
		// flags
		ui32Error = pSave->write( &m_i32Flags, sizeof( m_i32Flags ) );
		// timesegment
		ui32Error = m_pTimeSegment->save( pSave );
	pSave->end_chunk();

	//
	// save layers
	//
	for( int32 i = m_rEffects.size() - 1; i >= 0 ; i-- ) {
		pSave->begin_chunk( CHUNK_LAYER_EFFECT, LAYER_VERSION );
			// write class id
			uint32	ui32ClassId[2];
			ui32ClassId[0] = m_rEffects[i]->get_class_id().get_class_a();
			ui32ClassId[1] = m_rEffects[i]->get_class_id().get_class_b();
			pSave->write( &ui32ClassId, sizeof( ui32ClassId ) );

			// write class name
			std::string	sStr = m_rEffects[i]->get_class_name();
			if( sStr.size() > 255 )
				sStr.resize( 255 );
			pSave->write_str( sStr.c_str() );

			// write effect
			m_rEffects[i]->save( pSave );
		pSave->end_chunk();
	}

	return ui32Error;
}

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

	FactoryC*		pFactory = pLoad->get_factory();

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_LAYER_BASE:
			{
				if( pLoad->get_chunk_version() == LAYER_VERSION ) {
					// name
					ui32Error = pLoad->read_str( szStr );
					m_sName = szStr;
					// flags
					ui32Error = pLoad->read( &m_i32Flags, sizeof( m_i32Flags ) );
					// timesegment
					ui32Error = m_pTimeSegment->load( pLoad );
				}
			}
			break;

		case CHUNK_LAYER_EFFECT:
			{
				if( pLoad->get_chunk_version() == LAYER_VERSION ) {
					// read class id
					uint32	ui32ClassId[2];
					pLoad->read( &ui32ClassId, sizeof( ui32ClassId ) );
					// read class name
					ui32Error = pLoad->read_str( szStr );

					ClassIdC	rCLassId( ui32ClassId[0], ui32ClassId[1] );

					EffectI*	pEffect = (EffectI*)pFactory->create( rCLassId );

					if( pEffect ) {
						// Add effect to layer. This os done before the load, so that the parent is available on load time.
						add_effect( pEffect );

						ui32Error = pEffect->load( pLoad );
					}
					else {
						char		szClassId[32];
						std::string	sMsg;

						_snprintf( szClassId, 31, " (0x%08x, 0x%08x)", ui32ClassId[0], ui32ClassId[1] );	// this isnt the way to go, but I hate c++ streams

						sMsg = "Load Layer: Effect ";
						sMsg += szStr;
						sMsg += szClassId;
						sMsg += "cannot be created. Propably missing DLL.";

						pLoad->add_error_message( sMsg.c_str() );

						ui32Error = IO_ERROR_READ;
					}
				}
			}
			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;
}

