
// MemDumpDlg.cpp : файл реализации
//

#include "pch.h"
#include "afxdialogex.h"
#include "resource.h"
#include "MemDumpDlg.h"
#include "Config.h"
#include "Debugger.h"
#include "Platform.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// диалоговое окно CMemDumpDlg
DISPLAY_MODE_PARAM CMemDumpDlg::m_dmArray[NUMBER_DISPLAY_MODE] =
{
	{ DUMP_DISPLAY_MODE::DD_WORD_VIEW, _T("WORD") },
	{ DUMP_DISPLAY_MODE::DD_BYTE_VIEW, _T("BYTE") }
};

CMemDumpDlg::CMemDumpDlg(CWnd *pParent /*=nullptr*/)
	: CDialogEx(CMemDumpDlg::IDD, pParent)
	, m_nCurrentDDM(0)
	, m_hAccelTable(nullptr)
{
}

CMemDumpDlg::~CMemDumpDlg()
{
	hFont.DeleteObject();
}

CSize CMemDumpDlg::GetMinSize()
{
	CSize size;
	MINMAXINFO mmi;
	m_Resizer.GetMinMaxInfo(&mmi);
	size.cx = mmi.ptMinTrackSize.x;
	size.cy = mmi.ptMinTrackSize.y;
	return size;
}

void CMemDumpDlg::DoDataExchange(CDataExchange *pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_MD_MEMORY, m_listMemory);
	DDX_Control(pDX, IDC_DA_DUMP_ADDRESS, m_AddrEdit);
}

BEGIN_MESSAGE_MAP(CMemDumpDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_GETMINMAXINFO()
	ON_WM_SETFOCUS()
	ON_MESSAGE(WM_DBG_TOP_ADDRESS_UPDATE, &CMemDumpDlg::OnDumpAddressChange)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_MD_MEMORY, &CMemDumpDlg::OnLvnItemchangedMdMemory)
	ON_BN_CLICKED(IDC_BUTTON_DUMP_MODE, &CMemDumpDlg::OnBnClickedButtonDumpMode)
END_MESSAGE_MAP()

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

BOOL CMemDumpDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	m_hAccelTable = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
	LOGFONT lf;
	::GetObject((HGDIOBJ)::SendMessage(GetSafeHwnd(), WM_GETFONT, 0, 0), sizeof(LOGFONT), &lf);
	_tcscpy_s(lf.lfFaceName, LF_FACESIZE, _T("Courier New\0"));

	if (!hFont.CreateFontIndirect(&lf))
	{
		_tcscpy_s(lf.lfFaceName, LF_FACESIZE, _T("Lucida Console\0"));
		hFont.CreateFontIndirect(&lf);
	}

	m_listMemory.SetFont(&hFont, FALSE);
	m_Resizer.Init(this);
	m_Resizer.AddCtrl(IDC_MD_MEMORY, DT_LEFT | DT_TOP | DT_RIGHT | DT_BOTTOM, FALSE, FALSE);
	m_Resizer.AddCtrl(IDC_DA_DUMP_ADDRESS, DT_LEFT | DT_TOP, TRUE, TRUE);
	m_Resizer.AddCtrl(IDC_BUTTON_DUMP_MODE, DT_LEFT | DT_TOP, TRUE, TRUE);
	m_AddrEdit.SetLimitText(6);
	SetDlgItemText(IDC_BUTTON_DUMP_MODE, m_dmArray[m_nCurrentDDM].name); // на кнопке нарисуем начальное значение
	m_listMemory.Init(m_dmArray[m_nCurrentDDM].mode);
	return TRUE;  // возврат значения TRUE, если фокус не передан элементу управления
}

// отменяем действие кнопок enter и esc в диалоге
void CMemDumpDlg::OnOK()
{
}

void CMemDumpDlg::OnCancel()
{
}

void CMemDumpDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);
	m_Resizer.OnSize(cx, cy);
}


void CMemDumpDlg::OnGetMinMaxInfo(MINMAXINFO *lpMMI)
{
	m_Resizer.GetMinMaxInfo(lpMMI);
}

void CMemDumpDlg::OnSetFocus(CWnd *pOldWnd)
{
	CDialogEx::OnSetFocus(pOldWnd);
	m_listMemory.SetFocus();
}

LRESULT CMemDumpDlg::OnDumpAddressChange(WPARAM wParam, LPARAM lParam)
{
	CString strBuf;
	GetDlgItemText(IDC_DA_DUMP_ADDRESS, strBuf.GetBufferSetLength(10), 10);
	strBuf.ReleaseBuffer();
	uint16_t nAddr = OctStringToWord(strBuf);
	// обновим значение, чтобы оно было всегда 6 значным
	SetDlgItemText(IDC_DA_DUMP_ADDRESS, WordToOctString(nAddr));
	m_listMemory.RefreshItems(nAddr, true);
	return S_OK;
}


void CMemDumpDlg::OnLvnItemchangedMdMemory(NMHDR *pNMHDR, LRESULT *pResult)
{
	auto pNMListView = reinterpret_cast<LPNMLISTVIEW >(pNMHDR);
	int nSubItem = pNMListView->iSubItem;
	int nItem = pNMListView->iItem;

	switch (m_listMemory.GetDisplayMode())
	{
		case DUMP_DISPLAY_MODE::DD_WORD_VIEW:
		{
			if (CDumpListCtrl::IDS_MEMORY_COLUMN1_WORD <= nSubItem && nSubItem <= CDumpListCtrl::IDS_MEMORY_COLUMN4_WORD)
			{
				register int addr = m_listMemory.GetDumpAddress() + nItem * 010;
				register int nPos = (nSubItem - 1) * 2; // 2 - кол-во charов в wordе
				addr += nPos;
				register auto pstr = reinterpret_cast<CString *>(pNMListView->lParam);
				register uint16_t value = OctStringToWord(*pstr);
				CString str = m_listMemory.GetItemText(nItem, CDumpListCtrl::IDS_MEMORY_ASCII_WORD);
				str.SetAt(nPos++, m_listMemory.GetMemDumpByteAsANSI(LOBYTE(value)));
				str.SetAt(nPos,   m_listMemory.GetMemDumpByteAsANSI(HIBYTE(value)));
				m_listMemory.SetEndEdit(nItem, nSubItem);
				g_Platform.GetDebuggerPtr()->OnDebugModify_Memory(addr, value);
				m_listMemory.SetItemWithModified(value, nItem, nSubItem);
				m_listMemory.SetItemWithModifiedASCII(str, nItem, CDumpListCtrl::IDS_MEMORY_ASCII_WORD);
			}

			break;
		}

		case DUMP_DISPLAY_MODE::DD_BYTE_VIEW:
		{
			if (CDumpListCtrl::IDS_MEMORY_COLUMN1_BYTE <= nSubItem && nSubItem <= CDumpListCtrl::IDS_MEMORY_COLUMN8_BYTE)
			{
				register int addr = m_listMemory.GetDumpAddress() + nItem * 010;
				register int nPos = (nSubItem - 1);
				addr += nPos;
				register auto pstr = reinterpret_cast<CString *>(pNMListView->lParam);
				register uint8_t value = LOBYTE(OctStringToWord(*pstr));
				register TCHAR ch = m_listMemory.GetMemDumpByteAsANSI(value);
				CString str = m_listMemory.GetItemText(nItem, CDumpListCtrl::IDS_MEMORY_ASCII_BYTE);
				str.SetAt(nPos, ch);
				m_listMemory.SetEndEdit(nItem, nSubItem);
				g_Platform.GetDebuggerPtr()->OnDebugModify_Memory(addr, value);
				m_listMemory.SetItemWithModifiedByte(value, nItem, nSubItem);
				m_listMemory.SetItemWithModifiedASCII(str, nItem, CDumpListCtrl::IDS_MEMORY_ASCII_BYTE);
			}

			break;
		}
	}

	*pResult = S_OK;
}

void CMemDumpDlg::OnBnClickedButtonDumpMode()
{
	if (++m_nCurrentDDM >= NUMBER_DISPLAY_MODE)
	{
		m_nCurrentDDM = 0;
	}

	SetDlgItemText(IDC_BUTTON_DUMP_MODE, m_dmArray[m_nCurrentDDM].name);
	m_listMemory.SetDisplayMode(m_dmArray[m_nCurrentDDM].mode);
}

uint16_t CMemDumpDlg::GetDebugMemDumpWord(uint16_t dumpAddress)
{
	return g_Platform.GetDebuggerPtr()->GetDebugMemDumpWord(dumpAddress);
}
uint8_t CMemDumpDlg::GetDebugMemDumpByte(uint16_t dumpAddress)
{
	return g_Platform.GetDebuggerPtr()->GetDebugMemDumpByte(dumpAddress);
}

// для того, чтобы акселераторы работали, их надо загрузить и в этот диалог
// и передавать главному фрейму.
// способа проще найти не получилось.
BOOL CMemDumpDlg::PreTranslateMessage(MSG *pMsg)
{
	if (m_hAccelTable)
	{
		if (::TranslateAccelerator(AfxGetMainWnd()->GetSafeHwnd(), m_hAccelTable, pMsg))
		{
			return TRUE;
		}
	}

	return CDialogEx::PreTranslateMessage(pMsg);
}
