// INKSDoc.cpp : implementation of the CINKSDoc class
//

#include "stdafx.h"
#include "INKS.h"
#include "INKSDoc.h"
#include "INKSView.h"
#include "MainFrm.h"
#include "MouseTracker.h"
#include "Block.h"
#include "Tag.h"
#include "Inv.h"
#include "MouseSnap.h"
#include "ZoomView.h"
#include "SpacialHashing.h"
#include "CommonObject.h"
#include "CollapseDialog.h"
#include "resource.h"
#include "ElementContainer.h"
#include "RequestNameDlg.h"
#include "XmlCreator.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CINKSDoc

IMPLEMENT_DYNCREATE(CINKSDoc, CDocument)

BEGIN_MESSAGE_MAP(CINKSDoc, CDocument)

	ON_UPDATE_COMMAND_UI(ID_SELECT, OnUpdateSelectSelect)
	ON_UPDATE_COMMAND_UI(ID_DRAWGATE, OnUpdateSelectDrawGate)
	ON_UPDATE_COMMAND_UI(ID_DRAWBLOCK, OnUpdateSelectDrawBlock)
	ON_UPDATE_COMMAND_UI(ID_DRAWWIRE, OnUpdateSelectDrawWire)
	ON_UPDATE_COMMAND_UI(ID_DRAWTAG, OnUpdateSelectDrawTag)

	ON_COMMAND(ID_SELECT, OnSelectSelect)
	ON_COMMAND(ID_DRAWGATE, OnSelectDrawGate)
	ON_COMMAND(ID_DRAWWIRE, OnSelectDrawWire)
	ON_COMMAND(ID_DRAWBLOCK, OnSelectDrawBlock)
	ON_COMMAND(ID_DRAWTAG, OnSelectDrawTag)
	ON_COMMAND(ID_DRAWINV, OnSelectDrawInv)

	ON_COMMAND(ID_SELECTIONCONTEXTMENU_DELETEITEM, &CINKSDoc::OnSelectioncontextmenuDeleteitem)
	ON_COMMAND(ID_BLOCKMENU_DELETEITEM, &CINKSDoc::OnBlockmenuDeleteitem)
	ON_COMMAND(ID_BLOCKMENU_PUSHINTOTHEBLOCK, &CINKSDoc::OnBlockmenuPushintotheblock)

	ON_COMMAND(ID_PUSHIN, OnBlockmenuPushintotheblock)
	ON_COMMAND(ID_PUSHOUT, OnBlockmenuPushoutoftheblock)
	
END_MESSAGE_MAP()


// CINKSDoc construction/destruction

CINKSDoc::CINKSDoc()
: m_pSpaceHash(NULL), m_pMouseTracker(NULL), m_nDocumentType(-1), m_pTreeItem(NULL), m_bNeedReload(false), m_pParentDoc(NULL)
{

	m_pElementCont = new CElementContainer();
	m_pSpaceHash = new CSpacialHashing(200,100);
	m_COList.RemoveAll();

	m_ProjectDoc.Reset();
	m_SchemeDoc.Reset();
	m_BlockDoc.Reset();

	m_bSelectMultiple = false;

	m_pCreateOptionsDialog = &((CMainFrame*)::AfxGetMainWnd())->m_SideBar;

	m_SnapMouse.InitGrid(2000, 1000);
}

void CINKSDoc::TryMovedConnection()
{
	POSITION pos = this->m_SOList.GetHeadPosition();
	POSITION posCO = this->m_COList.GetHeadPosition();
	POSITION posAnch = NULL;
	CCommonObject *pObj = NULL;
	CCommonObject* pObjCO = NULL;
	while(pos){
		pObj = m_SOList.GetNext(pos);
		if(pObj->m_nObjType != 4){
			posCO = m_COList.GetHeadPosition();
			while(posCO){
				pObjCO = m_COList.GetNext(posCO);
				if(pObjCO->m_nObjType == 4){ // wire 
					CPoint *point = NULL;
					for(int i=0; i<pObj->m_nIn; i++){
						posAnch = ((CWire*)pObjCO)->m_Anchors.GetHeadPosition();
						while(posAnch){
							point = &((CWire*)pObjCO)->m_Anchors.GetNext(posAnch).m_PointMain;
							if(pObj->m_Connection.m_rectIns[i].PtInRect(*point)){
								pObj->SetConnection(true, i, pObjCO);
								pObjCO->SetConnection(true, i, pObj);
							}
						}
					}
					for(int i=0; i<pObj->m_nOut; i++){
						posAnch = ((CWire*)pObjCO)->m_Anchors.GetHeadPosition();
						while(posAnch){
							point = &((CWire*)pObjCO)->m_Anchors.GetNext(posAnch).m_PointMain;
							if(pObj->m_Connection.m_rectOuts[i].PtInRect(*point)){
								pObj->SetConnection(false, i, pObjCO);
								pObjCO->SetConnection(false, i, pObj);
							}
						}
					}
				}
				else if(pObjCO->m_nObjType == 5 && pObj->m_nObjType != 5 && pObjCO != pObj){ // tag
					for(int i=0; i<pObj->m_nIn; i++){
						if(pObj->m_Connection.m_rectIns[i].PtInRect(pObjCO->m_pointTopLeft)){
							pObj->SetConnection(true, i, pObjCO);
							pObjCO->SetConnection(true, i, pObj);
						}
					}
					for(int i=0; i<pObj->m_nOut; i++){
						if(pObj->m_Connection.m_rectOuts[i].PtInRect(pObjCO->m_pointTopLeft)){
							pObj->SetConnection(false, i, pObjCO);
							pObjCO->SetConnection(false, i, pObj);
						}
					}
				}
				else if(pObj->m_nObjType == 5 && pObj != pObjCO){
					for(int i=0; i<pObjCO->m_nIn; i++){
						if(pObjCO->m_Connection.m_rectIns[i].PtInRect(pObj->m_pointTopLeft)){
							if(((CTag*)pObj)->m_nTagType == 2){
								Rect *tempRect = pObj->GetBounds();
								m_pSpaceHash->DeRegisterCObject(pObj, tempRect->GetLeft(), tempRect->GetTop(), tempRect->GetRight(), tempRect->GetBottom());
								((CTag*)pObj)->SwapTag();
								tempRect = pObj->GetBounds();
								m_pSpaceHash->RegisterCObject(pObj, tempRect->GetLeft(), tempRect->GetTop(), tempRect->GetRight(), tempRect->GetBottom());
							}

							pObj->SetConnection(true, i, pObjCO);
							pObjCO->SetConnection(true, i, pObj);
						}
					}
					for(int i=0; i<pObjCO->m_nOut; i++){
						if(pObjCO->m_Connection.m_rectOuts[i].PtInRect(pObj->m_pointTopLeft)){
							if(((CTag*)pObj)->m_nTagType == 1){
								Rect *tempRect = pObj->GetBounds();
								m_pSpaceHash->DeRegisterCObject(pObj, tempRect->GetLeft(), tempRect->GetTop(), tempRect->GetRight(), tempRect->GetBottom());
								((CTag*)pObj)->SwapTag();
								tempRect = pObj->GetBounds();
								m_pSpaceHash->RegisterCObject(pObj, tempRect->GetLeft(), tempRect->GetTop(), tempRect->GetRight(), tempRect->GetBottom());
							}

							pObj->SetConnection(false, i, pObjCO);
							pObjCO->SetConnection(false, i, pObj);
						}
					}
				}
			}
		}
		else{ // selected wire; trying to connect its head || tail to something
			bool bConnectedIn = false;
			bool bConnectedOut = false;
			int nIns = 0;
			int nOuts = 0;
			nIns = pObj->m_nIn;
			nOuts = pObj->m_nOut;
			pObj->Disconnect();

			posCO = m_COList.GetHeadPosition();
			CPoint headPoint = ((CWire*)pObj)->m_Anchors.GetHead().m_PointMain;
			CPoint tailPoint = ((CWire*)pObj)->m_Anchors.GetTail().m_PointMain;
			while(posCO){
				pObjCO = m_COList.GetNext(posCO);
				if(pObjCO->GetType() != 4){ // something we're trying to connect to is not a wire 
					nIns = pObjCO->m_nIn;
					nOuts = pObjCO->m_nOut;
					for(int i=0; i<nIns; i++){
						if(!pObjCO->m_Connection.m_pObjectIn[i]){
							if(pObjCO->m_Connection.m_rectIns[i].PtInRect(headPoint) || pObjCO->m_Connection.m_rectIns[i].PtInRect(tailPoint)){
								pObj->SetConnection(true, i, pObjCO);
								pObjCO->SetConnection(true, i, pObj);
								bConnectedIn = true;
							}
						}
					}
					for(int i=0; i<nOuts; i++){
						if(!pObjCO->m_Connection.m_pObjectOut[i]){
							if(pObjCO->m_Connection.m_rectOuts[i].PtInRect(headPoint) || pObjCO->m_Connection.m_rectOuts[i].PtInRect(tailPoint)){
								pObj->SetConnection(false, i, pObjCO);
								pObjCO->SetConnection(false, i, pObj);
								bConnectedOut = false;
							}
						}
					}
				}
				else{ // trying to connect wire to a wire
					if(pObjCO != pObj){
						CCommonObject *pTemp;
						pTemp = m_pSpaceHash->HashTestWireEdge(headPoint, pObj);
						if(pTemp && pTemp == pObjCO && pTemp->GetType() == 4){
							pTemp->SetConnection(true, pTemp->m_nIn, pObj);
							pObj->SetConnection(true, pObj->m_nIn, pTemp);
						}
						pTemp = m_pSpaceHash->HashTest(tailPoint);
						if(pTemp && pTemp == pObjCO && pTemp->GetType() == 4){
							pTemp->SetConnection(false, pTemp->m_nOut, pObj);
							pObj->SetConnection(false, pObj->m_nOut, pTemp);
						}
					}
				}
			}
		}
	}
}


CINKSDoc::~CINKSDoc()
{
	SAFE_DELETE(m_pMouseTracker);
	SAFE_DELETE(m_pElementCont);
	SAFE_DELETE(m_pSpaceHash);
}

void CINKSDoc::ProjectDoc::Reset()
{

}

void CINKSDoc::SchemeDoc::Reset()
{
	m_nGatesCount = 0;
	m_nWiresCount = 0;
	m_nBlocksCount = 0;
	m_nTagsCount = 0;
	m_nInvCount = 0;
}

void CINKSDoc::BlockDoc::Reset()
{
	m_bAppBlock = false;
	m_nGatesCount = 0;
	m_nWiresCount = 0;
	m_nBlocksCount = 0;
	m_nTagsCount = 0;
	m_nInvCount = 0;
}

BOOL CINKSDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
		return FALSE;
	InitNewDocument();
	m_COList.RemoveAll();
	return TRUE;
}
void CINKSDoc::InitNewDocument()
{

}

// CINKSDoc serialization

void CINKSDoc::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
		// TODO: add storing code here
	}
	else
	{
		// TODO: add loading code here
	}

}

void CINKSDoc::OnCloseDocument()
{
	if(theApp.m_bAppExit)
		CloseDocument(true);
	else
		CloseDocument(false);
	//this->GetAssociatedView()->GetParentFrame()->ShowWindow(SW_HIDE);
	//CDocument::OnCloseDocument();
	
}

void CINKSDoc::CloseDocument(bool bClose)
{
	if(bClose){
		CDocument::OnCloseDocument();
	}
	else{
		if(theApp && theApp.m_pOpenedProjectDoc){
			GetAssociatedView()->GetParentFrame()->ShowWindow(SW_HIDE);
			theApp.m_pMainFrame->m_SideBar.ShowInfo((CINKSDoc*)theApp.m_pOpenedProjectDoc);
		}
	}
}

BOOL CINKSDoc::OnCloseProject()
{
	BOOL bClose = SaveModified();
	if(bClose){
		theApp.m_pMainFrame->m_SideBar.ShowInfo(NULL);
	}
	return bClose;
}

BOOL CINKSDoc::SaveModified()
{
	CString strHead = _T("?Save changes to ");
	if(IsModified()){
		strHead.Append(GetTitle());
		strHead.Append(_T("?"));
		int nRespond = MessageBox(AfxGetMainWnd()->m_hWnd, strHead, _T("Save?"), MB_ICONINFORMATION | MB_YESNOCANCEL);
		if(nRespond == IDYES){
			if(OnSaveDocument(m_strPathName))
				return TRUE;
			return FALSE;
		}
		else if(nRespond == IDNO){
			SetModifiedFlag(FALSE);
			return TRUE;
		}
		return FALSE;
	}
	return TRUE;
}

BOOL CINKSDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
	CString strPath = lpszPathName;
	if(m_nDocumentType == 0){ // project document
		ofstream outfile((CStringA)strPath);
		CXmlCreator creator;
		outfile << creator.WriteProject(m_ProjectDoc.m_strProjectName, this).str();
		outfile.flush();
		outfile.close();
	}
	else if(m_nDocumentType == 1){ // scheme document
		ofstream outfile(lpszPathName);
		CXmlCreator creator;
		outfile << creator.WriteScheme(m_SchemeDoc.m_strSchemeName, &m_COList).str();
		outfile.flush();
		outfile.close();
	}
	else if(m_nDocumentType == 2){ // block document
		CXmlCreator creator;
		bool bChangeParent = false;
		if(m_BlockDoc.m_bAppBlock){
			MessageBox(AfxGetMainWnd()->m_hWnd, _T("Cannot edit application specific blocks!\nSaving into project blocks"), _T("Information"), MB_ICONINFORMATION);
			CString strPathp = theApp.m_pOpenedProjectDoc->m_ProjectDoc.m_strProjectPath;
			strPathp.Append(_T("Content\\Blocks\\"));
			strPathp.Append(m_BlockDoc.m_strBlockName);
			strPathp.Append(_T(".xml"));
			strPath = strPathp;
			bChangeParent = true;
			m_BlockDoc.m_bAppBlock = false;
		}
		ofstream outfile((CStringA)strPath);
		// when closing (alt+f4) MUST close child documents first!!!
		CCommonObject *pObb = m_pParentDoc->GetCommonObject(m_BlockDoc.m_strBlockName);
		CBlock *pBlock = (CBlock*)pObb;
		outfile << creator.WriteBlock(m_BlockDoc.m_strBlockName, &m_COList, (CBlock*)m_pParentDoc->GetCommonObject(m_BlockDoc.m_strBlockName)).str();
		outfile.flush();
		outfile.close();
		if(bChangeParent){
			if(m_pParentDoc){
				CCommonObject *pObj = m_pParentDoc->GetCommonObject(m_BlockDoc.m_strBlockName);
				m_pParentDoc->m_bNeedReload = true;
				pObj->m_strOrigin = m_BlockDoc.m_strBlockName;
				pObj->m_bFileInProject = false;
				m_BlockDoc.m_bAppBlock = false;
				m_pTreeItem->m_bFileInProject = false;
				m_pParentDoc->SetModifiedFlag();
			}
		}
	}
	theApp.m_pMainFrame->m_TreeBar.SetModified(this->m_pTreeItem, false);
	SetModifiedFlag(FALSE);
	return TRUE;        // success
}


BOOL CINKSDoc::OnNewProject(LPCTSTR lpszPathName, LPCTSTR lpszProjectName)
{
	m_pParentDoc = NULL;
	m_ProjectDoc.m_strProjectPath = lpszPathName;
	m_ProjectDoc.m_strProjectName = lpszProjectName;

	m_nDocumentType = 0; // project block
	CString strNewProject = lpszPathName;
	strNewProject.Append(m_ProjectDoc.m_strProjectName);
	strNewProject.Append(_T(".xml"));
	SetModifiedFlag();
	return TRUE;
}

BOOL CINKSDoc::OnOpenProject(LPCTSTR lpszPathName)
{
	CString strPathName = lpszPathName;

	if(m_pElementCont->OpenProject(lpszPathName)){
		CMainFrame *pMainFrm = (CMainFrame*)AfxGetMainWnd();
		m_ProjectDoc.m_strProjectPath = m_pElementCont->m_strFileToParsePath;
		m_ProjectDoc.m_strProjectName = m_pElementCont->m_strDisplayName;
		pMainFrm->m_TreeBar.m_strProjectPath = m_pElementCont->m_strFileToParsePath;
		m_pTreeItem = pMainFrm->m_TreeBar.AddTreeItem(m_pElementCont->m_strDisplayName, m_pElementCont->m_strDisplayName, NULL, 0, true, _T(""));
		if(m_pTreeItem){
			m_pTreeItem->SetDoc(this);

			SetTitle(m_pElementCont->m_strDisplayName);
			SetPathName(lpszPathName);

			m_pElementCont->ClearParsedContent();
			m_nDocumentType = 0;
			return TRUE;
		}
		else
			return FALSE;
	}
	else{ //some sort of msg box...project opening didn't go well...
		return FALSE;
	}
}


// CINKSDoc diagnostics

#ifdef _DEBUG
void CINKSDoc::AssertValid() const
{
	CDocument::AssertValid();
}

void CINKSDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG


// CINKSDoc commands

BOOL CINKSDoc::OnOpenFile(CString strFile, int nType, bool bInProject)
{
	BOOL bRet = TRUE;
	m_nDocumentType = nType;
	// open scheme or block
	char *pBuffer = new char[MAX_PATH];
	CString strFileName = strFile;
	wcstombs(pBuffer, strFileName.GetBuffer(), strFileName.GetLength()+1);
	strFileName.ReleaseBuffer();
	TiXmlDocument docum(pBuffer);
	docum.LoadFile();
	SAFE_DELETE_ARRAY(pBuffer);
	CList<CString*, CString*> slInConnenctions;
	CList<CString*, CString*> slOutConnenctions;
	CString *strInConnections;
	CString *strOutConnections;
	TiXmlElement* mainRoot = docum.FirstChildElement();
	TiXmlElement* root = NULL;
	if(mainRoot){
		root = mainRoot->FirstChildElement("CONTENT");
	}

	if(root){
		CString strTitle;
		TiXmlElement* pName = root->FirstChildElement("NAME");
		// Type of GElement
		CString strOrigin, strName, strType;
		TiXmlElement* elem = NULL;
		elem = root->FirstChildElement("ELEM");
		int nGatesCount = 0;
		int nBlocksCount = 0;
		int nTagsCount = 0;
		int nWiresCount = 0;
		int nInvCount = 0;
		while(elem){
			strOrigin.Empty();
			strName.Empty();
			strType.Empty();
			int xPos;
			int yPos;
			int nType;
			int nbInProject;
			TiXmlElement* temp = elem->FirstChildElement("NAME");
			if(temp){
				strName = (CString)temp->GetText();
			}
			elem->Attribute("type", &nType);
			CCommonObject *pObj = NULL;
			if(nType == 2){			// block
				nBlocksCount++;
			}
			else if(nType == 3){	// gate
				nGatesCount++;
			}
			else if(nType == 4){	// wire
				nWiresCount++;
				/////////////////////////////////////////////////////////
				// generate new wire!
				/////////////////////////////////////////////////////////
			}
			else if(nType == 5){	// tag
				nTagsCount++;
			}
			else if(nType == 6){	// invertor
				nInvCount++;;
			}
				temp = elem->FirstChildElement("ORIGIN");
				if(temp){
					strOrigin = (CString)temp->GetText();
					temp->Attribute("inProject", &nbInProject);
					// parse file if needed!
					if(nbInProject){
						if(nType == 4){	// read wire anchors
							int nIn=0;
							int nOut=0;
							TiXmlElement *anchors = elem->FirstChildElement("ANCHORS");
							if(anchors){
								AnchorList anchorsList;
								CPoint tempPoint;
								anchors->Attribute("nIn", &nIn);
								anchors->Attribute("nOut", &nOut);
								TiXmlElement *point = anchors->FirstChildElement();
								int nXpos = 0;
								int nYpos = 0;
								while(point){
									point->Attribute("x", &nXpos);
									point->Attribute("y", &nYpos);
									tempPoint.SetPoint(nXpos, nYpos);
									anchorsList.AddTail(tempPoint);
									point = point->NextSiblingElement();
								}
								pObj = new CWire(nIn, nOut, anchorsList, NULL, 0, NULL, 0, strName);
							}
							
						}
						else{
							// if this element should be defined within project
							if(theApp.m_pOpenedProjectDoc->m_pElementCont->ContainsElement(strOrigin)){
								// if it is already parsed, just clone it
								pObj = theApp.m_pOpenedProjectDoc->m_pElementCont->GetCommonObject(strOrigin);
							}
							else{
								// otherwise find and parse it
								if(theApp.m_pOpenedProjectDoc->m_pElementCont->OpenProjectSpecific(strOrigin, theApp.m_pOpenedProjectDoc->m_ProjectDoc.m_strProjectPath)){
									pObj = theApp.m_pOpenedProjectDoc->m_pElementCont->GetCommonObject(strOrigin);
								}
								else{
									CString strMsg = _T("Couldn't find -project- specific element (");
									strMsg.Append(strName);
									strMsg.Append(_T(")!"));
									MessageBox(AfxGetMainWnd()->m_hWnd, strMsg, _T("Error"), MB_ICONERROR);
									bRet = FALSE;
								}
							}
						}
					}
					else{
						// if this element should be defined within app
						if(theApp.m_pElementCont->ContainsElement(strOrigin)){
							// if itis already parsed, just clone it
							pObj = theApp.m_pElementCont->GetCommonObject(strOrigin);
						}
						else{
							// otherwise find and parse it
							if(theApp.m_pElementCont->OpenAppSpecific(strOrigin)){
								pObj = theApp.m_pElementCont->GetCommonObject(strOrigin);
							}
							else{
								CString strMsg = _T("Couldn't find -application- specific element (");
								strMsg.Append(strName);
								strMsg.Append(_T(")!"));
								MessageBox(AfxGetMainWnd()->m_hWnd, strMsg, _T("Error"), MB_ICONERROR);
								bRet = FALSE;
							}
						}
					}
				}
			TiXmlElement *position = elem->FirstChildElement("POSITION");
			if(position){
				position->Attribute("tlx1", &xPos);
				position->Attribute("tly1", &yPos);
			}
			
			if(pObj){
				strInConnections = new CString[pObj->m_nIn];
				strOutConnections = new CString[pObj->m_nOut];
				TiXmlElement *connections = elem->FirstChildElement("CONNECTIONS");
				if(connections){
					TiXmlElement *inConn = connections->FirstChildElement("INCONNECTIONS");
					int nPos = 0;
					int nCount = 0;
					CString strPos;
					if(inConn){
						inConn->Attribute("nCount", &nCount);
						TiXmlElement *tempIn = inConn->FirstChildElement();
						CString strName;
						while(tempIn){
							strPos.Empty();
							strPos += tempIn->GetText();
							nPos = atoi((CStringA)strPos);
							strName.Empty();
							strName += tempIn->Attribute("Name");
							strInConnections[nPos] = strName;
							tempIn = tempIn->NextSiblingElement();
						}
					}
					TiXmlElement *outConn = connections->FirstChildElement("OUTCONNECTIONS");
					if(outConn){
						outConn->Attribute("nCount", &nCount);
						TiXmlElement *tempOut = outConn->FirstChildElement();
						CString strName;
						while(tempOut){
							strPos.Empty();
							strPos += tempOut->GetText();
							nPos = atoi((CStringA)strPos);
							strName.Empty();
							strName += tempOut->Attribute("Name");
							strOutConnections[nPos] = strName;
							tempOut = tempOut->NextSiblingElement();
						}
					}
				}

				CCommonObject *pObjNew = pObj->Clone(CPoint(xPos, yPos), strName, 0);
				pObjNew->m_bFileInProject = nbInProject;
				m_COList.AddTail(pObjNew);
				Rect *bounds;
				bounds = pObjNew->GetBounds();
				int l = bounds->GetLeft();
				int r = bounds->GetRight();
				if(pObjNew->GetType() == 4){
					
					m_pSpaceHash->RegisterWire(pObjNew, ((CWire*)pObjNew)->m_Points, (int)((CWire*)pObjNew)->m_Anchors.GetSize());
				}
				else{
					m_pSpaceHash->RegisterCObject(pObjNew, bounds->GetLeft(), bounds->GetTop(), bounds->GetRight(), bounds->GetBottom());
				}
				slInConnenctions.AddTail(strInConnections);
				slOutConnenctions.AddTail(strOutConnections);
			}
			elem = elem->NextSiblingElement("ELEM");
		}
		// now connect elements
		ConnectElements(&slInConnenctions, &slOutConnenctions);
		// and delete this horrible horrible thing up here
		POSITION pos = slInConnenctions.GetHeadPosition();
		CString *pDelete = NULL;
		while(pos){
			pDelete = slInConnenctions.GetNext(pos);
			SAFE_DELETE_ARRAY(pDelete);
		}
		pos = slOutConnenctions.GetHeadPosition();
		pDelete = NULL;
		while(pos){
			pDelete = slOutConnenctions.GetNext(pos);
			SAFE_DELETE_ARRAY(pDelete);
		}
		slInConnenctions.RemoveAll();
		slOutConnenctions.RemoveAll();

		SetTagsCount(nTagsCount);
		SetWiresCount(nWiresCount);
		SetBlocksCount(nBlocksCount);
		SetGatesCount(nGatesCount);
	}
	return bRet;
}

void CINKSDoc::ConnectElements(CList<CString*, CString*> *inList, CList<CString*, CString*> *outList)
{
	INT_PTR nCOSize = m_COList.GetSize();
	INT_PTR nInSize = inList->GetSize();
	INT_PTR nOutSize = outList->GetSize();
	if(nCOSize != nInSize || nCOSize != nOutSize){
		MessageBox(AfxGetMainWnd()->m_hWnd, _T("Couldn't connect elements!"), _T("Error"), MB_ICONERROR);
		return;
	}
	POSITION COPos = m_COList.GetHeadPosition();
	CCommonObject *pObj = NULL;
	POSITION inPos = inList->GetHeadPosition();
	POSITION outPos = outList->GetHeadPosition();
	CString *strLookFor;
	CCommonObject *pInOutObj = NULL;
	while(COPos){
		pObj = m_COList.GetNext(COPos);
		if(pObj){
			strLookFor = inList->GetNext(inPos);
			for(int i=0; i<pObj->m_nIn; i++){
				pObj->SetConnection(true, i, GetCommonObject(strLookFor[i]));
			}
			strLookFor = outList->GetNext(outPos);
			for(int i=0; i<pObj->m_nOut; i++){
				pObj->SetConnection(false, i, GetCommonObject(strLookFor[i]));
			}
		}
	}
}

CCommonObject* CINKSDoc::GetCommonObject(CString strName)
{
	if(!strName.GetLength())
		return NULL;
	POSITION pos = m_COList.GetHeadPosition();
	CCommonObject *pObj = NULL;
	while(pos){
		pObj = m_COList.GetNext(pos);
		if(pObj->m_strUniqueName == strName)
			return pObj;
	}
	return NULL;
}

bool CINKSDoc::SOListContains(CCommonObject *pObj)
{
	POSITION pos = this->m_SOList.GetHeadPosition();
	while(pos){
		if(pObj == m_SOList.GetNext(pos))
			return true;
	}
	return false;
}

bool CINKSDoc::RemoveFromCOList(CCommonObject *pObj)
{
	POSITION pos = this->m_COList.GetHeadPosition();
	POSITION posPrev;
	while(pos){
		posPrev = pos;
		if(m_COList.GetNext(pos) == pObj){
			pObj->Disconnect();
			m_COList.RemoveAt(posPrev);
			return true;
		}
	}
	return false;
}

bool CINKSDoc::AddNewCommonObject(CPoint point)
{
	m_SnapMouse.SnapCursor(point);
	CString strOrigin;
	int nSource = theApp.m_pMainFrame->m_SideBar.m_nSelectedSource;
	CCommonObject *pObj = CreateNewCommonObject(point, strOrigin, nSource);

	if(!pObj)
		return false;
	Rect *auxRect = pObj->GetBounds();
	bool hashArea = m_pSpaceHash->HashAreaTest(CPoint(auxRect->X,auxRect->Y),auxRect->Width,auxRect->Height);
	if(pObj != NULL && hashArea){
		Rect *bounds;
		bounds = pObj->GetBounds();
		m_pSpaceHash->RegisterCObject(pObj, bounds->GetLeft(), bounds->GetTop(), bounds->GetRight(), bounds->GetBottom());
		m_COList.AddTail(pObj);

		// register in TreeBar
		//theApp.m_pMainFrame->m_TreeBar.RegisterNewCommonObject(pObj->m_strUniqueName, (CString)pObj->m_strOrigin, GetTitle(), pObj->m_nObjType, bFileInProject, this);
		theApp.m_pMainFrame->m_TreeBar.RegisterNewCommonObject(pObj, this);
		theApp.m_pMainFrame->m_SideBar.ObjectInserted();

		this->SetModifiedFlag();
		return true;
	}
	else
		SAFE_DELETE(pObj);
	return false;
}

CCommonObject* CINKSDoc::CreateNewCommonObject(CPoint point, CString &strOrigin, int nSource)
{
	bool bFoundInProject;
	CCommonObject *pNewObject = NULL;
	CString name;
	int nRotation = theApp.m_pMainFrame->m_SideBar.GetObjRotation();
	switch(m_pMouseTracker->m_CreateMode){
		case CMouseTracker::CreateGate:
			{
				CString strName = m_pCreateOptionsDialog->GetGateName();
				if(!strName.IsEmpty()){
					strOrigin = strName;
					name.Format(_T("Gate%d"), GetNextGateID());
					while(GetCommonObject(name)){
						name.Format(_T("Gate%d"), GetNextBlockID());
					}
					CCommonObject *pObj = NULL;
					if(nSource == 2 && theApp.m_pOpenedProjectDoc->m_pElementCont->ContainsElement(strName)){
						pObj = theApp.m_pOpenedProjectDoc->m_pElementCont->GetCommonObject(strName);
						bFoundInProject = true;
					}
					else if(nSource == 1 && theApp.m_pElementCont->ContainsElement(strName)){
						pObj = theApp.m_pElementCont->GetCommonObject(strName);
						bFoundInProject = false;
					}
					if(pObj){
						pNewObject = pObj->Clone(point, name, nRotation);
						pNewObject->m_bFileInProject = bFoundInProject;
					}
					else{
						MessageBox(AfxGetMainWnd()->m_hWnd, _T("Couldn't clone selected object"), _T("error"), 0);
					}
					return pNewObject;
				}
			}

			break;
		case CMouseTracker::CreateBlock:
			{
				CString strName = m_pCreateOptionsDialog->GetGateName();
				if(!strName.IsEmpty()){
					strOrigin = strName;
					name.Format(_T("Block%d"), GetNextBlockID());
					while(GetCommonObject(name)){
						name.Format(_T("Block%d"), GetNextBlockID());
					}
					CCommonObject *pObj = NULL;
					if(theApp.m_pOpenedProjectDoc->m_pElementCont->ContainsElement(strName)){
						pObj = theApp.m_pOpenedProjectDoc->m_pElementCont->GetCommonObject(strName);
						bFoundInProject = true;
					}
					else if(theApp.m_pElementCont->ContainsElement(strName)){
						pObj = theApp.m_pElementCont->GetCommonObject(strName);
						bFoundInProject = false;
					}
					if(pObj){
						pNewObject = pObj->Clone(point, name, nRotation);
						pNewObject->m_bFileInProject = bFoundInProject;
					}
					else{
						MessageBox(AfxGetMainWnd()->m_hWnd, _T("couldn't clone selected object"), _T("error"), 0);
					}
					return pNewObject;
				} 
			}
			break;
		case CMouseTracker::CreateWire:
			{
				name.Format(_T("Wire%d"), GetNextWireID());
				while(GetCommonObject(name)){
					name.Format(_T("Wire%d"), GetNextWireID());
				}
				pNewObject = new CWire(1, 1, m_pMouseTracker->Anchors, m_pMouseTracker->m_gAddWireObject[0], m_pMouseTracker->m_nWireConnection[0], m_pMouseTracker->m_gAddWireObject[1], m_pMouseTracker->m_nWireConnection[1], name);
				bFoundInProject = true;
				pNewObject->m_bFileInProject = true;
				strOrigin = name;
				return pNewObject;
			}
			break;
		case CMouseTracker::CreateTag:
			{
				CRequestNameDlg dlg(_T("Please insert new tag name"), _T("New Tag"));
				if(dlg.DoModal() == IDOK){
					int nRot = 0;
					if(m_pMouseTracker->m_nWireConnection[0] < 0)
						nRot = 180;
					GetNextTagID();
					pNewObject = new CTag(point, dlg.GetName(), m_pMouseTracker->m_nWireConnection[0] > 0);
					pNewObject->SetRotation(nRot);
					pNewObject->SetConnection(true, 1, m_pMouseTracker->m_gAddWireObject[0]);
					pNewObject->m_bFileInProject = false;
					m_pMouseTracker->m_gAddWireObject[0]->SetConnection(m_pMouseTracker->m_nWireConnection[0] > 0, abs(m_pMouseTracker->m_nWireConnection[0])-1, pNewObject);
					return pNewObject;
				}
			}
			break;
		case CMouseTracker::CreateInv:
			{
				name.Format(_T("Inv%d"), GetNextWireID());
				while(GetCommonObject(name)){
					name.Format(_T("Inv%d"), GetNextWireID());
				}
				pNewObject = new CInv(point, name);
				pNewObject->m_bFileInProject = false;
				return pNewObject;
			}
			break;
		default:
			TRACE(_T("Trying to create bad object!\n"));
	}
	return NULL;
}

void CINKSDoc::SetGatesCount(int nCount)
{
	switch(m_nDocumentType){
		case 1:
			m_SchemeDoc.m_nGatesCount = nCount;
			return;
		case 2:
			++m_BlockDoc.m_nGatesCount = nCount;
			return;
		default:
			TRACE(_T("GetNextGateID default m_nDocumentType\n"));
			return;
	}
}

void CINKSDoc::SetBlocksCount(int nCount)
{
	switch(m_nDocumentType){
		case 1:
			m_SchemeDoc.m_nBlocksCount = nCount;
			return;
		case 2:
			++m_BlockDoc.m_nBlocksCount = nCount;
			return;
		default:
			TRACE(_T("GetNextGateID default m_nDocumentType\n"));
			return;
	}
}

void CINKSDoc::SetWiresCount(int nCount)
{
	switch(m_nDocumentType){
		case 1:
			m_SchemeDoc.m_nWiresCount = nCount;
			return;
		case 2:
			++m_BlockDoc.m_nWiresCount = nCount;
			return;
		default:
			TRACE(_T("GetNextGateID default m_nDocumentType\n"));
			return;
	}
}

void CINKSDoc::SetTagsCount(int nCount)
{
	switch(m_nDocumentType){
		case 1:
			m_SchemeDoc.m_nTagsCount = nCount;
			return;
		case 2:
			++m_BlockDoc.m_nTagsCount = nCount;
			return;
		default:
			TRACE(_T("GetNextGateID default m_nDocumentType\n"));
			return;
	}
}

int CINKSDoc::GetNextGateID()
{
	switch(m_nDocumentType){
		case 1:
			return ++m_SchemeDoc.m_nGatesCount;
		case 2:
			return ++m_BlockDoc.m_nGatesCount;
		default:
			TRACE(_T("GetNextGateID default m_nDocumentType\n"));
			return 0;
	}
}

int CINKSDoc::GetNextBlockID()
{
	switch(m_nDocumentType){
		case 1:
			return ++m_SchemeDoc.m_nBlocksCount;
		case 2:
			return ++m_BlockDoc.m_nBlocksCount;
		default:
			TRACE(_T("GetNextGateID default m_nDocumentType\n"));
			return 0;
	}
}

int CINKSDoc::GetNextWireID()
{
	switch(m_nDocumentType){
		case 1:
			return ++m_SchemeDoc.m_nWiresCount;
		case 2:
			return ++m_BlockDoc.m_nWiresCount;
		default:
			TRACE(_T("GetNextGateID default m_nDocumentType\n"));
			return 0;
	}
}

int CINKSDoc::GetNextTagID()
{
	switch(m_nDocumentType){
		case 1:
			return ++m_SchemeDoc.m_nTagsCount;
		case 2:
			return ++m_BlockDoc.m_nTagsCount;
		default:
			TRACE(_T("GetNextGateID default m_nDocumentType\n"));
			return 0;
	}
}

int CINKSDoc::GetNextInvID()
{
	switch(m_nDocumentType){
		case 1:
			return ++m_SchemeDoc.m_nInvCount;
		case 2:
			return ++m_BlockDoc.m_nInvCount;
		default:
			TRACE(_T("GetWireGateID default m_nDocumentType\n"));
			return 0;
	}
}

bool CINKSDoc::AddNewCommonObjectWire(CPoint point)
{
	// unnecessary for wire
	CString s;

	CCommonObject *pObj = CreateNewCommonObject(point, s, 0);
	if(pObj != NULL){
		int nPointCount = (int)pObj->m_gPath->GetPointCount();

		Point *pnts = new Point[nPointCount];
		pObj->m_gPath->GetPathPoints(pnts, nPointCount);

		m_pSpaceHash->RegisterWire(pObj, pnts, nPointCount);
		m_COList.AddTail(pObj);
		SAFE_DELETE_ARRAY(pnts);
		return true;
	}
	return false;
}

void CINKSDoc::DeleteSelection()
{
	INT_PTR nSize = m_SOList.GetSize();
	POSITION pos = m_SOList.GetHeadPosition();
	POSITION posPrev;
	CCommonObject *pObj = NULL;
	while(pos){
		posPrev = pos;
		pObj = m_SOList.GetNext(pos);
		if(RemoveFromCOList(pObj)){
			if(theApp.m_pMainFrame->m_TreeBar.CanDeregisterCommonObject(pObj->m_strUniqueName, this)){
				theApp.m_pMainFrame->m_TreeBar.DeRegisterCommonObject(pObj->m_strUniqueName, this);
			}
			else{
				CString msg = _T("Couldn't remove element ");
				msg.Append(pObj->m_strUniqueName);
				msg.Append(_T(" from treeBar!"));
				MessageBox(AfxGetMainWnd()->m_hWnd, msg, _T("Error"), 0);
				return;
			}
			m_SOList.RemoveAt(posPrev);
			m_pSpaceHash->DeRegisterCObject(pObj, pObj->m_rectBounding.GetLeft(), pObj->m_rectBounding.GetTop(), pObj->m_rectBounding.GetRight(), pObj->m_rectBounding.GetBottom());
			SAFE_DELETE(pObj);
			SetModifiedFlag();
		}
		else{
			CString msg = _T("Couldn't remove element ");
			msg.Append(pObj->m_strUniqueName);
			msg.Append(_T(" from object list!"));
			MessageBox(AfxGetMainWnd()->m_hWnd, msg, _T("Error"), 0);
			return;
		}
	}
	theApp.m_pMainFrame->m_SideBar.ShowInfo(this);
	UpdateAllViews(NULL);
}

void CINKSDoc::OnUpdateSelectSelect(CCmdUI *pCmdUI)
{
	for(int i=0; i<sizeof(nButtonIDs)/sizeof(nButtonIDs[0]); i++){
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, false);
	}
	if(m_pMouseTracker->m_Mode == CMouseTracker::ModeSelect && m_pMouseTracker->m_CreateMode == CMouseTracker::NoCreate)
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, true);
}

void CINKSDoc::OnUpdateSelectDrawGate(CCmdUI *pCmdUI)
{
	for(int i=0; i<sizeof(nButtonIDs)/sizeof(nButtonIDs[0]); i++){
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, false);
	}
	if(m_pMouseTracker->m_Mode == CMouseTracker::ModeCreate && m_pMouseTracker->m_CreateMode == CMouseTracker::CreateGate)
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, true);
}

void CINKSDoc::OnUpdateSelectDrawBlock(CCmdUI *pCmdUI)
{
	for(int i=0; i<sizeof(nButtonIDs)/sizeof(nButtonIDs[0]); i++){
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, false);
	}
	if(m_pMouseTracker->m_Mode == CMouseTracker::ModeCreate && m_pMouseTracker->m_CreateMode == CMouseTracker::CreateBlock)
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, true);
}

void CINKSDoc::OnUpdateSelectDrawWire(CCmdUI *pCmdUI)
{
	for(int i=0; i<sizeof(nButtonIDs)/sizeof(nButtonIDs[0]); i++){
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, false);
	}
	if(m_pMouseTracker->m_Mode == CMouseTracker::ModeCreate && m_pMouseTracker->m_CreateMode == CMouseTracker::CreateWire)
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, true);
}

void CINKSDoc::OnUpdateSelectDrawTag(CCmdUI *pCmdUI)
{
	for(int i=0; i<sizeof(nButtonIDs)/sizeof(nButtonIDs[0]); i++){
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, false);
	}
	if(m_pMouseTracker->m_Mode == CMouseTracker::ModeCreate && m_pMouseTracker->m_CreateMode == CMouseTracker::CreateTag)
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, true);
}

void CINKSDoc::OnUpdateSelectDrawInv(CCmdUI *pCmdUI)
{
	for(int i=0; i<sizeof(nButtonIDs)/sizeof(nButtonIDs[0]); i++){
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, false);
	}
	if(m_pMouseTracker->m_Mode == CMouseTracker::ModeCreate && m_pMouseTracker->m_CreateMode == CMouseTracker::CreateInv)
		pCmdUI->m_pOther->SendMessage(TB_CHECKBUTTON, pCmdUI->m_nID, true);
}

void CINKSDoc::OnSelectSelect()
{
	theApp.m_pMainFrame->UpdateCreateBar(CMouseTracker::ModeSelect, CMouseTracker::NoCreate);
	m_pMouseTracker->Reset();
	m_pMouseTracker->m_Mode = m_pMouseTracker->ModeSelect;
	m_pMouseTracker->m_CreateMode = m_pMouseTracker->NoCreate;
}

void CINKSDoc::OnSelectDrawGate()
{
	theApp.m_pMainFrame->UpdateCreateBar(CMouseTracker::ModeCreate, CMouseTracker::CreateGate);
	m_pMouseTracker->Reset();
	m_pMouseTracker->m_Mode = m_pMouseTracker->ModeCreate;
	m_pMouseTracker->m_CreateMode = m_pMouseTracker->CreateGate;
}

void CINKSDoc::OnSelectDrawWire()
{
	theApp.m_pMainFrame->UpdateCreateBar(CMouseTracker::ModeCreate, CMouseTracker::CreateWire);
	m_pMouseTracker->Reset();
	m_pMouseTracker->m_Mode = m_pMouseTracker->ModeCreate;
	m_pMouseTracker->m_CreateMode = m_pMouseTracker->CreateWire;	
}

void CINKSDoc::OnSelectDrawBlock()
{
	theApp.m_pMainFrame->UpdateCreateBar(CMouseTracker::ModeCreate, CMouseTracker::CreateBlock);
	m_pMouseTracker->Reset();
	m_pMouseTracker->m_Mode = m_pMouseTracker->ModeCreate;
	m_pMouseTracker->m_CreateMode = m_pMouseTracker->CreateBlock;

}

void CINKSDoc::OnSelectDrawTag()
{
	theApp.m_pMainFrame->UpdateCreateBar(CMouseTracker::ModeCreate, CMouseTracker::CreateTag);
	m_pMouseTracker->Reset();
	m_pMouseTracker->m_Mode = m_pMouseTracker->ModeCreate;
	m_pMouseTracker->m_CreateMode = m_pMouseTracker->CreateTag;

	CINKSView *pView = GetAssociatedView();
	pView->TrackTag();
}

void CINKSDoc::OnSelectDrawInv()
{
	theApp.m_pMainFrame->UpdateCreateBar(CMouseTracker::ModeCreate, CMouseTracker::CreateInv);
	m_pMouseTracker->Reset();
	m_pMouseTracker->m_Mode = m_pMouseTracker->ModeCreate;
	m_pMouseTracker->m_CreateMode = m_pMouseTracker->CreateInv;
}

CINKSView* CINKSDoc::GetAssociatedView()
{
	POSITION pos = GetFirstViewPosition();
	if(pos){
		CView* pView = GetNextView(pos);
		if(pView->IsKindOf(RUNTIME_CLASS(CINKSView)))
			return (CINKSView*)pView;
	}
	return NULL;
}

void CINKSDoc::OnSelectioncontextmenuDeleteitem()
{
	DeleteSelection();
}

void CINKSDoc::OnBlockmenuDeleteitem()
{
	DeleteSelection();
}

void CINKSDoc::OnBlockmenuPushintotheblock()
{
	INT_PTR nSize = m_SOList.GetSize();
	if(nSize == 0 || nSize > 1){
		return;
	}
	CCommonObject *pObj = m_SOList.GetHead();
	if(pObj->GetType() != 2){ // !scheme
		return;
	}
	theApp.m_pMainFrame->m_TreeBar.PushIntoTheBlock(pObj->m_strUniqueName, GetTitle(), this);
}

void CINKSDoc::OnBlockmenuPushoutoftheblock()
{
	if(m_nDocumentType == 2)
		theApp.m_pMainFrame->m_TreeBar.PushOutOfTheBlock(GetTitle());
}

void CINKSDoc::UpdateObjecInfo()
{
	theApp.m_pMainFrame->m_SideBar.ShowInfo(this);
}