// XTPTrackUndoManager.cpp : implementation of the CXTPTrackUndoManager class.
//
// This file is a part of the XTREME REPORTCONTROL MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "Common/XTPCustomHeap.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPSmartPtrInternalT.h"


#include "../XTPReportDefines.h"
#include "../XTPReportRecordItem.h"
#include "../XTPReportControl.h"


#include "XTPTrackControl.h"
#include "XTPTrackControlItem.h"
#include "XTPTrackUndoManager.h"
#include "XTPTrackBlock.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

const int CONTEXT_NONE = 0;
const int CONTEXT_UNDO = 1;
const int CONTEXT_REDO = 2;

//////////////////////////////////////////////////////////////////////////
// CXTPTrackUndoCommand

CXTPTrackUndoCommand::CXTPTrackUndoCommand(int nID)
	: m_nID(nID)
{

}

CXTPTrackUndoCommand::~CXTPTrackUndoCommand()
{
}

void CXTPTrackUndoCommand::Undo()
{
	ASSERT(FALSE);
}


/////////////////////////////////////////////////////////////////////////
// CXTPTrackUndoManager
CXTPTrackUndoManager::CXTPTrackUndoManager()
{
	m_nUndoContext = CONTEXT_NONE;

	m_pUndoGroup = NULL;

#ifdef _XTP_ACTIVEX
	EnableAutomation();
	EnableTypeLib();
#endif
}

CXTPTrackUndoManager::~CXTPTrackUndoManager()
{
	Clear();

	SAFE_DELETE(m_pUndoGroup);
}

void CXTPTrackUndoManager::Clear()
{
	int i;
	for (i = 0; i < m_arrUndoCommands.GetSize(); i++)
	{
		delete m_arrUndoCommands[i];
	}
	m_arrUndoCommands.RemoveAll();

	for (i = 0; i < m_arrRedoCommands.GetSize(); i++)
	{
		delete m_arrRedoCommands[i];
	}
	m_arrRedoCommands.RemoveAll();

	SAFE_DELETE(m_pUndoGroup);
}

void CXTPTrackUndoManager::StartGroup()
{
	ASSERT(m_pUndoGroup == NULL);
	m_pUndoGroup = new CXTPTrackUndoGroupCommand(this);
}

void CXTPTrackUndoManager::EndGroup()
{
	if (m_pUndoGroup->GetCount() == 0)
	{
		delete m_pUndoGroup;
	}
	else
	{
		if (m_nUndoContext  != CONTEXT_UNDO)
			m_arrUndoCommands.Add(m_pUndoGroup);
		else
			m_arrRedoCommands.Add(m_pUndoGroup);
	}
	m_pUndoGroup = NULL;
}


void CXTPTrackUndoManager::AddUndoCommand(CXTPTrackUndoCommand* pCommand)
{
	if (m_pUndoGroup)
	{
		m_pUndoGroup->Add(pCommand);
	}
	else if (m_nUndoContext != CONTEXT_UNDO)
	{
		m_arrUndoCommands.Add(pCommand);
	}
	else
	{
		m_arrRedoCommands.Add(pCommand);
	}

	if (m_nUndoContext == CONTEXT_NONE)
	{
		for (int i = 0; i < m_arrRedoCommands.GetSize(); i++)
		{
			delete m_arrRedoCommands[i];
		}
		m_arrRedoCommands.RemoveAll();
	}
}


BOOL CXTPTrackUndoManager::CanUndo() const
{
	return m_arrUndoCommands.GetSize() > 0;
}

BOOL CXTPTrackUndoManager::CanRedo() const
{
	return m_arrRedoCommands.GetSize() > 0;
}

void CXTPTrackUndoManager::Undo()
{
	if (m_arrUndoCommands.GetSize() > 0)
	{
		m_nUndoContext = 1;

		CXTPTrackUndoCommand* pUndoCommand = m_arrUndoCommands[m_arrUndoCommands.GetSize() - 1];

		pUndoCommand->Undo();
		delete pUndoCommand;

		m_arrUndoCommands.RemoveAt(m_arrUndoCommands.GetSize() - 1);

		m_nUndoContext = 0;
	}
}

void CXTPTrackUndoManager::Redo()
{
	if (m_arrRedoCommands.GetSize() > 0)
	{
		m_nUndoContext = 2;

		CXTPTrackUndoCommand* pUndoCommand = m_arrRedoCommands[m_arrRedoCommands.GetSize() - 1];

		pUndoCommand->Undo();
		delete pUndoCommand;

		m_arrRedoCommands.RemoveAt(m_arrRedoCommands.GetSize() - 1);

		m_nUndoContext = 0;
	}
}


//////////////////////////////////////////////////////////////////////////
// CXTPTrackUndoGroupCommand

CXTPTrackUndoGroupCommand::CXTPTrackUndoGroupCommand(CXTPTrackUndoManager* pManager)
: CXTPTrackUndoCommand(XTP_ID_TRACKUNDO_GROUP)
{
	m_pManager = pManager;
}

CXTPTrackUndoGroupCommand::~CXTPTrackUndoGroupCommand()
{
	for (int i = (int)m_arrUndoCommands.GetSize() - 1; i >= 0; i--)
	{
		delete m_arrUndoCommands[i];
	}
	m_arrUndoCommands.RemoveAll();
}

void CXTPTrackUndoGroupCommand::Add(CXTPTrackUndoCommand* pCommand)
{
	m_arrUndoCommands.Add(pCommand);
}

void CXTPTrackUndoGroupCommand::Undo()
{
	m_pManager->StartGroup();

	for (int i = (int)m_arrUndoCommands.GetSize() - 1; i >= 0; i--)
	{
		m_arrUndoCommands[i]->Undo();
		delete m_arrUndoCommands[i];
	}
	m_arrUndoCommands.RemoveAll();

	m_pManager->EndGroup();
}

int CXTPTrackUndoGroupCommand::GetCount() const
{
	return (int)m_arrUndoCommands.GetSize();
}



//////////////////////////////////////////////////////////////////////////
// CXTPTrackUndoAddMarkerCommand

CXTPTrackUndoAddMarkerCommand::CXTPTrackUndoAddMarkerCommand(CXTPTrackMarker* pMarker)
	: CXTPTrackUndoCommand(XTP_ID_TRACKUNDO_ADDMARKER)
{
	m_pMarker = pMarker;
	m_pMarker->InternalAddRef();
}

CXTPTrackUndoAddMarkerCommand::~CXTPTrackUndoAddMarkerCommand()
{
	m_pMarker->InternalRelease();
}

void CXTPTrackUndoAddMarkerCommand::Undo()
{
	m_pMarker->Remove();
}

//////////////////////////////////////////////////////////////////////////
// CXTPTrackUndoDeleteMarkerCommand

CXTPTrackUndoDeleteMarkerCommand::CXTPTrackUndoDeleteMarkerCommand(CXTPTrackMarker* pMarker)
	: CXTPTrackUndoCommand(XTP_ID_TRACKUNDO_DELETEMARKER)
{
	m_pMarker = pMarker;
	m_pMarker->InternalAddRef();
}

CXTPTrackUndoDeleteMarkerCommand::~CXTPTrackUndoDeleteMarkerCommand()
{
	m_pMarker->InternalRelease();
}

void CXTPTrackUndoDeleteMarkerCommand::Undo()
{
	m_pMarker->InternalAddRef();
	m_pMarker->GetControl()->GetMarkers()->Add(m_pMarker);

}


///////////////////////////////////////////////////////////////////////////
// CXTPTrackUndoSetMarkerPositionCommand

CXTPTrackUndoSetMarkerPositionCommand::CXTPTrackUndoSetMarkerPositionCommand(CXTPTrackMarker* pMarker, int nOldPosition)
	: CXTPTrackUndoCommand(XTP_ID_TRACKUNDO_SETMARKERPOSITION)
{
	m_nOldPosition = nOldPosition;

	m_pMarker = pMarker;
	m_pMarker->InternalAddRef();
}

CXTPTrackUndoSetMarkerPositionCommand::~CXTPTrackUndoSetMarkerPositionCommand()
{
	m_pMarker->InternalRelease();
}

void CXTPTrackUndoSetMarkerPositionCommand::Undo()
{
	m_pMarker->SetPosition(m_nOldPosition);
}




//////////////////////////////////////////////////////////////////////////
// CXTPTrackUndoAddBlockCommand

CXTPTrackUndoAddBlockCommand::CXTPTrackUndoAddBlockCommand(CXTPTrackBlock* pBlock)
: CXTPTrackUndoCommand(XTP_ID_TRACKUNDO_ADDBLOCK)
{
	m_pBlock = pBlock;
	m_pBlock->InternalAddRef();
}

CXTPTrackUndoAddBlockCommand::~CXTPTrackUndoAddBlockCommand()
{
	m_pBlock->InternalRelease();
}

void CXTPTrackUndoAddBlockCommand::Undo()
{
	m_pBlock->Remove();
}

//////////////////////////////////////////////////////////////////////////
// CXTPTrackUndoDeleteBlockCommand

CXTPTrackUndoDeleteBlockCommand::CXTPTrackUndoDeleteBlockCommand(CXTPTrackBlock* pBlock)
	: CXTPTrackUndoCommand(XTP_ID_TRACKUNDO_DELETEBLOCK)
{
	m_pBlock = pBlock;
	m_pBlock->InternalAddRef();

	m_pOldItem = pBlock->GetItem();
	m_pOldItem->InternalAddRef();
}

CXTPTrackUndoDeleteBlockCommand::~CXTPTrackUndoDeleteBlockCommand()
{
	m_pBlock->InternalRelease();
	m_pOldItem->InternalRelease();
}

void CXTPTrackUndoDeleteBlockCommand::Undo()
{
	m_pBlock->InternalAddRef();

	m_pOldItem->Add(m_pBlock);
}

///////////////////////////////////////////////////////////////////////////
// CXTPTrackUndoSetMarkerPositionCommand

CXTPTrackUndoSetBlockPositionCommand::CXTPTrackUndoSetBlockPositionCommand(CXTPTrackBlock* pBlock, int nOldPosition, int nOldLength)
: CXTPTrackUndoCommand(XTP_ID_TRACKUNDO_SETBLOCKPOSITION)
{
	m_nOldPosition = nOldPosition;
	m_nOldLength = nOldLength;

	m_pBlock = pBlock;
	m_pBlock->InternalAddRef();
}

CXTPTrackUndoSetBlockPositionCommand::~CXTPTrackUndoSetBlockPositionCommand()
{
	m_pBlock->InternalRelease();
}

void CXTPTrackUndoSetBlockPositionCommand::Undo()
{
	m_pBlock->SetPosition(m_nOldPosition);
	m_pBlock->SetLength(m_nOldLength);
	m_pBlock->GetItem()->RecalcLayout();
}



#ifdef _XTP_ACTIVEX

BEGIN_DISPATCH_MAP(CXTPTrackUndoManager, CCmdTarget)
	DISP_FUNCTION_ID(CXTPTrackUndoManager, "Undo", 1000, Undo, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION_ID(CXTPTrackUndoManager, "Redo", 1001, Redo, VT_EMPTY, VTS_NONE)
	DISP_PROPERTY_EX_ID(CXTPTrackUndoManager, "CanUndo", 1002, CanUndo, SetNotSupported, VT_BOOL)
	DISP_PROPERTY_EX_ID(CXTPTrackUndoManager, "CanRedo", 1003, CanRedo, SetNotSupported, VT_BOOL)
	DISP_FUNCTION_ID(CXTPTrackUndoManager, "Clear", 1004, Clear, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION_ID(CXTPTrackUndoManager, "StartGroup", 1015, StartGroup, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION_ID(CXTPTrackUndoManager, "EndGroup", 1016, EndGroup, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()

// {ABC09CE5-E015-4f87-885D-DE3326A63BEA}
static const GUID IID_ITrackUndoManager =
{ 0xabc09ce5, 0xe015, 0x4f87, { 0x88, 0x5d, 0xde, 0x33, 0x26, 0xa6, 0x3b, 0xea } };

BEGIN_INTERFACE_MAP(CXTPTrackUndoManager, CCmdTarget)
INTERFACE_PART(CXTPTrackUndoManager, IID_ITrackUndoManager, Dispatch)
END_INTERFACE_MAP()

IMPLEMENT_OLETYPELIB_EX(CXTPTrackUndoManager, IID_ITrackUndoManager)

#endif