// INKSView.cpp : implementation of the CINKSView class
//

#include "stdafx.h"
#include "INKS.h"
#include "INKSDoc.h"
#include "INKSView.h"
#include "MainFrm.h"

#include "Gate.h"
#include "Block.h"
#include "Wire.h"
#include "ChildFrm.h"
#include "CommonObject.h"
#include "SpacialHashing.h"

#include "PDFCreator.h"
#include "PSCreator.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CINKSView
IMPLEMENT_DYNCREATE(CINKSView, CView)

BEGIN_MESSAGE_MAP(CINKSView, CZoomView)
	ON_WM_CREATE()
	ON_WM_SETCURSOR()
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_RBUTTONDOWN()
	//ON_WM_PAINT()
	ON_WM_MOUSEMOVE()
	ON_WM_MOUSEWHEEL()
	
	ON_COMMAND(ID_ZOOM_IN, OnSelectZoomIn)
	ON_COMMAND(ID_ZOOM_OUT, OnSelectZoomOut)
	ON_COMMAND(ID_ZOOM_TO_FIT, OnSelectZoomToFit)
	ON_COMMAND(ID_ZOOM_HAND, OnSelectZoomHand)

	//ON_WM_ERASEBKGND()
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
	ON_COMMAND(ID_EXPORT_EMF, &CINKSView::OnExportEmf)
	ON_COMMAND(ID_EXPORT_PDF, &CINKSView::OnExportPdf)
	ON_COMMAND(ID_EXPORT_PS, &CINKSView::OnExportPs)
END_MESSAGE_MAP()

// CINKSView construction/destruction

CINKSView::CINKSView()
	//: CZoomView(this, NULL)
	: CZoomView()
{
	EnableActiveAccessibility();
	CSize viewSize = GetTotalSize();
}

CINKSView::~CINKSView()
{
	//SAFE_DELETE(m_COListIterator);
	//SAFE_DELETE(GetDocument()->m_COListIterator);
	//SAFE_DELETE(GetDocument()->m_SOListIterator);
}

int CINKSView::OnCreate(LPCREATESTRUCT lpcs)
{
	return CZoomView::OnCreate(lpcs);
}

BOOL CINKSView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	HINSTANCE hInst = ::AfxFindResourceHandle(MAKEINTRESOURCE(IDC_DEFAULTCURSOR), RT_GROUP_CURSOR);
	HCURSOR cursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_DEFAULTCURSOR));
	cs.lpszClass = AfxRegisterWndClass(0, cursor, GetSysColorBrush(COLOR_WINDOW), NULL);

	return CZoomView::PreCreateWindow(cs);
}

BOOL CINKSView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// default preparation
	return DoPreparePrinting(pInfo);
}

void CINKSView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add extra initialization before printing
}

void CINKSView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add cleanup after printing
}


// CINKSView diagnostics

#ifdef _DEBUG
void CINKSView::AssertValid() const
{
	CView::AssertValid();
}

void CINKSView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CINKSDoc* CINKSView::GetDocument() const // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CINKSDoc)));
	return (CINKSDoc*)m_pDocument;
}
#endif //_DEBUG


void CINKSView::OnSaveDocument(CStringA lpszPathName)
{

}

// CDrawView message handlers
void CINKSView::OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint)
{
	Invalidate();
	CClientDC dc(this);
	SetMapMode(&dc);
	//OnPrepareDC(&dc);
	OnDraw(&dc);
}

// CINKSView message handlers
void CINKSView::OnInitialUpdate()
{
	//GetDocument()->m_pMouseTracker = new CMouseTracker(this->GetParentFrame(), &GetDocument()->m_SnapMouse);
	GetDocument()->m_pMouseTracker = new CMouseTracker(this, &GetDocument()->m_SnapMouse);
	GetDocument()->m_pMouseTracker->theApp = (CINKSApp*)AfxGetApp();

	GetDocument()->m_pMouseTracker->m_pSpaceHash = GetDocument()->m_pSpaceHash;
	GetDocument()->m_pMouseTracker->m_SnapMouse = &GetDocument()->m_SnapMouse;

	GetDocument()->m_pMouseTracker->InitialUpdate(this);

	CSize sizeTotal(2000,1000);
	SetScrollSizes(MM_TEXT, sizeTotal);

	GetDocument()->m_SnapMouse.InitGrid(2000, 1000);
	
	CZoomView::OnInitialUpdate();
}


void CINKSView::OnDraw(CDC* pDC)
{
	CINKSDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if(!pDoc)
		return;

	CBrush brSceneView(RGB(255,255,255));
	CRect rcClient;
	GetClientRect(&rcClient);
	pDC->FillRect(rcClient, &brSceneView);
	//((CINKSDoc*)GetDocument())->m_SnapMouse.InitGrid(this->GetTotalSize().cx, this->GetTotalSize().cy);
	((CINKSDoc*)GetDocument())->m_SnapMouse.InitGrid(2000, 1000);
	((CINKSDoc*)GetDocument())->m_SnapMouse.DrawGrid(pDC);

	// Block for Graphics
	Graphics g(pDC->GetSafeHdc());
	g.SetSmoothingMode(SmoothingModeHighQuality);
	g.SetInterpolationMode(InterpolationModeHighQualityBicubic);
	g.SetPixelOffsetMode(PixelOffsetModeHighQuality);

	POSITION pos = pDoc->m_COList.GetHeadPosition();
	CCommonObject *pObj = NULL;
	while(pos){
		pObj = pDoc->m_COList.GetNext(pos);
		pObj->OutScreen(g);
	}
	pDoc->m_pMouseTracker->Draw(pDC, g);
}

void CINKSView::OnLButtonDown(UINT nFlags, CPoint point)
{
	CZoomView::OnLButtonDown(nFlags, point);
	
	CClientDC dc(this);
	OnPrepareDC(&dc);
	SetMapMode(&dc);

	DPtoLP(&point);
	GetDocument()->m_SnapMouse.SnapCursor(point);
	if(point.x >= 2000 || point.y >= 1000){
		MessageBox(_T("Point beyond maximum canvas size!"), _T("Error!"), MB_ICONERROR);
		return;
	}
	bool bNeedUpdate = false;
	bool bShiftDown = ::GetKeyState(VK_SHIFT) < 0;
	bool bControlDown = ::GetKeyState(VK_CONTROL) < 0;

	
	if(GetDocument()->m_pMouseTracker->m_Mode == GetDocument()->m_pMouseTracker->ModeSelect){ // if selecting
///////////////////////////////////////////////////////////////////////////////
		// try HitTest on existing objects
		CPoint pnt(point);
		GetDocument()->m_SnapMouse.SnapCursor(pnt);
		CCommonObject *pObj = GetDocument()->m_pSpaceHash->HashTest(pnt);
		if(pObj){ // we hit something
			if(!GetDocument()->SOListContains(pObj)){ // if object is not selected yet
				if(!GetDocument()->m_SOList.IsEmpty()){ // if we have something in the list already
					if(!bShiftDown){ // and not hold shiftkey
						POSITION pos = GetDocument()->m_SOList.GetHeadPosition();
						while(pos){
							GetDocument()->m_SOList.GetNext(pos)->m_bIsSelected = false;
						}
						GetDocument()->m_SOList.RemoveAll();
					}
				}
				pObj->m_bIsSelected = true;
				GetDocument()->m_SOList.AddTail(pObj);
				//SelectDependentObjects(pObj);
				bNeedUpdate = true;
			}
			else{ // if object hit is already selected
				if(bShiftDown){ // and we hold shiftkey
					// deselect it
					POSITION pos = GetDocument()->m_SOList.GetHeadPosition();
					CCommonObject *pObjTemp = NULL;
					while(pos){
						pObjTemp = GetDocument()->m_SOList.GetAt(pos);
						if(pObj == pObjTemp){
							pObjTemp->m_bIsSelected = false;
							//DeSelectDependentObjects(pObj);
							GetDocument()->m_SOList.RemoveAt(pos);

							break;
						}
						GetDocument()->m_SOList.GetNext(pos);
					}
					bNeedUpdate = true;
				}
				else if(bControlDown){ // choose one CObject from more selected
					if(GetDocument()->m_SOList.GetSize()){
						POSITION pos = GetDocument()->m_SOList.GetHeadPosition();
						POSITION prevPos;
						while(pos){
							prevPos = pos;
							GetDocument()->m_SOList.GetNext(pos)->m_bIsSelected = false;
							GetDocument()->m_SOList.RemoveAt(prevPos);
							
						}
					}
					GetDocument()->m_SOList.AddTail(pObj);
					//SelectDependentObjects(pObj);
					bNeedUpdate = true;
				}
				
			}
			GetDocument()->m_pMouseTracker->m_nSOListCount = (int)GetDocument()->m_SOList.GetSize();
			GetDocument()->m_pMouseTracker->m_bMoveCObject = true;
		}
		else{ // we did not hit any object
			if(!GetDocument()->m_SOList.IsEmpty() && !bShiftDown){ // if SOList is !empty, clear it
				POSITION pos = GetDocument()->m_SOList.GetHeadPosition();
				while(pos){
					GetDocument()->m_SOList.GetNext(pos)->m_bIsSelected = false;
				}
				GetDocument()->m_SOList.RemoveAll();
				bNeedUpdate = true;
			}
			//...probably we're going to select some region
			GetDocument()->m_pMouseTracker->m_bSelectRegion = true;
			GetDocument()->m_pMouseTracker->m_nSOListCount = 0;
		}
		if(bNeedUpdate){
			GetDocument()->UpdateObjecInfo();
			OnUpdate(this, NULL, NULL);
			bNeedUpdate = false;
		}
	}
	else if(GetDocument()->m_pMouseTracker->m_Mode == CMouseTracker::ModeZoomIn ){ // || GetDocument()->m_pMouseTracker->m_Mode == CMouseTracker::ModeZoomOut
		GetDocument()->m_pMouseTracker->m_bSelectRegion = true;
	}
	int trackresult = GetDocument()->m_pMouseTracker->Track(&dc, nFlags, point);
	//int trackresult = -1;
	if(trackresult > 0){
		switch(trackresult){
			case CMouseTracker::TrackMove:
				{
				GetDocument()->TryMovedConnection();
				theApp.m_pMainFrame->m_SideBar.ShowInfo(GetDocument());
				bNeedUpdate = true;
				}
				break;
			case CMouseTracker::TrackCreateNew:
				{
					bNeedUpdate = GetDocument()->AddNewCommonObject(point);
				}
				break;
			case CMouseTracker::TrackSucceeded:
				{
					bNeedUpdate = GetDocument()->AddNewCommonObjectWire(point);
				}
				break;
			case CMouseTracker::TrackNoMove:
				{
					if(GetDocument()->m_pMouseTracker->m_Mode == CMouseTracker::ModeZoomIn || GetDocument()->m_pMouseTracker->m_Mode == CMouseTracker::ModeZoomOut)
						Zoom(CRect(0,0,0,0));
				}
				break;
			case CMouseTracker::TrackSelectRegion:
				{
					if(GetDocument()->m_pMouseTracker->m_Mode == CMouseTracker::ModeSelect){
						SelectObjectsFromRegion(GetDocument()->m_pMouseTracker->GetSelectedRegion());
						bNeedUpdate = true;
					}
					else if(GetDocument()->m_pMouseTracker->m_Mode == CMouseTracker::ModeZoomIn || GetDocument()->m_pMouseTracker->m_Mode == CMouseTracker::ModeZoomOut){
						//SetZoomArea(GetDocument()->m_pMouseTracker->GetSelectedRegion());
						Zoom(GetDocument()->m_pMouseTracker->GetSelectedRegion());
					}
				}
				break;
			case CMouseTracker::TrackEditWireEdge:
				{
					GetDocument()->TryMovedConnection();
					theApp.m_pMainFrame->m_SideBar.ShowInfo(GetDocument());
					bNeedUpdate = true;
				}
				break;
		}
		GetDocument()->m_pMouseTracker->Reset();
	}
	if(bNeedUpdate){
		GetDocument()->SetModifiedFlag();
		OnUpdate(this, NULL, NULL);
	}
}

void CINKSView::OnLButtonUp(UINT nFlags, CPoint point)
{
	//CScrollView::OnLButtonUp(nFlags, point);
}

void CINKSView::OnRButtonDown(UINT nFlags, CPoint point)
{
	if(!GetDocument()->m_SOList.IsEmpty()){
		ClientToScreen(&point);
		CMenu menu;
		if(menu.LoadMenuW(IDR_MENU1)){
			int nType = GetDocument()->m_SOList.GetHead()->GetType();
			if(GetDocument()->m_SOList.GetSize() == 1 && nType == 2){ // block
				CMenu *pMenu = menu.GetSubMenu(1);
				pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd());
			}
			else{ // gate || wire || multiple
				CMenu *pMenu = menu.GetSubMenu(0);
				pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd());
			}
		}
	}
}

void CINKSView::OnMouseMove(UINT nFlags, CPoint point)
{
	//p1 page coordinates; p2 snapped page coordinates; p3 screen coordinates; 
	CPoint p1, p2, p3;
	p1 = p2 = p3 = point;
	CClientDC dc(this);
	OnPrepareDC(&dc);
	//dc.DPtoLP(&p1);
	//dc.DPtoLP(&p2);
	DPtoLP(&p1);
	DPtoLP(&p2);
	GetDocument()->m_SnapMouse.SnapCursor(p2);
	theApp.m_pMainFrame->UpdateMousePos(p1, p2, p3);
}

void CINKSView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if(nChar == 'o' || nChar == 'O'){
		ofstream outfile("spacialhashing.txt");
		stringstream *ss = new stringstream();
		this->GetDocument()->m_pSpaceHash->OutVH(*ss);
		outfile << ss->str();
		outfile.close();
		SAFE_DELETE(ss);
	}
	if(nChar == 'l' || nChar == 'L'){
		ofstream outfile("spacialhashingLINES.txt");
		stringstream *ss = new stringstream();
		this->GetDocument()->m_pSpaceHash->OutLine(*ss);
		outfile << ss->str();
		outfile.close();
		SAFE_DELETE(ss);
	}
	if(nChar == VK_DELETE && !GetDocument()->m_SOList.IsEmpty()){
		GetDocument()->DeleteSelection();
	}
	if(nChar == 'g' || nChar == 'G'){
		GetDocument()->m_SnapMouse.EnableGrid(false);
		OnUpdate(this, NULL, NULL);
	}
	CZoomView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CINKSView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if(nChar == VK_SHIFT){
		CClientDC dc(this);
		OnPrepareDC(&dc);
		GetDocument()->m_pMouseTracker->OnSetCursor(&dc);
	}
	CZoomView::OnKeyUp(nChar, nRepCnt, nFlags);
}

bool CINKSView::SelectObjectsFromRegion(CRect rect)
{
	return false;
	//rect.NormalizeRect();
	//GetDocument()->m_COListIterator->ToFront();
	//Rect selRectr;
	//while(GetDocument()->m_COListIterator->HasNext()){
	//	selRectr = *GetDocument()->m_COListIterator->PeekNext()->GetBounds();
	//	if(rect.PtInRect(CPoint(selRectr.GetLeft(), selRectr.GetTop())) && rect.PtInRect(CPoint(selRectr.GetRight(), selRectr.GetBottom()))){
	//		GetDocument()->m_SOList.Append(GetDocument()->m_COListIterator->PeekNext());
	//		GetDocument()->m_COListIterator->PeekNext()->m_bIsSelected = true;
	//	}
	//		GetDocument()->m_COListIterator->Next();
	//}
}

BOOL CINKSView::OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message)
{
	CClientDC dc(this);
	OnPrepareDC(&dc);
	BOOL b = FALSE;
	if(nHitTest == HTCLIENT)
		b = GetDocument()->m_pMouseTracker->OnSetCursor(&dc);
	if(!b)
		b = CZoomView::OnSetCursor(pWnd, nHitTest, message); 
	return b;
}


void CINKSView::OnSelectZoomIn()
{
	GetDocument()->m_pMouseTracker->m_Mode = GetDocument()->m_pMouseTracker->ModeZoomIn;
	m_ZoomMode = ZoomModeIn;
}
void CINKSView::OnSelectZoomOut()
{
	GetDocument()->m_pMouseTracker->m_Mode = GetDocument()->m_pMouseTracker->ModeZoomOut;
	m_ZoomMode = ZoomModeOut;
}
void CINKSView::OnSelectZoomToFit()
{
}
void CINKSView::OnSelectZoomHand()
{
	//theApp.m_pMainFrame->UpdateInfoBar(NULL, GetDocument()->m_pMouseTracker->ModeZoom, GetDocument()->m_pMouseTracker->NoCreate);
	GetDocument()->m_pMouseTracker->m_Mode = GetDocument()->m_pMouseTracker->ModeZoomHand;
	m_ZoomMode = ZoomModeHand;

}


CList<CCommonObject*, CCommonObject*>* CINKSView::GetSOList()
{
	return &GetDocument()->m_SOList;
}

CList<CCommonObject*, CCommonObject*>* CINKSView::GetCOList()
{
	return &GetDocument()->m_COList;
}

void CINKSView::TrackTag()
{
	CPoint pntScreen;
	GetCursorPos(&pntScreen);
	ClientToScreen(&pntScreen);
	
	CClientDC dc(this);
	OnPrepareDC(&dc);
	SetMapMode(&dc);

	DPtoLP(&pntScreen);
	GetDocument()->m_SnapMouse.SnapCursor(pntScreen);

	int nRes = GetDocument()->m_pMouseTracker->Track(&dc, 1, CPoint(0,0));
	if(nRes == CMouseTracker::TrackSucceeded){
		// here is stored tagged object
		GetDocument()->m_pMouseTracker->m_gAddWireObject[0];
		// and here which in/out was hit
		GetDocument()->m_pMouseTracker->m_nWireConnection[0];	// 1,2,3... input; -1,-2,-3... output
		GetDocument()->AddNewCommonObject(GetDocument()->m_pMouseTracker->m_Point);
		//Invalidate();
		GetDocument()->m_pMouseTracker->Reset();
		OnUpdate(this, NULL, NULL);
		TrackTag();
	}
	GetDocument()->m_pMouseTracker->Reset();
	GetDocument()->m_pMouseTracker->m_Mode = CMouseTracker::ModeSelect;
	GetDocument()->m_pMouseTracker->m_CreateMode = CMouseTracker::NoCreate;
	OnUpdate(this, NULL, NULL);
}
void CINKSView::OnExportEmf()
{
	CClientDC dc(this);

	Metafile* myMetafile = new Metafile(L"MyDiskFile.emf", dc.GetSafeHdc());
	Graphics* myGraphics = new Graphics(myMetafile);
	//Pen* myPen = new Pen(Color(255, 0, 0, 200));
	POSITION pos = GetDocument()->m_COList.GetHeadPosition();
	CCommonObject *pObj = NULL;
	myGraphics->SetSmoothingMode(SmoothingModeAntiAlias);
	while(pos){
		pObj = GetDocument()->m_COList.GetNext(pos);
		myGraphics->DrawPath(pObj->m_pPen, pObj->m_gPath);
		if(pObj->m_gpText)
			myGraphics->DrawPath(pObj->m_pPen, pObj->m_gpText);
	}
	
	//myGraphics->DrawLine(myPen, 0, 0, 60, 40);
	delete myGraphics;
	//delete myPen;
	delete myMetafile;
	
}

void CINKSView::OnExportPdf()
{
	CPdfCreator creator;
	// TODO: Add your command handler code here
}

void CINKSView::OnExportPs()
{

	CString strOutFile = theApp.m_pOpenedProjectDoc->m_ProjectDoc.m_strProjectPath;
	strOutFile.Append(GetDocument()->GetTitle());
	strOutFile.Append(_T(".ps"));
	ofstream outfile(strOutFile.GetBuffer());
	strOutFile.ReleaseBuffer();
	CPSCreator creator;
	
	outfile << creator.WriteScheme(&GetDocument()->m_COList).str();
	outfile.flush();
	outfile.close();
	CString strOut = _T("PostScript file generated to \n");
	strOut.Append(strOutFile);
	MessageBox(strOut, _T("Information"), MB_ICONINFORMATION);
	// TODO: Add your command handler code here
}
