
#include "OutputTrackC.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 <string>
#include <vector>
#include <stdlib.h>		//qsort


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


enum OutputTrackChunksE {
	CHUNK_TRACK_BASE =		0x1000,
	CHUNK_TRACK_SCENE =		0x2000,
};

const uint32	TRACK_VERSION = 1;


OutputTrackC::OutputTrackC( OutputChannelC* pParent ) :
	m_pParent( pParent )
{
}

OutputTrackC::OutputTrackC( EditableI* pOriginal ) :
	EditableI( pOriginal ),
	m_pParent( 0 )
{
}

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

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

OutputTrackC*
OutputTrackC::create_new( OutputChannelC* pParent )
{
	return new OutputTrackC( pParent );
}

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

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

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

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

	for( i = 0; i < pTrack->m_rSegments.size(); i++ )
		m_rSegments.push_back( (OutputTrackSegmentC*)pTrack->m_rSegments[i]->duplicate() );

	m_pParent = pTrack->m_pParent;
}

void
OutputTrackC::restore( EditableI* pEditable )
{
	OutputTrackC*	pTrack = (OutputTrackC*)pEditable;
	m_sName = pTrack->m_sName;
	m_rSegments = pTrack->m_rSegments;
	m_pParent = pTrack->m_pParent;
}

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

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

void
OutputTrackC::set_parent( OutputChannelC* pOutCh )
{
	m_pParent = pOutCh;
}

OutputChannelC*
OutputTrackC::get_parent() const
{
	return m_pParent;
}


PajaTypes::int32
OutputTrackC::get_segment_count()
{
	return m_rSegments.size();
}

OutputTrackSegmentC*
OutputTrackC::get_segment( int32 i32Index )
{
	return m_rSegments[i32Index];
}


PajaTypes::int32
OutputTrackC::add_segment( OutputTrackSegmentC* pSeg )
{
	if( get_undo() )
		get_undo()->add_discardable_data( pSeg, DATA_CREATED );
	m_rSegments.insert( m_rSegments.begin(), pSeg );
	pSeg->set_parent( this );
	return m_rSegments.size() - 1;
}

void
OutputTrackC::del_segment( PajaTypes::int32 i32Index )
{
	if( i32Index >= 0 && i32Index < m_rSegments.size() ) {
		OutputTrackSegmentC*	pSeg = m_rSegments[i32Index];
		m_rSegments.erase( m_rSegments.begin() + i32Index );
		if( get_undo() )
			get_undo()->add_discardable_data( pSeg, DATA_REMOVED );
	}
}


OutputTrackSegmentC*
OutputTrackC::remove_segment( PajaTypes::int32 i32Index )
{
	if( i32Index >= 0 && i32Index < m_rSegments.size() ) {
		OutputTrackSegmentC*	pSeg = m_rSegments[i32Index];
		m_rSegments.erase( m_rSegments.begin() + i32Index );
		return pSeg;
	}

	return 0;
}

OutputTrackSegmentC*
OutputTrackC::remove_segment( OutputTrackSegmentC* pSeg )
{
	for( uint32 i = 0; i < m_rSegments.size(); i++ ) {
		if( m_rSegments[i] == pSeg ) {
			m_rSegments.erase( m_rSegments.begin() + i );
			return pSeg;
		}
	}
	return 0;
}


void
OutputTrackC::insert_segment( PajaTypes::int32 i32IndexBefore, OutputTrackSegmentC* pSeg )
{
	pSeg->set_parent( this );
	if( i32IndexBefore >= m_rSegments.size() )
		m_rSegments.push_back( pSeg );
	else
		m_rSegments.insert( m_rSegments.begin() + i32IndexBefore, pSeg );
}



static
int
compare_func( const void* vpParam1, const void* vpParam2 )
{
	OutputTrackSegmentC*	pSeg1 = *(OutputTrackSegmentC**)vpParam1;
	OutputTrackSegmentC*	pSeg2 = *(OutputTrackSegmentC**)vpParam2;

	if( pSeg1->get_start_time() < pSeg2->get_start_time() )
		return -1;

	return 1;
}

void
OutputTrackC::sort_segments()
{
	qsort( m_rSegments.begin(), m_rSegments.size(), sizeof( OutputTrackSegmentC* ), compare_func );
}


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

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

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

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

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



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


	// 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_TRACK_BASE, TRACK_VERSION );
		// name
		ui32Error = pSave->write_str( sStr.c_str() );
		// flags
		ui32Error = pSave->write( &m_i32Flags, sizeof( m_i32Flags ) );
	pSave->end_chunk();

	//
	// save scenes
	//
/*	for( int32 i = m_rEffects.size() - 1; i >= 0 ; i-- ) {
		pSave->begin_chunk( CHUNK_TRACK_scene, TRACK_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
OutputTrackC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	char	szStr[256];

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

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

/*		case CHUNK_TRACK_SCENE:
			{
				if( pLoad->get_chunk_version() == TRACK_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 ) {

						ui32Error = pEffect->load( pLoad );
						// add effect to layer
						add_scene( pEffect );
					}
					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;
}

