// DisasmCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"
//#include "Config.h"
#include "DisasmCtrl.h"
#include "Debugger.h"

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

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

CDisasmCtrl::CDisasmCtrl() :
	m_pDebugger(nullptr),
	m_nSelection(-1)
{
}

CDisasmCtrl::~CDisasmCtrl()
{
}

BEGIN_MESSAGE_MAP(CDisasmCtrl, CListCtrl)
	ON_WM_SIZE()
	ON_WM_SETFOCUS()
	ON_WM_MOUSEWHEEL()
	ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, &OnItemchanged)
	ON_NOTIFY_REFLECT(LVN_KEYDOWN, &OnKeydown)
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &OnCustomDraw)
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(0, strHeader, LVCFMT_LEFT, MulDiv(20, nPixelW, 96));
	strHeader.LoadString(IDS_DEBUG_ADDRESS);
	InsertColumn(1, strHeader, LVCFMT_LEFT, MulDiv(60, nPixelW, 96));
	strHeader.LoadString(IDS_DEBUG_COMMAND);
	InsertColumn(2, strHeader, LVCFMT_LEFT, MulDiv(60, nPixelW, 96));
	strHeader.LoadString(IDS_DEBUG_ARGUMENTS);
	InsertColumn(3, strHeader, LVCFMT_LEFT, MulDiv(120, nPixelW, 96));
	strHeader.LoadString(IDS_DEBUG_COMMENTS);
	InsertColumn(4, strHeader, LVCFMT_LEFT, MulDiv(120, nPixelW, 96));
	OutContent();
}

void CDisasmCtrl::AttachDebugger(CDebugger *pDebugger)
{
	m_pDebugger = pDebugger;
}

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

void CDisasmCtrl::OutContent()
{
	if (!m_Lock.IsLocked())
	{
		// Lock for recurce call preserve
		m_Lock.Lock();
		int nCount = GetCountPerPage();
		// Delete All Items
		DeleteAllItems();

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

		if (m_pDebugger)
		{
			m_pDebugger->SetLinesNum(nCount);
		}

		// 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)
		{
			//если мы вверху и нажали кнопку вверх
			if (m_pDebugger) //сдвигаемся вверх
			{
				m_pDebugger->StepBackward();
			}

			RedrawItems(0, nLastItem); //и всё перерисовываем
			::SendMessage(GetParent()->GetSafeHwnd(), WM_DEBUG_ADDRESS_UPDATE, 0, 0);
		}
		else if (pLVKeyDow->wVKey == VK_PRIOR)
		{
			//если мы вверху и нажали кнопку pgup
			if (m_pDebugger) //сдвигаемся вверх
			{
				while (--nCount > 0)
				{
					m_pDebugger->StepBackward();
				}
			}

			RedrawItems(0, nLastItem); //и всё перерисовываем
			::SendMessage(GetParent()->GetSafeHwnd(), WM_DEBUG_ADDRESS_UPDATE, 0, 0);
		}
	}
	else if (nSel == nLastItem)
	{
		if (pLVKeyDow->wVKey == VK_DOWN)
		{
			//если мы внизу и нажали кнопку вниз
			if (m_pDebugger) //сдвигаемся вниз
			{
				m_pDebugger->StepForward();
			}

			RedrawItems(0, nLastItem); //и всё перерисовываем
			::SendMessage(GetParent()->GetSafeHwnd(), WM_DEBUG_ADDRESS_UPDATE, 0, 0);
		}
		else if (pLVKeyDow->wVKey == VK_NEXT)
		{
			//если мы внизу и нажали кнопку pgdn
			if (m_pDebugger) //сдвигаемся вниз
			{
				while (--nCount > 0)
				{
					m_pDebugger->StepForward();
				}
			}

			RedrawItems(0, nLastItem); //и всё перерисовываем
			::SendMessage(GetParent()->GetSafeHwnd(), WM_DEBUG_ADDRESS_UPDATE, 0, 0);
		}
	}

	*pResult = S_OK;
}


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

	if (zDelta < 0)
	{
		if (m_pDebugger) //сдвигаемся вниз
		{
			m_pDebugger->StepForward();
		}

		RedrawItems(0, nLastItem); //и всё перерисовываем
		::SendMessage(GetParent()->GetSafeHwnd(), WM_DEBUG_ADDRESS_UPDATE, 0, 0);
	}
	else if (zDelta > 0)
	{
		if (m_pDebugger) //сдвигаемся вверх
		{
			m_pDebugger->StepBackward();
		}

		RedrawItems(0, nLastItem); //и всё перерисовываем
		::SendMessage(GetParent()->GetSafeHwnd(), WM_DEBUG_ADDRESS_UPDATE, 0, 0);
	}

	return TRUE;
	//return CListCtrl::OnMouseWheel(nFlags, zDelta, pt);
}


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:
			if (m_pDebugger)
			{
				// 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, ARG_COLUMN, LVIR_BOUNDS, m_rcSubs[ARG_COLUMN]);
				m_rcSubs[ARG_COLUMN].left += 4; m_rcSubs[ARG_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;
				m_pDebugger->DrawDebuggerLine(nIndex, CDC::FromHandle(pLVCD->nmcd.hdc), m_rcSubs);

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

				*pResult = CDRF_SKIPDEFAULT;
			}

			break;
	}
}

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

