// DisasmCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"
#include "DisasmCtrl.h"
#include "Debugger.h"
#include "Platform.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDisasmCtrl

CDisasmCtrl::CDisasmCtrl() :
	m_nSelection(-1)
{
}

CDisasmCtrl::~CDisasmCtrl()
{
}

BEGIN_MESSAGE_MAP(CDisasmCtrl, CListCtrl)
	ON_WM_SIZE()
	ON_WM_SETFOCUS()
	ON_WM_MOUSEWHEEL()
	ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, &CDisasmCtrl::OnItemchanged)
	ON_NOTIFY_REFLECT(LVN_KEYDOWN, &CDisasmCtrl::OnKeydown)
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CDisasmCtrl::OnCustomDraw)
	ON_NOTIFY_REFLECT(NM_DBLCLK, &CDisasmCtrl::OnNMDblclk)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDisasmCtrl message handlers

void CDisasmCtrl::Init()
{
	ModifyStyle(0, LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | SBS_VERT);
	SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER | LVS_EX_SNAPTOGRID);
//  CRect rcDebug;
//  GetClientRect(&rcDebug);
	// Create Header
	int nPixelW = GetDeviceCaps(GetDC()->GetSafeHdc(), LOGPIXELSX);
	CString strHeader;
	strHeader.LoadString(IDS_DEBUG_MARK);
	InsertColumn(MARK_COLUMN, strHeader, LVCFMT_LEFT, MulDiv(20, nPixelW, 96));
	strHeader.LoadString(IDS_DEBUG_ADDRESS);
	InsertColumn(ADDR_COLUMN, strHeader, LVCFMT_LEFT, MulDiv(50, nPixelW, 96));
	strHeader.LoadString(IDS_DEBUG_COMMAND);
	InsertColumn(INSTR_COLUMN, strHeader, LVCFMT_LEFT, MulDiv(200, nPixelW, 96));
	strHeader.LoadString(IDS_DEBUG_COMMENTS);
	InsertColumn(COM_COLUMN, strHeader, LVCFMT_LEFT, MulDiv(150, nPixelW, 96));
	OutContent();
	g_Platform.GetDebuggerPtr()->InitMarker();
}

void CDisasmCtrl::OnSize(UINT nType, int cx, int cy)
{
	CListCtrl::OnSize(nType, cx, cy);
	OutContent();
}

void CDisasmCtrl::OutContent()
{
	if (m_lock.try_lock())  // Lock for recurse call preserve
	{
		// Delete All Items
		DeleteAllItems();
		int nCount = GetCountPerPage();

		if (nCount != -1)
		{
			// Insert new items
			for (int i = 0; i < nCount; ++i)
			{
				InsertItem(i, LPSTR_TEXTCALLBACK);

				if (i == m_nSelection)
				{
					SetItemState(m_nSelection, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
				}
			}
		}

		// Unlock
		m_lock.unlock();
	}
}

void CDisasmCtrl::OnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
	auto pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);

	// Change selection marker
	if (pNMListView->iItem != -1 && pNMListView->uNewState & LVIS_SELECTED)
	{
		m_nSelection = pNMListView->iItem;
	}

	*pResult = S_OK;
}


void CDisasmCtrl::OnKeydown(NMHDR *pNMHDR, LRESULT *pResult)
{
	auto pLVKeyDow = reinterpret_cast<LPNMLVKEYDOWN>(pNMHDR);
	int nLastItem = GetItemCount() - 1;
	int nSel = GetSelectionMark();
	int nCount = GetCountPerPage();

	if (nSel == 0)
	{
		if (pLVKeyDow->wVKey == VK_UP)
		{
			// если мы вверху и нажали кнопку вверх
			GetParent()->SendMessage(WM_DBG_DISASM_STEP_UP);
		}
		else if (pLVKeyDow->wVKey == VK_PRIOR)
		{
			// если мы вверху и нажали кнопку pgup
			GetParent()->SendMessage(WM_DBG_DISASM_PG_UP, WPARAM(nCount));
		}

		RedrawItems(0, nLastItem); // и всё перерисовываем
	}
	else if (nSel == nLastItem)
	{
		if (pLVKeyDow->wVKey == VK_DOWN)
		{
			// если мы внизу и нажали кнопку вниз
			GetParent()->SendMessage(WM_DBG_DISASM_STEP_DN);
		}
		else if (pLVKeyDow->wVKey == VK_NEXT)
		{
			// если мы внизу и нажали кнопку pgdn
			GetParent()->SendMessage(WM_DBG_DISASM_PG_DN, WPARAM(nCount));
		}

		RedrawItems(0, nLastItem); // и всё перерисовываем
	}

	*pResult = S_OK;
}


BOOL CDisasmCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	int nLastItem = GetItemCount() - 1;

	if (zDelta < 0)
	{
		GetParent()->SendMessage(WM_DBG_DISASM_STEP_DN);
	}
	else if (zDelta > 0)
	{
		GetParent()->SendMessage(WM_DBG_DISASM_STEP_UP);
	}

	RedrawItems(0, nLastItem); // и всё перерисовываем
	return TRUE;
}

void CDisasmCtrl::OnNMDblclk(NMHDR *pNMHDR, LRESULT *pResult)
{
	auto pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);

	if (pNMItemActivate->iSubItem == 0) // если кликнули по самому первому столбцу
	{
		GetParent()->SendMessage(WM_DBG_DISASM_CHECK_BP, WPARAM(pNMItemActivate->iItem));
		Invalidate(FALSE);
	}

	*pResult = 0;
}

void CDisasmCtrl::OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult)
{
	auto pLVCD = reinterpret_cast<NMLVCUSTOMDRAW *>(pNMHDR);
	*pResult = CDRF_DODEFAULT;

	switch (pLVCD->nmcd.dwDrawStage)
	{
		case CDDS_PREPAINT:
			*pResult = CDRF_NOTIFYITEMDRAW;
			break;

		case CDDS_ITEMPREPAINT:
		{
			// меняем цвет курсора выделения
			auto nIndex = int(pLVCD->nmcd.dwItemSpec);

			if (GetItemState(nIndex, LVIS_SELECTED | LVIS_FOCUSED)
			        && ::GetFocus() == pLVCD->nmcd.hdr.hwndFrom)
			{
				pLVCD->clrTextBk = RGB(0xff, 0xff, 0); // RGB(0, 0xcc, 0xff);
				SetItemState(nIndex, 0, LVIS_SELECTED);
			}

			*pResult = CDRF_NOTIFYPOSTPAINT;
		}
		break;

//      case (CDDS_ITEMPREPAINT | CDDS_SUBITEM):
//          // здесь можно поменять текст конкретного итема
//          // только это всё равно медленнее работает, чем прорисовать всю строку за раз
//          switch (pLVCD->iSubItem)
//          {
//          case MARK_COLUMN:
//
//              break;
//          case ADDR_COLUMN:
//                  break;
//          case INSTR_COLUMN:
//              break;
//          }
//          break;

		case CDDS_ITEMPOSTPAINT:
			// Get item index
			auto nIndex = int(pLVCD->nmcd.dwItemSpec);
			// Get subitems rectangles
			GetSubItemRect(nIndex, MARK_COLUMN, LVIR_LABEL, m_rcSubs[MARK_COLUMN]);
			GetSubItemRect(nIndex, ADDR_COLUMN, LVIR_BOUNDS, m_rcSubs[ADDR_COLUMN]);
			m_rcSubs[ADDR_COLUMN].left += 4; m_rcSubs[ADDR_COLUMN].right -= 4;
			GetSubItemRect(nIndex, INSTR_COLUMN, LVIR_BOUNDS, m_rcSubs[INSTR_COLUMN]);
			m_rcSubs[INSTR_COLUMN].left += 4; m_rcSubs[INSTR_COLUMN].right -= 4;
			GetSubItemRect(nIndex, COM_COLUMN, LVIR_BOUNDS, m_rcSubs[COM_COLUMN]);
			m_rcSubs[COM_COLUMN].left += 4; m_rcSubs[COM_COLUMN].right -= 4;
			g_Platform.GetDebuggerPtr()->DrawDebuggerLine(nIndex, CDC::FromHandle(pLVCD->nmcd.hdc), m_rcSubs);
			*pResult = CDRF_SKIPDEFAULT;
			break;
	}
}

void CDisasmCtrl::OnSetFocus(CWnd *pOldWnd)
{
	CListCtrl::OnSetFocus(pOldWnd);
	Invalidate(FALSE);
}

