
#include "UndoC.h"
#include "DataBlockI.h"
#include "EditableI.h"
#include "PajaTypes.h"
#include <vector>
#include <string>
#include <assert.h>


using namespace PajaTypes;
using namespace Edit;


UndoC::UndoC( const char* szName ) :
	m_sName( szName ),
	m_bUndoDone( false )
{
	// Empty
}

UndoC::~UndoC()
{
	uint32 i;

	// Delete undo blocks
	for( i = 0; i < m_rUndoBlocks.size(); i++ ) {
		m_rUndoBlocks[i]->release();
		m_rUndoBlocks[i] = 0;
	}

	// Delete redo blocks
	for( i = 0; i < m_rRedoBlocks.size(); i++ ) {
		m_rRedoBlocks[i]->release();
		m_rRedoBlocks[i] = 0;
	}

	for( i = 0; i < m_rDiscardable.size(); i++ ) {
		if( m_rDiscardable[i].m_bDiscard ) {
			if( m_rDiscardable[i].m_pData )
				m_rDiscardable[i].m_pData->release();
			m_rDiscardable[i].m_pData = 0;
		}
	}
}

void
UndoC::undo()
{
	PajaTypes::int32 i;

	if( !m_bUndoDone ) {
		// Store the objects for redo. And do it only once.
		for( i = 0; i < (int32)m_rUndoBlocks.size(); i++ ) {
			assert( m_rUndoBlocks[i]->get_original() );
			EditableI*	pBlock = m_rUndoBlocks[i]->get_original()->clone();
			m_rRedoBlocks.push_back( pBlock );
		}
	}

	// Do undo
	for( i = (int32)m_rUndoBlocks.size() - 1; i >= 0; i-- ) {
		assert( m_rUndoBlocks[i]->get_original() );
		m_rUndoBlocks[i]->get_original()->restore( m_rUndoBlocks[i] );
	}

	m_bUndoDone = true;

	// Invert discartion flags. Now created objects will be deleted.
	for( i = 0; i < (int32)m_rDiscardable.size(); i++ ) {
		m_rDiscardable[i].m_bDiscard = !m_rDiscardable[i].m_bDiscard;
		if( m_rDiscardable[i].m_bDiscard )
			m_rDiscardable[i].m_pData->set_alive( false );
		else
			m_rDiscardable[i].m_pData->set_alive( true );
	}
}

void
UndoC::redo()
{
	if( m_bUndoDone ) {
		// Do redo
		for( uint32 i = 0; i < m_rRedoBlocks.size(); i++ ) {
			assert( m_rRedoBlocks[i]->get_original() );
			m_rRedoBlocks[i]->get_original()->restore( m_rRedoBlocks[i] );
		}
		// Return discartion flags as it was before undo.
		for( i = 0; i < m_rDiscardable.size(); i++ ) {
			m_rDiscardable[i].m_bDiscard = !m_rDiscardable[i].m_bDiscard;
			if( m_rDiscardable[i].m_bDiscard )
				m_rDiscardable[i].m_pData->set_alive( false );
			else
				m_rDiscardable[i].m_pData->set_alive( true );
		}
	}
}

void
UndoC::add_restore_data( EditableI* pBlock )
{
	m_rUndoBlocks.push_back( pBlock );
}

void
UndoC::add_discardable_data( DataBlockI* pBlock, uint32 ui32Flags )
{
	DiscardableS	rDiscardable;
	rDiscardable.m_pData = pBlock;
	rDiscardable.m_bDiscard = ui32Flags == DATA_REMOVED ? true : false;		// remove removed data.
	if( rDiscardable.m_bDiscard )
		pBlock->set_alive( false );
	m_rDiscardable.push_back( rDiscardable );
}

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

bool
UndoC::can_redo()
{
	return m_bUndoDone;
}
