// XTPFlowGraphUndoManager.cpp : implementation of the CXTPFlowGraphUndoManager class.
//
// This file is a part of the XTREME TOOLKIT PRO 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/Resource.h"
#include "Common/XTPDrawHelpers.h"
#include "Common/XTPPropExchange.h"
#include "Common/XTPImageManager.h"

#include "XTPFlowGraphElement.h"
#include "XTPFlowGraphControl.h"
#include "XTPFlowGraphNode.h"
#include "XTPFlowGraphNodes.h"
#include "XTPFlowGraphPage.h"
#include "XTPFlowGraphUndoManager.h"
#include "XTPFlowGraphConnection.h"
#include "XTPFlowGraphConnectionPoint.h"
#include "XTPFlowGraphConnectionPoints.h"
#include "XTPFlowGraphConnections.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;

//////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoCommand

CXTPFlowGraphUndoCommand::CXTPFlowGraphUndoCommand(int nID)
	: m_nID(nID)
{

}

CXTPFlowGraphUndoCommand::~CXTPFlowGraphUndoCommand()
{
}

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


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

	m_pUndoGroup = NULL;
	m_bEnabled = TRUE;

}

CXTPFlowGraphUndoManager::~CXTPFlowGraphUndoManager()
{
	Clear();

	SAFE_DELETE(m_pUndoGroup);
}

void CXTPFlowGraphUndoManager::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 CXTPFlowGraphUndoManager::StartGroup()
{
	if (m_pUndoGroup != NULL)
	{
		m_arrGroupStack.Add(m_pUndoGroup);
	}

	m_pUndoGroup = new CXTPFlowGraphUndoGroupCommand(this);
}

void CXTPFlowGraphUndoManager::EndGroup()
{
	if (m_pUndoGroup->GetCount() == 0)
	{
		delete m_pUndoGroup;
	}
	else
	{
		if (m_arrGroupStack.GetSize() > 0)
			m_arrGroupStack[m_arrGroupStack.GetSize() - 1]->Add(m_pUndoGroup);
		else if (m_nUndoContext  != CONTEXT_UNDO)
			m_arrUndoCommands.Add(m_pUndoGroup);
		else
			m_arrRedoCommands.Add(m_pUndoGroup);
	}
	m_pUndoGroup = NULL;

	if (m_arrGroupStack.GetSize())
	{
		m_pUndoGroup = m_arrGroupStack[m_arrGroupStack.GetSize() - 1];
		m_arrGroupStack.RemoveAt(m_arrGroupStack.GetSize() - 1);
	}
}


void CXTPFlowGraphUndoManager::AddUndoCommand(CXTPFlowGraphUndoCommand* pCommand)
{
	if (!m_bEnabled)
	{
		delete pCommand;
		return;
	}

	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 CXTPFlowGraphUndoManager::CanUndo() const
{
	return m_arrUndoCommands.GetSize() > 0;
}

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

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

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

		pUndoCommand->Undo();
		delete pUndoCommand;

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

		m_nUndoContext = 0;
	}
}

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

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

		pUndoCommand->Undo();
		delete pUndoCommand;

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

		m_nUndoContext = 0;
	}
}


//////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoGroupCommand

CXTPFlowGraphUndoGroupCommand::CXTPFlowGraphUndoGroupCommand(CXTPFlowGraphUndoManager* pManager)
: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_GROUP)
{
	m_pManager = pManager;
}

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

void CXTPFlowGraphUndoGroupCommand::Add(CXTPFlowGraphUndoCommand* pCommand)
{
	m_arrUndoCommands.Add(pCommand);
}

void CXTPFlowGraphUndoGroupCommand::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 CXTPFlowGraphUndoGroupCommand::GetCount() const
{
	return (int)m_arrUndoCommands.GetSize();
}


//////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoAddNodeCommand

CXTPFlowGraphUndoAddNodeCommand::CXTPFlowGraphUndoAddNodeCommand(CXTPFlowGraphNode* pNode)
: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_ADDNODE)
{
	m_pNode = pNode;
	m_pNode->InternalAddRef();
}

CXTPFlowGraphUndoAddNodeCommand::~CXTPFlowGraphUndoAddNodeCommand()
{
	m_pNode->InternalRelease();
}

void CXTPFlowGraphUndoAddNodeCommand::Undo()
{
	m_pNode->Remove();
}

//////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoAddConnectionPointCommand

CXTPFlowGraphUndoAddConnectionPointCommand::CXTPFlowGraphUndoAddConnectionPointCommand(CXTPFlowGraphConnectionPoint* pConnectionPoint)
: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_ADDCONNECTIONPOINT)
{
	m_pConnectionPoint = pConnectionPoint;
	m_pConnectionPoint->InternalAddRef();
}

CXTPFlowGraphUndoAddConnectionPointCommand::~CXTPFlowGraphUndoAddConnectionPointCommand()
{
	m_pConnectionPoint->InternalRelease();
}

void CXTPFlowGraphUndoAddConnectionPointCommand::Undo()
{
	m_pConnectionPoint->Remove();
}

//////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoAddConnectionCommand

CXTPFlowGraphUndoAddConnectionCommand::CXTPFlowGraphUndoAddConnectionCommand(CXTPFlowGraphConnection* pConnection)
: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_ADDCONNECTION)
{
	m_pConnection = pConnection;
	m_pConnection->InternalAddRef();
}

CXTPFlowGraphUndoAddConnectionCommand::~CXTPFlowGraphUndoAddConnectionCommand()
{
	m_pConnection->InternalRelease();
}

void CXTPFlowGraphUndoAddConnectionCommand::Undo()
{
	m_pConnection->Remove();
}


//////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoDeleteNodeCommand

CXTPFlowGraphUndoDeleteNodeCommand::CXTPFlowGraphUndoDeleteNodeCommand(CXTPFlowGraphNode* pNode)
	: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_DELETENODE)
{
	m_pNode = pNode;
	m_pNode->InternalAddRef();

	m_pPage = pNode->GetPage();
}

CXTPFlowGraphUndoDeleteNodeCommand::~CXTPFlowGraphUndoDeleteNodeCommand()
{
	m_pNode->InternalRelease();
}

void CXTPFlowGraphUndoDeleteNodeCommand::Undo()
{
	m_pNode->InternalAddRef();

	m_pPage->GetNodes()->AddNode(m_pNode);
}


//////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoDeleteConnectionPointCommand

CXTPFlowGraphUndoDeleteConnectionPointCommand::CXTPFlowGraphUndoDeleteConnectionPointCommand(CXTPFlowGraphConnectionPoint* pConnectionPoint)
: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_DELETECONNECTIONPOINT)
{
	m_pConnectionPoint = pConnectionPoint;
	m_pConnectionPoint->InternalAddRef();

	m_pNode = m_pConnectionPoint->GetNode();
	m_pNode->InternalAddRef();

	m_nIndex = m_pConnectionPoint->m_nConnectionPointIndex;
}

CXTPFlowGraphUndoDeleteConnectionPointCommand::~CXTPFlowGraphUndoDeleteConnectionPointCommand()
{
	m_pNode->InternalRelease();
	m_pConnectionPoint->InternalRelease();
}

void CXTPFlowGraphUndoDeleteConnectionPointCommand::Undo()
{
	if (m_pNode->GetPage() != NULL)
	{
		m_pNode->GetConnectionPoints()->AddConnectionPoint(m_pConnectionPoint, m_nIndex);
		m_pConnectionPoint->InternalAddRef();
	}
}


//////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoDeleteConnectionCommand

CXTPFlowGraphUndoDeleteConnectionCommand::CXTPFlowGraphUndoDeleteConnectionCommand(CXTPFlowGraphConnection* pConnection)
	: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_DELETECONNECTION)
{
	m_pConnection = pConnection;
	m_pConnection->InternalAddRef();

	m_pPage = pConnection->GetPage();

	m_pInputPoint = m_pConnection->GetInputPoint();
	if (m_pInputPoint) m_pInputPoint->InternalAddRef();

	m_pOutputPoint = m_pConnection->GetOutputPoint();
	if (m_pOutputPoint) m_pOutputPoint->InternalAddRef();
}

CXTPFlowGraphUndoDeleteConnectionCommand::~CXTPFlowGraphUndoDeleteConnectionCommand()
{
	m_pConnection->InternalRelease();

	if (m_pInputPoint) m_pInputPoint->InternalRelease();
	if (m_pOutputPoint) m_pOutputPoint->InternalRelease();
}

void CXTPFlowGraphUndoDeleteConnectionCommand::Undo()
{
	if (!(m_pInputPoint && m_pInputPoint->GetNode() && m_pInputPoint->GetNode()->GetPage() == m_pPage))
		 return;

	if (!(m_pOutputPoint && m_pOutputPoint->GetNode() && m_pOutputPoint->GetNode()->GetPage() == m_pPage))
		return;

	m_pConnection->InternalAddRef();

	m_pConnection->SetInputPoint(m_pInputPoint);
	m_pConnection->SetOutputPoint(m_pOutputPoint);

	m_pPage->GetConnections()->AddConnection(m_pConnection);
}

///////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoSetNodePositionCommand

CXTPFlowGraphUndoSetNodePositionCommand::CXTPFlowGraphUndoSetNodePositionCommand(CXTPFlowGraphNode* pNode, CPoint ptOldPosition)
: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_SETNODEPOSITION)
{
	m_ptOldPosition = ptOldPosition;

	m_pNode = pNode;
	m_pNode->InternalAddRef();
}

CXTPFlowGraphUndoSetNodePositionCommand::~CXTPFlowGraphUndoSetNodePositionCommand()
{
	m_pNode->InternalRelease();
}

void CXTPFlowGraphUndoSetNodePositionCommand::Undo()
{
	m_pNode->SetLocation(m_ptOldPosition);
}

///////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoSetNodeSizeCommand

CXTPFlowGraphUndoSetNodeSizeCommand::CXTPFlowGraphUndoSetNodeSizeCommand(CXTPFlowGraphNode* pNode, CSize szOldSize)
: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_SETNODESIZE)
{
	m_szOldSize = szOldSize;

	m_pNode = pNode;
	m_pNode->InternalAddRef();
}

CXTPFlowGraphUndoSetNodeSizeCommand::~CXTPFlowGraphUndoSetNodeSizeCommand()
{
	m_pNode->InternalRelease();
}

void CXTPFlowGraphUndoSetNodeSizeCommand::Undo()
{
	m_pNode->SetSize(m_szOldSize);
}

///////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoSetNodePositionCommand

CXTPFlowGraphUndoSetNodeCaptionCommand::CXTPFlowGraphUndoSetNodeCaptionCommand(CXTPFlowGraphElement* pNode, CString strOldCaption)
	: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_SETNODECAPTION)
{
	m_strOldCaption = strOldCaption;

	m_pNode = pNode;
	m_pNode->InternalAddRef();
}

CXTPFlowGraphUndoSetNodeCaptionCommand::~CXTPFlowGraphUndoSetNodeCaptionCommand()
{
	m_pNode->InternalRelease();
}

void CXTPFlowGraphUndoSetNodeCaptionCommand::Undo()
{
	m_pNode->SetCaption(m_strOldCaption);
}

///////////////////////////////////////////////////////////////////////////
// CXTPFlowGraphUndoSetConnectionPositionCommand

CXTPFlowGraphUndoSetConnectionPositionCommand::CXTPFlowGraphUndoSetConnectionPositionCommand(CXTPFlowGraphConnection* pConnection, CPoint ptOldPosition)
: CXTPFlowGraphUndoCommand(XTP_ID_FLOWGRAPHUNDO_SETCONNECTIONPOSITION)
{
	m_ptOldPosition = ptOldPosition;

	m_pConnection = pConnection;
	m_pConnection->InternalAddRef();
}

CXTPFlowGraphUndoSetConnectionPositionCommand::~CXTPFlowGraphUndoSetConnectionPositionCommand()
{
	m_pConnection->InternalRelease();
}

void CXTPFlowGraphUndoSetConnectionPositionCommand::Undo()
{
	m_pConnection->SetControlPoint(m_ptOldPosition);
}