// sxbmevw.cpp : implementation of the CSxbmemView class
//

#include "stdafx.h"
#include "sxbmem.h"

#include "sxbmedoc.h"
#include "memview.h"
#include "sxbmevw.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

extern CSxbmemApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CSxbmemView

IMPLEMENT_DYNCREATE(CSxbmemView, CFormView)

BEGIN_MESSAGE_MAP(CSxbmemView, CFormView)
	//{{AFX_MSG_MAP(CSxbmemView)
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_Refresh, OnRefresh)
	ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
	ON_COMMAND(ID_EDIT_CUT, OnEditCut)
	ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
	ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
	ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
	ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
	ON_CBN_SELCHANGE(IDC_StructView, OnSelchangeStructView)
	ON_WM_ERASEBKGND()
	ON_CBN_SELCHANGE(IDC_Format, OnSelchangeFormat)
	ON_CBN_EDITCHANGE(IDC_Addr, OnEditchangeAddr)
	ON_CBN_SETFOCUS(IDC_Addr, OnSetfocusAddr)
	ON_CBN_KILLFOCUS(IDC_Addr, OnKillfocusAddr)
	ON_CBN_SELCHANGE(IDC_Addr, OnSelchangeAddr)
	ON_EN_UPDATE(IDC_Size, OnUpdateSize)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCopy)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSxbmemView construction/destruction

CSxbmemView::CSxbmemView()
	: CFormView(CSxbmemView::IDD)
{
	//{{AFX_DATA_INIT(CSxbmemView)
	m_RefreshRate = 1;
	m_Size = 1024;
	m_MemAddr = _T("");
	m_Format = -1;
	//}}AFX_DATA_INIT
	// TODO: add construction code here
}

CSxbmemView::~CSxbmemView()
{
	theApp.WriteProfileInt("Settings", "Size", m_Size);
	if (m_MemAddr == "")
		m_MemAddr = '0';
	theApp.WriteProfileString("Settings", "Addr", m_MemAddr);
}

void CSxbmemView::DoDataExchange(CDataExchange* pDX)
{
	CFormView::DoDataExchange(pDX);

	//{{AFX_DATA_MAP(CSxbmemView)
	DDX_Control(pDX, IDC_Addr, m_MemAddrBox);
	DDX_Control(pDX, IDC_StructView, m_StructViewBox);
	DDX_Text(pDX, IDC_RefreshRate, m_RefreshRate);
	DDV_MinMaxDWord(pDX, m_RefreshRate, 0, 10000);
	DDX_Text(pDX, IDC_Size, m_Size);
	DDV_MinMaxDWord(pDX, m_Size, 0, 16384);
	DDX_CBString(pDX, IDC_Addr, m_MemAddr);
	DDX_CBIndex(pDX, IDC_Format, m_Format);
	//}}AFX_DATA_MAP
	if (pDX->m_bSaveAndValidate) {
		int index = m_MemAddrBox.FindString(-1, m_MemAddr);
		if (index != CB_ERR) {
			const CSymbol *symb = (CSymbol *)m_MemAddrBox.GetItemDataPtr(index);
			m_MemAddress = symb->m_Address;
			}
		else {
			m_MemAddress = strtoul(m_MemAddr, NULL, 16);
			if (m_MemAddress < 0 || m_MemAddress > UINT_MAX - m_Size) {
				CString msg;

				msg.Format("Please enter a number in the range: 0 - 0x%X", UINT_MAX - m_Size);
				AfxMessageBox(msg);
				pDX->Fail();
				}
			}
		}
}

/////////////////////////////////////////////////////////////////////////////
// CSxbmemView diagnostics

#ifdef _DEBUG
void CSxbmemView::AssertValid() const
{
	CFormView::AssertValid();
}

void CSxbmemView::Dump(CDumpContext& dc) const
{
	CFormView::Dump(dc);
}

CSxbmemDoc* CSxbmemView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSxbmemDoc)));
	return (CSxbmemDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CSxbmemView message handlers

void CSxbmemView::OnInitialUpdate() 
{
	// TODO: Add your specialized code here and/or call the base class
	CSxbmemDoc *doc = GetDocument();
	m_MemAddr = doc->m_MemAddr;
	m_MemAddress = doc->m_MemAddress;
	m_Size = doc->m_Size;
	m_Format = doc->m_Format;
	CFormView::OnInitialUpdate();

	SetTimer(5, m_RefreshRate * 100, NULL);

	theApp.AddSymbols(m_MemAddrBox);
	if (m_MemAddrBox.FindString(-1, m_MemAddr) != CB_ERR)
		m_MemAddrBox.SelectString(-1, m_MemAddr);
	else {
		HGLOBAL glob = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, m_MemAddr.GetLength()+1);
		LPSTR text = (LPSTR)GlobalLock(glob);
		strcpy(text, m_MemAddr);
		GlobalUnlock(glob);

		OpenClipboard();
		SetClipboardData(CF_TEXT, text);
		CloseClipboard();

		m_MemAddrBox.Paste();
		}		

	theApp.AddDefs(m_StructViewBox);
	m_StructViewBox.SelectString(-1, doc->m_DefName);

	theApp.GetMemory(m_MemAddress, doc->m_Buffer, &m_Size);
}

void CSxbmemView::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default
	
	CFormView::OnTimer(nIDEvent);

	if (nIDEvent == 5) {
		BYTE *buffer = new BYTE[m_Size];
		BOOL valid = m_MemAddress >= 0 && m_MemAddress <= UINT_MAX - m_Size;

		DWORD size = m_Size;
		if (valid && theApp.GetMemory(m_MemAddress, buffer, &size) && size == m_Size) {
			CSxbmemDoc *doc = GetDocument();

			BOOL needupdate = memcmp(GetDocument()->m_Buffer, buffer, m_Size) != 0 ||
			                  m_MemAddress != doc->m_MemAddress || 
			                  doc->m_Format != m_Format ||
			                  m_Size != doc->m_Size;

			if (needupdate) {
				memcpy(doc->m_Buffer, buffer, m_Size);
				doc->m_MemAddress = m_MemAddress;
				doc->m_MemAddr = m_MemAddr;
				doc->m_Size = m_Size;
				doc->m_Format = m_Format;
				doc->UpdateAllViews(this, uavDisplayMemory);
				}
			}

		delete[] buffer;
		}
}

void CSxbmemView::OnRefresh() 
{
	// TODO: Add your control notification handler code here
	if (UpdateData(TRUE)) {
		KillTimer(5);
		if (m_RefreshRate)
			SetTimer(5, m_RefreshRate * 100, NULL);
		OnTimer(5);
		}	
}

CEdit *IsValid()
{
	CEdit *wnd = (CEdit *)CWnd::GetFocus();

	char name[32];
	::GetClassName(wnd->GetSafeHwnd(), name, 32);

	return strcmp(name, "Edit") == 0 ? wnd : NULL;
}

void CSxbmemView::OnEditCopy() 
{
	// TODO: Add your command handler code here
	if (IsValid())
		IsValid()->Copy();
}

void CSxbmemView::OnUpdateEditCopy(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	CEdit *wnd = IsValid();
	BOOL valid = wnd != NULL;

	if (valid) {
		int nStartChar, nEndChar;
		wnd->GetSel(nStartChar, nEndChar);
		valid = nStartChar != nEndChar;
		}

	pCmdUI->Enable(valid);
}

void CSxbmemView::OnEditCut() 
{
	// TODO: Add your command handler code here
	if (IsValid())
		IsValid()->Cut();
}

void CSxbmemView::OnEditPaste() 
{
	// TODO: Add your command handler code here
	if (IsValid())
		IsValid()->Paste();
}

void CSxbmemView::OnUpdateEditPaste(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	pCmdUI->Enable(IsValid() != NULL);
}

void CSxbmemView::OnEditUndo() 
{
	// TODO: Add your command handler code here
	if (IsValid())
		IsValid()->Undo();
}

void CSxbmemView::OnUpdateEditUndo(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
	CEdit *wnd = IsValid();
	BOOL valid = wnd != NULL;

	valid = valid && wnd->CanUndo();

	pCmdUI->Enable(valid);
}

void CSxbmemView::OnSelchangeStructView() 
{
	// TODO: Add your control notification handler code here
	CString sel;

	m_StructViewBox.GetLBText(m_StructViewBox.GetCurSel(), sel);
	CSplitterWnd *swnd = (CSplitterWnd *)GetParent();

	CView *oldview = (CView *)swnd->GetPane(1, 0);
	CRect rect;
	oldview->GetWindowRect(rect);
	BOOL istree = oldview->IsKindOf(RUNTIME_CLASS(CTreeView));

	GetDocument()->m_DefName = sel;
	GetDocument()->m_Def = (CDef *)m_StructViewBox.GetItemDataPtr(m_StructViewBox.GetCurSel());

	if (sel == "Raw bytes") {
		GetDlgItem(IDC_Size)->EnableWindow();
		if (istree) {
//			GetDocument()->RemoveView(oldview);
			swnd->DeleteView(1, 0);
			swnd->CreateView(1, 0, RUNTIME_CLASS(CMemView), rect.Size(), NULL);
			}
		}
	else {
		m_Size = GetDocument()->m_Def->m_Size;
		UpdateData(FALSE);
		GetDlgItem(IDC_Size)->EnableWindow(FALSE);
		if (!istree) {
//			GetDocument()->RemoveView(oldview);
			swnd->DeleteView(1, 0);
			swnd->CreateView(1, 0, RUNTIME_CLASS(CMemTree), rect.Size(), NULL);
			}
		else {
			CMemTree *newview = (CMemTree *)swnd->GetPane(1, 0);
			newview->CMemTree::OnInitialUpdate();
			}
		}
	swnd->RecalcLayout();
}

void RemoveRectRgn(CRgn& rgn, CWnd *sheet, CWnd *wnd)
{
	CRect rect;
	wnd->GetWindowRect(rect);
	sheet->ScreenToClient(rect);
	CRgn region;
	region.CreateRectRgnIndirect(rect);

	rgn.CombineRgn(&rgn, &region, RGN_DIFF);
}

BOOL CSxbmemView::OnEraseBkgnd(CDC* pDC) 
{
	// TODO: Add your message handler code here and/or call default
	CRect rct;
	GetClientRect(rct);
	
	if (!rct.IsRectEmpty()) {
		CRgn rgn;
		rgn.CreateRectRgnIndirect(rct);

		for (CWnd *wnd = GetWindow(GW_CHILD); wnd; wnd = wnd->GetWindow(GW_HWNDNEXT)) 
			if (wnd->IsWindowVisible())
				RemoveRectRgn(rgn, this, wnd);

		CBrush b;
		b.CreateStockObject(LTGRAY_BRUSH);
		pDC->FillRgn(&rgn, &b);
		}
	return rct.IsRectEmpty() ? CFormView::OnEraseBkgnd(pDC) : TRUE;
}

void CSxbmemView::OnSelchangeFormat() 
{
	// TODO: Add your control notification handler code here
	int oldformat = m_Format;

	if (UpdateData(TRUE)) {
		GetDocument()->m_Format = m_Format;
		GetDocument()->UpdateAllViews(this, uavDisplayMemory);
		}
	else {
		m_Format = oldformat;
		UpdateData(FALSE);
		}
}

void CSxbmemView::OnEditchangeAddr() 
{
	// TODO: Add your control notification handler code here
	CString txt = m_MemAddr;

	if (!UpdateData(TRUE)) {
		m_MemAddr = txt;
		UpdateData(FALSE);
		}
	else {
		int index = m_MemAddrBox.FindString(-1, m_MemAddr);

		if (index != CB_ERR) {
			int len = m_MemAddr.GetLength();

			m_MemAddrBox.GetLBText(index, m_MemAddr);
			UpdateData(FALSE);
			m_MemAddrBox.SetEditSel(len, m_MemAddr.GetLength());
			}
		}
}

BOOL CSxbmemView::PreTranslateMessage(MSG* pMsg) 
{
	// TODO: Add your specialized code here and/or call the base class
	if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_DELETE ||
			pMsg->wParam == VK_BACK))
		if (m_HasFocus) {
			DWORD sel = m_MemAddrBox.GetEditSel();
			
			if (LOWORD(sel) > 0)
				m_MemAddrBox.SetEditSel(LOWORD(sel) - 1, HIWORD(sel));
			}
	
	return CFormView::PreTranslateMessage(pMsg);
}

void CSxbmemView::OnSetfocusAddr() 
{
	// TODO: Add your control notification handler code here
	m_HasFocus = TRUE;
}

void CSxbmemView::OnKillfocusAddr() 
{
	// TODO: Add your control notification handler code here
	m_HasFocus = FALSE;
}

void CSxbmemView::OnSelchangeAddr() 
{
	// TODO: Add your control notification handler code here
	int sel = m_MemAddrBox.GetCurSel();

	if (sel > CB_ERR) {
		m_MemAddrBox.GetLBText(sel, m_MemAddr);
		UpdateData(FALSE);
		OnRefresh();
		}
}

void CSxbmemView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	// TODO: Add your specialized code here and/or call the base class
	if (lHint == 0)
		CFormView::OnUpdate(pSender, lHint, pHint);
}

void CSxbmemView::OnUpdateSize() 
{
	// TODO: Add your control notification handler code here
	DWORD size = m_Size;
	if (!UpdateData(TRUE)) {
		m_Size = size;
		UpdateData(FALSE);
		}
}
