
// BKView.cpp : реализация класса CBKView
//

#include "stdafx.h"
// SHARED_HANDLERS можно определить в обработчиках фильтров просмотра реализации проекта ATL, эскизов
// и поиска; позволяет совместно использовать код документа в данным проекте.
#ifndef SHARED_HANDLERS
#include "BK.h"
#endif

#include "BKDoc.h"
#include "BKView.h"
#include "MainFrm.h"
#include "Screen.h"
#include "BKErrMsgSys.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CBKView

IMPLEMENT_DYNCREATE(CBKView, CView)

BEGIN_MESSAGE_MAP(CBKView, CView)
	// Стандартные команды печати
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CBKView::OnFilePrintPreview)
	ON_COMMAND(ID_FILE_CUSTOM_PRINT, &CBKView::OnCustomFilePrint)
	ON_WM_CONTEXTMENU()
	ON_WM_RBUTTONUP()
	ON_WM_CREATE()
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()
END_MESSAGE_MAP()

// создание/уничтожение CBKView

CBKView::CBKView()
	: m_pScreen(NULL)
	, m_bPrintScreen(TRUE)
	, m_bPrintInverse(TRUE)
{
	m_strPrintTitle.LoadString(IDS_PRINT_TITLE);
	m_rgnRes.CreateRectRgn(0, 0, 0, 0);
	//m_br.CreateSysColorBrush(COLOR_BTNFACE);
	m_br.CreateSolidBrush(RGB(0x80, 0x80, 0x80));
}

CBKView::~CBKView()
{
	SAFE_DELETE(m_pScreen);
}

BOOL CBKView::PreCreateWindow(CREATESTRUCT &cs)
{
	// TODO: изменить класс Window или стили посредством изменения
	//  CREATESTRUCT cs
	cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_SAVEBITS,
	                                   ::LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_WINDOW + 1), NULL);
	return CView::PreCreateWindow(cs);
}

BOOL CBKView::PreTranslateMessage(MSG *pMsg)
{
	// перенаправляем всю клавиатуру в MainFrame.
	//!!! Но правильным способом было бы обрабатывать клавиатуру тут
	switch (pMsg->message)
	{
		case WM_KEYDOWN:
		case WM_KEYUP:
		case WM_SYSKEYDOWN:
		case WM_SYSKEYUP:
			AfxGetMainWnd()->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam);
			return TRUE;
	}

	return CView::PreTranslateMessage(pMsg);
}

// рисование CBKView

void CBKView::OnDraw(CDC * /*pDC*/)
{
	CBKDoc *pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (!pDoc)
	{
		return;
	}

	if (IsWindowVisible())
	{
		m_pScreen->DrawScreen(TRUE);
	}
}


// печать CBKView

void CBKView::OnFilePrintPreview()
{
#ifndef SHARED_HANDLERS
	AFXPrintPreview(this);
#endif
}

BOOL CBKView::OnPreparePrinting(CPrintInfo *pInfo)
{
	// подготовка по умолчанию
	return DoPreparePrinting(pInfo);
}

void CBKView::OnBeginPrinting(CDC * /*pDC*/, CPrintInfo * /*pInfo*/)
{
	// TODO: добавьте дополнительную инициализацию перед печатью
}

void CBKView::OnEndPrinting(CDC * /*pDC*/, CPrintInfo * /*pInfo*/)
{
	// TODO: добавьте очистку после печати
}

void CBKView::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
	ClientToScreen(&point);
	OnContextMenu(this, point);
}

int CBKView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
	{
		return -1;
	}

	if ((m_pScreen = new CScreen(g_Config.m_nScreenRenderType)) == NULL)
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOCSCREENCREATE]);
		TRACE0("Не удалось создать класс CScreen\n");
		return -1;
	}

	if (!m_pScreen->Create(NULL, _T("BKScreen"), WS_VISIBLE | WS_CHILD, CRect(0, 0, CScreen::BK_SCREEN_WIDTH, CScreen::BK_SCREEN_HEIGHT), this, 0))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOCSCREENINIT]);
		TRACE0("Не удалось создать панель Screen View\n");
		return -1;      // не удалось создать
	}

	CMainFrame *pMF = reinterpret_cast<CMainFrame *>(GetParentFrame());

	if (pMF)
	{
		pMF->SetScreen(m_pScreen, &m_debugger);
	}

	m_pScreen->AdjustLayout();
	return 0;
}

void CBKView::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);
	m_pScreen->AdjustLayout();
}

void CBKView::OnContextMenu(CWnd * /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
	theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}


// диагностика CBKView

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

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

CBKDoc *CBKView::GetDocument() const // встроена неотлаженная версия
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CBKDoc)));
	return (CBKDoc *)m_pDocument;
}
#endif //_DEBUG


// обработчики сообщений CBKView

BOOL CBKView::OnEraseBkgnd(CDC *pDC)
{
	CRect rcScreen;
	m_pScreen->GetClientRect(rcScreen);
	m_pScreen->ClientToScreen(rcScreen);
	ScreenToClient(rcScreen);
	CRect rcClient;
	GetClientRect(rcClient);
	CRgn rgnScreen;
	rgnScreen.CreateRectRgnIndirect(rcScreen);
	CRgn rgnClient;
	rgnClient.CreateRectRgnIndirect(rcClient);
	m_rgnRes.CombineRgn(&rgnClient, &rgnScreen, RGN_DIFF);
	pDC->SelectClipRgn(&m_rgnRes);
	CBrush *pOld = pDC->SelectObject(&m_br);
	pDC->PatBlt(0, 0, rcClient.Width(), rcClient.Height(), PATCOPY);
	pDC->SelectObject(pOld);
	return TRUE;
}

void CBKView::OnCustomFilePrint()
{
	CPrintDlg *pdlg = new CPrintDlg(FALSE);

	if (pdlg)
	{
		pdlg->m_pDebugger = &m_debugger;
		pdlg->m_strTitle = m_strPrintTitle;
		pdlg->m_bPrintScreen = m_bPrintScreen;
		pdlg->m_bInverse = m_bPrintInverse;
		pdlg->m_startAddr = m_debugger.GetCurrentAddress();
		pdlg->m_endAddr = m_debugger.GetBottomAddress();

		if (pdlg->DoModal() == IDOK)
		{
			m_strPrintTitle = pdlg->m_strTitle;
			m_bPrintScreen = pdlg->m_bPrintScreen;
			m_bPrintInverse = pdlg->m_bInverse;
			Print(pdlg);
		}

		delete pdlg;
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}

	CMainFrame *pMF = reinterpret_cast<CMainFrame *>(GetParentFrame());

	if (pMF)
	{
		pMF->SetFocusToBK();
	}
}




void CBKView::Print(CPrintDlg *pPrintDlg)
{
	WORD startAddress = pPrintDlg->m_startAddr;
	WORD endAddress   = pPrintDlg->m_endAddr;
	CDC *pdc = new CDC();

	if (pdc)
	{
		// Attach a printer DC
		pdc->Attach(pPrintDlg->GetPrinterDC());
		pdc->m_bPrinting = TRUE;
		// Initialise print document details
		DOCINFO di;
		ZeroMemory(&di, sizeof(DOCINFO));
		di.cbSize = sizeof(DOCINFO);
		di.lpszDocName = m_strPrintTitle;
		// Begin a new print job
		BOOL bPrintingOK = pdc->StartDoc(&di);
		// Get the printing extents and store in the m_rectDraw field of a
		// CPrintInfo object
		CPrintInfo Info;

		if (m_bPrintScreen)
		{
			Info.SetMaxPage(1);
		}
		else
		{
			Info.SetMaxPage(pPrintDlg->m_nPages);
		}

		Info.m_rectDraw.SetRect(0, 0, pdc->GetDeviceCaps(HORZRES), pdc->GetDeviceCaps(VERTRES));

		//OnBeginPrinting (&dc, &Info);
		for (UINT page = Info.GetMinPage(); page <= Info.GetMaxPage() && bPrintingOK; page++)
		{
			// begin new page
			pdc->StartPage();
			Info.m_nCurPage = page;
			startAddress = PrintPage(pdc, &Info, startAddress, endAddress);
			bPrintingOK = (pdc->EndPage() > 0);  // end page
		}

		//OnEndPrinting (&dc, &Info);
		if (bPrintingOK)
		{
			// end a print job
			pdc->EndDoc();
		}
		else
		{
			// abort job.
			pdc->AbortDoc();
		}

		// detach the printer DC
		pdc->Detach();
		delete pdc;
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}
}


WORD CBKView::PrintPage(CDC *pDC, CPrintInfo *pInfo, WORD currAddress, WORD endAddress)
{
	// Get paper sizes
	int width = pInfo->m_rectDraw.Width();
	int height = pInfo->m_rectDraw.Height();
	int lines_per_page = PRINT_LINES_PER_PAGE; // Lines per page
	int fnt_height = height / lines_per_page;
	int tabs = width / PRINT_TAB_LENGTH;
	CPen pen;
	pen.CreatePen(PS_SOLID, 10, RGB(0, 0, 0));
	CFont fnt;
	fnt.CreatePointFont(fnt_height, _T("Arial"), pDC);
	CFont *pOld = pDC->SelectObject(&fnt);
	CPen *pOldPen = pDC->SelectObject(&pen);
	pDC->SetTextColor(RGB(0, 0, 0));
	pDC->SetBkColor(RGB(255, 255, 255));

	if (m_bPrintScreen)
	{
		// Print Screenshot
		CDC *pdc = new CDC();

		if (pdc)
		{
			pdc->CreateCompatibleDC(pDC);
			// Get Screenshot handle
			HBITMAP hScreenShot = (HBITMAP)CopyImage(m_pScreen->GetScreenshot(), IMAGE_BITMAP, CScreen::BK_SCREEN_WIDTH, CScreen::BK_SCREEN_HEIGHT, 0);

			if (hScreenShot)
			{
				BITMAP bm;
				GetObject(hScreenShot, sizeof(BITMAP), &bm);
				// Calculate print rectangle
				int len_w = (width < height) ? width : height;
				len_w = len_w * 10 / 12;
				int len_h = len_w * 3 / 4; //делаем пропорции 4/3
				int xOfs = (pInfo->m_rectDraw.Width() - len_w) / 2;
				int yOfs = (pInfo->m_rectDraw.Height() - len_h) / 2;
				// Stretch Screenshot to Print page
				int stretchMode = m_bPrintInverse ? SRCINVERT : SRCCOPY;
				HGDIOBJ obj = pdc->SelectObject(hScreenShot);
				pDC->StretchBlt(xOfs, yOfs, len_w, len_h, pdc, 0, 0, bm.bmWidth, bm.bmHeight, stretchMode);
				pdc->SelectObject(obj);
				DeleteObject(hScreenShot);
			}
			else
			{
				CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
			}

			pdc->DeleteDC();
			delete pdc;
		}
		else
		{
			CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
		}
	}
	else
	{
		for (int line = 3; line < lines_per_page - 3; line++)
		{
			CRect rcString(0, line * fnt_height, width, line * fnt_height + fnt_height);
			CString strInstr;
			CString strArg;
			WORD codes[3];
			WORD addrOfs = m_debugger.DebugInstruction(currAddress, strInstr, strArg, codes);

			//у strArg надо удалить маркеры колоризации.
			while (1)
			{
				int pos = strArg.Find(_T("<HLC>"), 0); //находим первый попавшийся маркер

				if (pos < 0)
				{
					break;
				}

				int len = strArg.GetLength();
				strArg = strArg.Left(pos) + strArg.Right(len - pos - 6);
			}

			CString str;
			str.Format(_T("%07o  |"), currAddress);
			rcString.left = tabs * 1;
			pDC->DrawText(str, &rcString, DT_LEFT | DT_EXPANDTABS);
			rcString.left = tabs * 2;
			pDC->DrawText(strInstr, &rcString, DT_LEFT | DT_EXPANDTABS);
			rcString.left = tabs * 3;
			pDC->DrawText(strArg, &rcString, DT_LEFT | DT_EXPANDTABS);

			switch (addrOfs)
			{
				case 1:
					str.Format(_T("  |%06o"), codes[0]);
					break;

				case 2:
					str.Format(_T("  |%06o   %06o"), codes[0], codes[1]);
					break;

				case 3:
					str.Format(_T("  |%06o   %06o   %06o"), codes[0], codes[1], codes[2]);
					break;
			}

			rcString.left = tabs * 5;
			pDC->DrawText(str, &rcString, DT_LEFT | DT_EXPANDTABS);
			currAddress += addrOfs * 2;

			if (currAddress >= endAddress)
			{
				break;
			}
		}
	}

	pDC->MoveTo(0, 3 * fnt_height - fnt_height / 2);
	pDC->LineTo(width, 3 * fnt_height - fnt_height / 2);
	pDC->MoveTo(0, (lines_per_page - 3) * fnt_height + fnt_height / 2);
	pDC->LineTo(width, (lines_per_page - 3) * fnt_height + fnt_height / 2);
	CRect rcString;
	rcString.SetRect(0, fnt_height, width, fnt_height + fnt_height);
	CString strTitle;
	strTitle.LoadString(IDS_EMUL_TITLE);
	int oldMode = pDC->SetBkMode(TRANSPARENT);
	pDC->DrawText(strTitle, &rcString, DT_LEFT | DT_EXPANDTABS);
	pDC->DrawText(m_strPrintTitle, &rcString, DT_RIGHT | DT_EXPANDTABS);
	pDC->SetBkMode(oldMode);
	pDC->SetTextColor(RGB(255, 255, 255));
	pDC->SetBkColor(RGB(0, 0, 0));
	rcString.SetRect(0, (lines_per_page - 2) * fnt_height, width, (lines_per_page - 2) * fnt_height + fnt_height);
	CString strPages;
	strPages.Format(IDS_PRINT_PAGES, pInfo->m_nCurPage, pInfo->GetMaxPage());
	pDC->PatBlt(rcString.left, rcString.top, rcString.Width(), rcString.Height(), BLACKNESS);
	pDC->DrawText(strPages, &rcString, DT_RIGHT | DT_EXPANDTABS);
	pDC->SelectObject(pOld);
	pDC->SelectObject(pOldPen);
	return currAddress;
}

