
// 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 "PrintDlg.h"
#include "BKErrMsgSys.h"
#include "Config.h"

#include "Board.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#ifdef _DEBUG
//при отладке полезно выключать хук, а то очень тормозит при трассировке
//главное - не забыть включить обратно
//#define SET_KBD_HOOK 1
#else
//в релизной версии обязательно должно быть включено. А то блин склероз.
#define SET_KBD_HOOK 1
#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_MESSAGE(WM_VKBD_DOWN, &OnVirtualKeyDown)
	ON_MESSAGE(WM_VKBD_UP, &OnVirtualKeyUp)
	ON_WM_CONTEXTMENU()
	ON_WM_RBUTTONUP()
	ON_WM_CREATE()
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	ON_WM_SYSKEYDOWN()
	ON_WM_SYSKEYUP()
	ON_WM_CLOSE()
	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()
END_MESSAGE_MAP()

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

CBKView::CBKView()
	: m_pScreen(nullptr)
	, m_bPrintScreen(true)
	, m_bPrintInverse(true)
	, m_nRenderType(-1)
{
	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()
{
	DeleteSCR();
}

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(nullptr, IDC_ARROW), HBRUSH(COLOR_WINDOW + 1), nullptr);
	return CView::PreCreateWindow(cs);
}


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

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

	if (!pDoc)
	{
		return;
	}

	DrawScreen();
}


void CBKView::DrawScreen()
{
	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);
}

static HHOOK g_hHook = nullptr; //handle to the hook procedure
static bool g_bAppFocused = true; //флаг что фокус на окне, чтобы если окно не в фокусе, кнопки WIN не перехватывались
static CBKView *g_pthisObj = nullptr; //Указатель на объект this, чтобы можно было вызвать нестатические функции из статической
LRESULT CALLBACK CBKView::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	auto lp = (KBDLLHOOKSTRUCT *)lParam;

	if (nCode < 0)
	{
		return CallNextHookEx(g_hHook, nCode, wParam, lParam);
	}

	if (g_bAppFocused)
	{
		if (nCode == HC_ACTION)
		{
			switch (wParam)
			{
				case WM_KEYDOWN:
				case WM_SYSKEYDOWN:
					if (lp->vkCode == VK_LWIN || lp->vkCode == VK_RWIN)
					{
						g_pthisObj->EmulateKeyDown(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_ESCAPE) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+КТ
						g_pthisObj->EmulateKeyDown(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0))
					{
						//СУ+КТ
						g_pthisObj->EmulateKeyDown(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_F4) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+F4
						g_pthisObj->EmulateKeyDown(lp->vkCode, lp->flags);
						return 1;
					}

					break;

				case WM_KEYUP:
				case WM_SYSKEYUP:
					if (lp->vkCode == VK_LWIN || lp->vkCode == VK_RWIN)
					{
						g_pthisObj->EmulateKeyUp(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_ESCAPE) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+КТ
						g_pthisObj->EmulateKeyUp(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0))
					{
						//СУ+КТ
						g_pthisObj->EmulateKeyUp(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_F4) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+F4
						g_pthisObj->EmulateKeyUp(lp->vkCode, lp->flags);
						return 1;
					}

					break;
			}
		}
	}

	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}


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

#ifdef SET_KBD_HOOK
	//ставим хук для перехвата кнопок левый, правый WIN и прочие системные кнопки
	g_pthisObj = this;
	g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)&LowLevelKeyboardProc, nullptr /*GetModuleHandle(nullptr)*/, 0);
	ASSERT(g_hHook != nullptr);
#endif //SET_KBD_HOOK
	int ret = CreateSCR();
	return ret;
}


bool CBKView::ReCreateSCR()
{
	if (m_nRenderType != g_Config.m_nScreenRenderType)
	{
		DeleteSCR();
		return (CreateSCR() == 0);
	}

	return true;
}


int CBKView::CreateSCR()
{
	if (!m_pScreen)
	{
		m_nRenderType = -1;

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

		if (!m_pScreen->Create(nullptr, _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;      // не удалось создать
		}

		m_nRenderType = g_Config.m_nScreenRenderType;
		auto pMF = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());

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

		m_pScreen->AdjustLayout();
	}

	return 0;
}

void CBKView::DeleteSCR()
{
	if (m_pScreen)
	{
		m_pScreen->DestroyWindow();
		SAFE_DELETE(m_pScreen);
		m_nRenderType = -1;
	}
}


void CBKView::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);
	m_pScreen->AdjustLayout();
	CRect rect;
	m_pScreen->GetClientRect(&rect);
	GetParentFrame()->SendMessage(WM_SCREENSIZE_CHANGED, rect.Width(), rect.Height());
}

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()
{
	auto 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 != 0;
			m_bPrintInverse = pdlg->m_bInverse != 0;
			Print(pdlg);
		}

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

	auto pMF = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());

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




void CBKView::Print(CPrintDlg *pPrintDlg)
{
	WORD startAddress = pPrintDlg->m_startAddr;
	WORD endAddress   = pPrintDlg->m_endAddr;
	auto 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
		auto pdc = new CDC();

		if (pdc)
		{
			pdc->CreateCompatibleDC(pDC);
			// Get Screenshot handle
			auto 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->SetStretchBltMode(HALFTONE);
				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 надо удалить маркеры колоризации.
			for (;;)
			{
				int pos = strArg.Find(_T(COLORED_TAG), 0); //находим первый попавшийся маркер

				if (pos < 0)
				{
					break;
				}

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

			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;
}


void CBKView::OnInitialUpdate()
{
	CView::OnInitialUpdate();
	auto mw = GetParentFrame();

	if (mw)
	{
		mw->PostMessage(WM_START_PLATFORM);
	}
}

void CBKView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());

	if (nChar == VK_MENU) //если нажат Ctrl, то нажатие alt отлавливается тут и только тут
	{
		//WORD uScan = HIWORD(pMsg->lParam) & 0x1FF; //это чтобы различать левый/правый альты, если uScan == 56, то левый альт, иначе - правый.
		//если nFlags & 0x100 == 0, то левый альт, если != 0 то правый
		mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_AR2, true);
		mw->GetBKVKBDViewPtr()->RedrawWindow();
	}
	else if (mw->GetChip() && !EmulateJoystickDown(nChar, nFlags))
	{
		EmulateKeyDown(nChar, nFlags);
	}
}

void CBKView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());

	if (nChar == VK_MENU) //после нажатия alt+клавиша, отжатие alt отлавливается тут и только тут
	{
		//WORD uScan = HIWORD(pMsg->lParam) & 0x1FF; //это чтобы различать левый/правый альты, если uScan == 56, то левый альт, иначе - правый.
		//если nFlags & 0x100 == 0, то левый альт, если != 0 то правый
		mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_AR2, false);
		mw->GetBKVKBDViewPtr()->RedrawWindow();
	}
	else if (mw->GetChip() && !EmulateJoystickUp(nChar, nFlags))
	{
		EmulateKeyUp(nChar, nFlags);
	}
}

void CBKView::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());

	// Если нажали любой Alt
	if (nChar == VK_MENU)
	{
		//WORD uScan = HIWORD(pMsg->lParam) & 0x1FF; //это чтобы различать левый/правый альты, если uScan == 56, то левый альт, иначе - правый.
		//если nFlags & 0x100 == 0, то левый альт, если != 0 то правый
		mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_AR2, true);
		mw->GetBKVKBDViewPtr()->RedrawWindow();
	}
	else
	{
		EmulateKeyDown(nChar, nFlags);
	}
}

void CBKView::OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());

	if (nChar == VK_MENU) //после нажатия alt, отжатие alt отлавливается тут и только тут, если не нажимали при этом других клавиш
	{
		//WORD uScan = HIWORD(pMsg->lParam) & 0x1FF; //это чтобы различать левый/правый альты, если uScan == 56, то левый альт, иначе - правый.
		//если nFlags & 0x100 == 0, то левый альт, если != 0 то правый
		mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_AR2, false);
		mw->GetBKVKBDViewPtr()->RedrawWindow();
	}
	else
	{
		EmulateKeyUp(nChar, nFlags);
	}
}

void CBKView::EmulateKeyDown(UINT nChar, UINT nFlags)
{
	auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());
	auto board = mw->GetChip();

	switch (nChar)
	{
		case VK_PAUSE:      // Если нажали СТОП
		case VK_DIVIDE:
			board->StopInterrupt(); //нажали на кнопку стоп
			break;

		case VK_SHIFT:      // Если нажали Шифт
			mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_SHIFT, true);
			mw->GetBKVKBDViewPtr()->RedrawWindow();
			break;

		case VK_CONTROL:    // Если нажали СУ (Любой Ctrl)
			//если nFlags & 0x100 == 0, то левый ctrl, если != 0 то правый
			mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_SU, true);
			mw->GetBKVKBDViewPtr()->RedrawWindow();
			//вот ещё так можно определять
//      GetAsyncKeyState(VK_LCONTROL); //<0 - нажато, >=0 - нет; ret&1 - кнопка нажата после предыдущего вызова GetAsyncKeyState
//      GetAsyncKeyState(VK_RCONTROL);
			break;

		/* кнопки Загл и Стр будем эмулировать капслоком. капслок включен - стр включено, выключен - загл включено, или наоборот */
		case VK_CAPITAL:
			mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_CAPS, !mw->GetBKVKBDViewPtr()->isCapitalPress());
			break;

		default:
		{
			// Запишем код клавиши в регистр 177662
			WORD nScanCode = 0;
			WORD nInt = 0;
			bool bSuccess = mw->GetBKVKBDViewPtr()->TranslateKey(nChar, !!(nFlags & KF_EXTENDED), &nScanCode, &nInt);

			if (!board->m_kprs.bKeyPressed && bSuccess)
			{
				//если ещё прошлый код не прочитали, новый игнорируем.
				if ((board->m_reg177660 & 0200) == 0)
				{
					//сюда заходим только если прочитан прошлый код
					board->m_reg177662in = nScanCode & 0177;
					board->m_reg177660 |= 0200;
					board->KeyboardInterrupt((mw->GetBKVKBDViewPtr()->isAR2Press()) ? INTERRUPT_274 : nInt);
				}

				if (g_Config.m_bBKKeyboard) //отключаем автоповтор, как на реальной клавиатуре БК
				{
					board->m_kprs.bKeyPressed = true;
					//отметим в векторе, что нажали очередную какую-то клавишу
					board->m_kprs.nScanCode = nScanCode;
					board->m_kprs.bProcessed = false;
					TRACE1("push char %d\n", nScanCode);
				}
				else
				{
					if (board->m_kprs.nScanCode == -1)
					{
						board->m_kprs.nScanCode = nScanCode;
						board->m_kprs.bProcessed = false;
						TRACE1("push char %d\n", nScanCode);
					}
				}
			}
		}
	}
}

void CBKView::EmulateKeyUp(UINT nChar, UINT nFlags)
{
	auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());
	auto board = mw->GetChip();

	switch (nChar)
	{
		case VK_PAUSE:
		case VK_DIVIDE:
			board->UnStopInterrupt(); //отжали кнопку стоп
			break;

		case VK_SHIFT:
			mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_SHIFT, false);
			mw->GetBKVKBDViewPtr()->RedrawWindow();
			break;

		case VK_CONTROL:
			mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_SU, false);
			mw->GetBKVKBDViewPtr()->RedrawWindow();
			break;

		default:
		{
			// отметим в структуре. что отжали очередную какую-то клавишу
			if (board->m_kprs.nScanCode != -1)
			{
				WORD nScanCode = 0;
				WORD nInt = 0;
				bool bSuccess = mw->GetBKVKBDViewPtr()->TranslateKey(nChar, !!(nFlags & KF_EXTENDED), &nScanCode, &nInt);

				if (bSuccess)
				{
					switch (nScanCode)
					{
						case BKKEY_LAT:
							mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_XLAT, false);
							break;

						case BKKEY_RUS:
							mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_XLAT, true);
							break;
					}

					if (board->m_kprs.nScanCode == nScanCode)
					{
						TRACE1("processed char %d\n", nScanCode);
						board->m_kprs.bProcessed = true;
					}
					else
					{
						TRACE2("processed char %d, expected: %d\n", nScanCode, board->m_kprs.nScanCode);
						board->m_kprs.bProcessed = true;
					}
				}
			}
		}
	}
}

bool CBKView::EmulateJoystickDown(UINT nChar, UINT nFlags)
{
	if (g_Config.m_bJoystick && !(nFlags & KF_EXTENDED))
	{
		auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());
		WORD &joystick = mw->GetChip()->m_reg177714in;

		switch (nChar)
		{
			case VK_HOME:
				joystick |= 0040;   // джойстик: кнопка 1; мышь: левая кнопка
				break;

			case VK_END:
				joystick |= 0200;   // джойстик: кнопка 2;
				break;

			case VK_PRIOR:
				joystick |= 0100;   // джойстик: кнопка 3; мышь: правая кнопка
				break;

			case VK_NEXT:
				joystick |= 0400;   // джойстик: кнопка 4
				break;

			case VK_CLEAR:
				joystick |= 0020;   // джойстик: кнопка 5
				break;

			case VK_RIGHT:
				joystick |= 0002;   // джойстик: вправо; мышь: вправо
				break;

			case VK_LEFT:
				joystick |= 0010;   // джойстик: влево; мышь: влево
				break;

			case VK_UP:
				joystick |= 0001;   // джойстик: вверх; мышь: вверх
				break;

			case VK_DOWN:
				joystick |= 0004;   // джойстик: вниз; мышь: вниз
				break;

			default:
				return false;
		}

		return true;
	}

	return false;
}


bool CBKView::EmulateJoystickUp(UINT nChar, UINT nFlags)
{
	if (g_Config.m_bJoystick && !(nFlags & KF_EXTENDED))
	{
		auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());
		WORD &joystick = mw->GetChip()->m_reg177714in;

		switch (nChar)
		{
			case VK_HOME:
				joystick &= ~0040;  // джойстик: кнопка 1; мышь: левая кнопка
				break;

			case VK_END:
				joystick &= ~0200;  // джойстик: кнопка 2;
				break;

			case VK_PRIOR:
				joystick &= ~0100;  // джойстик: кнопка 3; мышь: правая кнопка
				break;

			case VK_NEXT:
				joystick &= ~0400;  // джойстик: кнопка 4
				break;

			case VK_CLEAR:
				joystick &= ~0020;  // джойстик: кнопка 5
				break;

			case VK_RIGHT:
				joystick &= ~0002;  // джойстик: вправо; мышь: вправо
				break;

			case VK_LEFT:
				joystick &= ~0010;  // джойстик: влево; мышь: влево
				break;

			case VK_UP:
				joystick &= ~0001;  // джойстик: вверх; мышь: вверх
				break;

			case VK_DOWN:
				joystick &= ~0004;  // джойстик: вниз; мышь: вниз
				break;

			default:
				return false;
		}

		return true;
	}

	return false;
}

/*
*wParam - флаг клавиши BKKeyType
*Потенциально можно передавать и остальные управляющие клавиши, для отображения статуса.
*Нужно только подумать, как сделать так, чтобы не конфликтовало с обычной клавиатурой.
*lParam - если обычная клавиша, то мл.байт - сканкод, ст.байт - прерывание.
**/
LRESULT CBKView::OnVirtualKeyDown(WPARAM wParam, LPARAM lParam)
{
	auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());

	if (mw)
	{
		register auto board = mw->GetChip();

		switch (wParam)
		{
			case BKKeyType::BKKEY_REGULAR:
				if (!board->m_kprs.bKeyPressed)
				{
					BYTE nScanCode = LOBYTE(lParam);
					BYTE nInt = HIBYTE(lParam);

					//если ещё прошлый код не прочитали, новый игнорируем.
					if ((board->m_reg177660 & 0200) == 0)
					{
						//сюда заходим только если прочитан прошлы код
						board->m_reg177662in = nScanCode & 0177;
						board->m_reg177660 |= 0200;
						board->KeyboardInterrupt(nInt);
					}

					board->m_kprs.bKeyPressed = true;
					//отметим в структуре, что нажали очередную какую-то клавишу
					board->m_kprs.nScanCode = nScanCode;
					board->m_kprs.bProcessed = false;
					TRACE1("push vkbd char %d\n", nScanCode);
				}

				break;

			case BKKeyType::BKKEY_LSHIFT:
			case BKKeyType::BKKEY_RSHIFT:
				mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_SHIFT, true);
				break;

			case BKKeyType::BKKEY_CTRL:
				mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_SU, true);
				break;

			case BKKeyType::BKKEY_ALT:
				mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_AR2, true);
				break;

			case BKKeyType::BKKEY_ZAGL:
				if (GetKeyState(VK_CAPITAL) & 1) //если капслок включен
				{
					keybd_event(VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);               // Нажать и отпустить клавишу
					keybd_event(VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); //
					//Это вызовет событие OnKeyDown и обработается там
				}

				break;

			case BKKeyType::BKKEY_STR:
				if (!(GetKeyState(VK_CAPITAL) & 1)) //если капслок выключен
				{
					keybd_event(VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);               // Нажать и отпустить клавишу
					keybd_event(VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); //
					//Это вызовет событие OnKeyDown и обработается там
				}

				break;

			case BKKeyType::BKKEY_STOP:
				board->StopInterrupt(); //нажали на кнопку стоп
				break;
		}
	}

	return S_OK;
}

LRESULT CBKView::OnVirtualKeyUp(WPARAM wParam, LPARAM lParam)
{
	auto mw = DYNAMIC_DOWNCAST(CMainFrame, GetParentFrame());

	if (mw)
	{
		register auto board = mw->GetChip();

		switch (wParam)
		{
			case BKKeyType::BKKEY_REGULAR:

				// отметим в структуре. что отжали очередную какую-то клавишу
				if (board->m_kprs.nScanCode != -1)
				{
					BYTE nScanCode = LOBYTE(lParam);

					if (board->m_kprs.nScanCode == nScanCode)
					{
						TRACE1("processed vkbd char %d\n", nScanCode);
						board->m_kprs.bProcessed = true;
					}

					switch (nScanCode)
					{
						case BKKEY_LAT:
							mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_XLAT, false);
							break;

						case BKKEY_RUS:
							mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_XLAT, true);
							break;
					}
				}

				break;

			case BKKeyType::BKKEY_LSHIFT:
			case BKKeyType::BKKEY_RSHIFT:
				mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_SHIFT, false);
				break;

			case BKKeyType::BKKEY_CTRL:
				mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_SU, false);
				break;

			case BKKeyType::BKKEY_ALT:
				mw->GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_AR2, false);
				break;

			case BKKeyType::BKKEY_STOP:
				board->UnStopInterrupt(); //отжали кнопку стоп
				break;
		}
	}

	return S_OK;
}



void CBKView::OnClose()
{
	if (g_hHook)
	{
		BOOL bRes = UnhookWindowsHookEx(g_hHook);
		ASSERT(bRes);
	}

	CView::OnClose();
}


void CBKView::OnSetFocus(CWnd *pOldWnd)
{
	//m_pScreen->SetFocus(); //фокусируемся на экране (это не обязательно)
	CView::OnSetFocus(pOldWnd); //можно и на себе  фокус оставлять
	g_bAppFocused = true;
}


void CBKView::OnKillFocus(CWnd *pNewWnd)
{
	CView::OnKillFocus(pNewWnd);
	g_bAppFocused = false;
}
