/***************************************************************************
**
**    $Header:   D:/EP196/SRC/LOG/SRCDLL/SRCBP.CPP   1.0   15 Aug 1997 14:03:40   ZJRD  $
**
**    $Log:   D:/EP196/SRC/LOG/SRCDLL/SRCBP.CPP  $
** 
**    Rev 1.0   15 Aug 1997 14:03:40   ZJRD
** Initial revision.
** 
****************************************************************************/

// srcbp.cpp : implementation file
//

#include "stdafx.h"
#include "srcdll.h"
#include "srcgbl.h"
#include "srcsht.h"
#include "srcaddr.h"
#include "srcbps.h"
#include "srcbpr.h"

#include "bptexp.h"
#include "bptdata.h"

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

/////////////////////////////////////////////////////////////////////////////
// CSourceBreakpointPage

CSourceBreakpointPage::CSourceBreakpointPage(CWnd* pParent /*=NULL*/)
	: CPropertyPage(CSourceBreakpointPage::IDD)
{
	pParent;
	
	//{{AFX_DATA_INIT(CSourceBreakpointPage)
	m_strAddr = _T("");
	//}}AFX_DATA_INIT
}

void CSourceBreakpointPage::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CSourceBreakpointPage)
	DDX_Control(pDX, IDC_BUTTON_SRCBP_RESTORE, m_btnRestore);
	DDX_Control(pDX, IDC_BUTTON_SRCBP_SAVE, m_btnSave);
	DDX_Control(pDX, IDC_LIST_SRCBP_BP, m_lstBp);
	DDX_Control(pDX, IDC_COMBO_SRCBP_FUNCTION, m_comboFunction);
	DDX_Control(pDX, IDC_COMBO_SRCBP_MODULE, m_comboModule);
	DDX_Control(pDX, IDC_BUTTON_SRCBP_DELALL, m_btnDeleteAll);
	DDX_Control(pDX, IDC_BUTTON_SRCBP_DELETE, m_btnDelete);
	DDX_Control(pDX, IDC_BUTTON_SRCBP_DISABLE, m_btnDisable);
	DDX_Control(pDX, IDC_BUTTON_SRCBP_ENABLE, m_btnEnable);
	DDX_Control(pDX, IDC_BUTTON_SRCBP_ADD, m_btnAdd);
	DDX_Text(pDX, IDC_EDIT_SRCBP_ADDR, m_strAddr);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CSourceBreakpointPage, CPropertyPage)
	//{{AFX_MSG_MAP(CSourceBreakpointPage)
	ON_BN_CLICKED(IDC_BUTTON_SRCBP_ADD, OnButtonSrcbpAdd)
	ON_BN_CLICKED(IDC_BUTTON_SRCBP_ENABLE, OnButtonSrcbpEnable)
	ON_BN_CLICKED(IDC_BUTTON_SRCBP_DISABLE, OnButtonSrcbpDisable)
	ON_BN_CLICKED(IDC_BUTTON_SRCBP_DELETE, OnButtonSrcbpDelete)
	ON_BN_CLICKED(IDC_BUTTON_SRCBP_DELALL, OnButtonSrcbpDelall)
	ON_CBN_SELCHANGE(IDC_COMBO_SRCBP_MODULE, OnSelchangeComboSrcbpModule)
	ON_CBN_SELCHANGE(IDC_COMBO_SRCBP_FUNCTION, OnSelchangeComboSrcbpFunction)
	ON_LBN_SELCHANGE(IDC_LIST_SRCBP_BP, OnSelchangeListSrcbpBp)
	ON_LBN_DBLCLK(IDC_LIST_SRCBP_BP, OnDblclkListSrcbpBp)
	ON_BN_CLICKED(IDC_BUTTON_SRCBP_SAVE, OnButtonSrcbpSave)
	ON_BN_CLICKED(IDC_BUTTON_SRCBP_RESTORE, OnButtonSrcbpRestore)
	ON_EN_CHANGE(IDC_EDIT_SRCBP_ADDR, OnChangeEditSrcbpAddr)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSourceBreakpointPage common routines

void CSourceBreakpointPage::EnableControl()
{
	// Add button
	BOOL bEnable = m_strAddr.IsEmpty() ? FALSE : TRUE;
	m_btnAdd.EnableWindow(bEnable);
	
	// Delete All button
	bEnable = m_lstBp.GetCount() > 0 ? TRUE : FALSE;
	m_btnDeleteAll.EnableWindow(bEnable);
	m_btnSave.EnableWindow(bEnable);
	
	// Delete button
	bEnable = LB_ERR != m_lstBp.GetCurSel() ? TRUE : FALSE;
	m_btnDelete.EnableWindow(bEnable);

	// Enable / Disable button
	if ( bEnable ) {
		CString strItem;
		m_lstBp.GetText(m_lstBp.GetCurSel(), strItem);
		bEnable = '-' == strItem[1] ? TRUE : FALSE;
		m_btnEnable.EnableWindow(bEnable);
		m_btnDisable.EnableWindow(!bEnable);
	}
	else {
		m_btnEnable.EnableWindow(bEnable);
		m_btnDisable.EnableWindow(bEnable);
	}
}

void CSourceBreakpointPage::GetBPInfo()
{
	// Get from BP list
	m_lstBp.ResetContent();

	int nLen(0);
	CObList* pList = ::BptGetBpList();
	POSITION pos = pList->GetHeadPosition();
	while ( pos ) {
		CBpData* pObj = (CBpData*)pList->GetNext(pos);
		CString strOneBp;
		if ( GetOneBp(pObj, strOneBp) ) {
			nLen = max(nLen, strOneBp.GetLength());
			m_lstBp.AddString(strOneBp);
		}
		else {
			// Skip the error node
			continue;
		}
	}
	
	// Select 1st bp
	if ( m_lstBp.GetCount() > 0 ) {
		m_lstBp.SetCurSel(0);
	}
	m_lstBp.SetHorizontalExtent(8*(nLen+1));
}

BOOL CSourceBreakpointPage::GetOneBp(CBpData* pObj, CString& strOneBp)
{
	// Get Address
	CString strAddr;
	if ( !::SrcAddrToText(pObj->GetAddr(), strAddr) ) {
		::SrcDisplayErrorMessage(::errBpAddr);
		return FALSE;
	}

	// Get Enable
	CString strEnable = pObj->IsEnable() ? '+' : '-';
	
	// Get symbol
	CString strSymbol = pObj->GetSymbol();
	CSourceAddr Addr;
	if ( !strSymbol.IsEmpty() && !::SrcTextToAddr(strSymbol, Addr) ) {
		::SrcDisplayErrorMessage(::errBpSymbol);
		strSymbol = "";
	}

	// Get one item
	// 251: " + 123456  #PRIME1#main"	; 2 SPACE
	// 196: " + P0:1234 #PRIME1#main"	; 1 SPACE
	strOneBp = CString(' ') + strEnable + ' ' + strAddr + ' ' + strSymbol;
	
	return TRUE;
}

void CSourceBreakpointPage::SetBPInfo()
{
	// Free breakpoint data list
	CObList* pList = ::BptGetBpList();
	POSITION pos = pList->GetHeadPosition();
	while ( pos ) {
		CBpData* pObj = (CBpData*)pList->GetNext(pos);
		delete pObj;
	}
	pList->RemoveAll();
	
	// Get BP from list-box
	for ( int i(0); i < m_lstBp.GetCount(); i++ ) {
		CString strOneBp;
		m_lstBp.GetText(i, strOneBp);
		
		// Split string
		CBpData BpData;
		GetOneBp(strOneBp, BpData);

		// Add to bp list
		pList->AddTail(new CBpData(BpData));
	}
}

void CSourceBreakpointPage::GetOneBp(const CString& strOneBp, CBpData& BpData) const
{
	// Get Addr
	CString strAddr = strOneBp.Mid(3, 7);
	CSourceAddr Addr;
	::SrcTextToAddr(strAddr, Addr);

	// Get Symbol
	int nPos = strOneBp.Find('#');
	CString strSymbol = -1 == nPos ? "" : strOneBp.Mid(nPos);
	
	// Get Enable
	BOOL bEnable = '+' == strOneBp[1] ? TRUE : FALSE;
	
	// Make CBpData
	BpData = CBpData(Addr, strSymbol, bEnable);
}

void CSourceBreakpointPage::GetModuleInfo()
{
	// If symbol is not loaded, cancel
	if ( !::SrcIsLoadedSymbol() ) {
		return;
	}
	
	// Get module info from Symbol server
	CStringList* pModuleList = new CStringList;
	::SrcGetModuleList(pModuleList);
	
	// Add to the list box
	m_comboModule.ResetContent();
	POSITION pos = pModuleList->GetHeadPosition();
	while ( pos ) {
		CString strModule = pModuleList->GetNext(pos);
		m_comboModule.AddString(strModule);
	}

	if ( m_comboModule.GetCount() > 0 ) {
		m_comboModule.SetCurSel(0);
		GetFunctionInfo();
	}

	// Free module info list
	pModuleList->RemoveAll();
	delete pModuleList;
}

void CSourceBreakpointPage::GetFunctionInfo()
{
	// If symbol is not loaded, cancel
	if ( !::SrcIsLoadedSymbol() ) {
		return;
	}
	
	// Get current selected module
	CString strModule;
	m_comboModule.GetLBText(m_comboModule.GetCurSel(), strModule);

	// Get function info from Symbol server
	CStringList* pFunctionList = new CStringList;
	::SrcGetFunctionList(strModule, pFunctionList);
	
	// Add to the list box
	m_comboFunction.ResetContent();
	POSITION pos = pFunctionList->GetHeadPosition();
	while ( pos ) {
		CString strFunction = pFunctionList->GetNext(pos);
		m_comboFunction.AddString(strFunction);
	}

	if ( m_comboFunction.GetCount() > 0 ) {
		m_comboFunction.SetCurSel(0);
	}

	// Free function info list
	pFunctionList->RemoveAll();
	delete pFunctionList;
	
	// Set Address edit box
	SetBpAddr();
}

void CSourceBreakpointPage::SetBpAddr()
{
	// Get module & function
	CString strModule;
	int nSelect = m_comboModule.GetCurSel();
	if ( LB_ERR != nSelect ) {
		m_comboModule.GetLBText(nSelect, strModule);
	}
	
	CString strFunction;
	nSelect = m_comboFunction.GetCurSel();
	if ( LB_ERR != nSelect ) {
		m_comboFunction.GetLBText(nSelect, strFunction);
	}

	// Set to Address if OK
	CString strAddr;
	if ( !strModule.IsEmpty() ) {
	 	strAddr += CString('#') + strModule;
	 	if ( !strFunction.IsEmpty() ) {
	 		strAddr += CString('#') + strFunction;
	 	}
		m_strAddr = strAddr;
	}
}

BOOL CSourceBreakpointPage::AddOneBp()
{
	// Is empty?
	m_strAddr.TrimLeft();
	m_strAddr.TrimRight();
	if ( m_strAddr.IsEmpty() ) {
		::SrcDisplayErrorMessage(::errBpEmpty);
		return FALSE;
	}

	// Convert to Address
	CSourceAddr Addr;
	if ( !::SrcTextToAddr(m_strAddr, Addr) ) {
		::SrcDisplayErrorMessage(::errBpInput);
		return FALSE;
	}
	
	// Does it exist in the list-box?
	for ( int i(0); i < m_lstBp.GetCount(); i++ ) {
		CString strOneBp;
		m_lstBp.GetText(i, strOneBp);
		
		// Split string
		CBpData BpData;
		GetOneBp(strOneBp, BpData);
		if ( BpData.GetAddr() == Addr ) {
			::SrcDisplayErrorMessage(::errBpExist);
			return FALSE;
		}
	}

	// Get symbol
	// If user don't keyin symbol but we can convert the address to symbol, add it.
	CString strSymbol;
	if ( !::SrcAddrToSymbol(Addr, strSymbol) ) {
		strSymbol = "";
	}

	// Get CBpData
	CBpData BpData(Addr, strSymbol, TRUE);
	
	// Add to list-box
	CString strOneBp;
	if ( !GetOneBp(&BpData, strOneBp) ) {
		return FALSE;
	}
	m_lstBp.AddString(strOneBp);

	int nPixel = m_lstBp.GetHorizontalExtent();
	int nLen = (strOneBp.GetLength()+1) * 8;
	if ( nLen > nPixel ) {
		m_lstBp.SetHorizontalExtent(nLen);
	}

	return TRUE;
}

BOOL CSourceBreakpointPage::SaveBpFile()
{
	// Open dialog
	HINSTANCE hInstEXE = AfxGetResourceHandle();
	AfxSetResourceHandle(::SrcGetDLLHandle());

	CString strFilter;
	strFilter.LoadString(IDS_BP_FILTER);
	
	AfxSetResourceHandle(hInstEXE);

	CSourceBpSaveDlg dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, strFilter);

	if ( IDCANCEL == dlg.DoModal() ) {
		return FALSE;
	}
	
	// Create an archive
	CFile file;
	if ( !file.Open(dlg.GetPathName(), CFile::modeCreate|CFile::modeWrite) ) {
		::SrcDisplayErrorMessage(::errBpFile);
		return FALSE;
	}
	CArchive ar(&file, CArchive::store);
	
	// Save head info
	ar << BYTE(0x00);

	int nCount = m_lstBp.GetCount();
	ar << HIBYTE(nCount);
	ar << LOBYTE(nCount);

	static char BASED_CODE szID[] = "Chen Jun.Bpt";
	ar << CString(szID);
	
	ar << BYTE(0xCC);
	
	ar << WORD(nCount);

	// Get BP from list-box
	for ( int i(0); i < nCount; i++ ) {
		CString strOneBp;
		m_lstBp.GetText(i, strOneBp);
		
		// Split string
		CBpData BpData;
		GetOneBp(strOneBp, BpData);
	
		// Save to the archive
		BpData.Serialize(ar);
	}

	// Close file & archive
	ar.Close();
	file.Close();

	return TRUE;
}

BOOL CSourceBreakpointPage::RestoreBpFile()
{
	// Open dialog
	HINSTANCE hInstEXE = AfxGetResourceHandle();
	AfxSetResourceHandle(::SrcGetDLLHandle());

	CString strFilter;
	strFilter.LoadString(IDS_BP_FILTER);
	
	AfxSetResourceHandle(hInstEXE);

	CSourceBpRestoreDlg dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, strFilter);

	if ( IDCANCEL == dlg.DoModal() ) {
		return FALSE;
	}

	if ( IDCANCEL == dlg.DoModal() ) {
		return FALSE;
	}

	// Detect minimal size
	CFileStatus rStatus;
	BOOL bFile = CFile::GetStatus(dlg.GetPathName(), rStatus);
	if ( !bFile || rStatus.m_size < 27 ) {
		::SrcDisplayErrorMessage(::errBpFormat);
		return FALSE;
	}
	
	// Create an archive
	CFile file;
	if ( !file.Open(dlg.GetPathName(), CFile::modeRead) ) {
		::SrcDisplayErrorMessage(::errBpFile);
		return FALSE;
	}
	CArchive ar(&file, CArchive::load);

	// Read head info
	BYTE b;
	ar >> b;
	if ( 0x00 != b ) {
		// Close file & archive
		ar.Close();
		file.Close();
			
		::SrcDisplayErrorMessage(::errBpFormat);
		return FALSE;
	}
	
	BYTE b1, b2;
	ar >> b1;
	ar >> b2;

	ar >> b;
	if ( 0x0C != b ) {
		// Close file & archive
		ar.Close();
		file.Close();
			
		::SrcDisplayErrorMessage(::errBpFormat);
		return FALSE;
	}

	static char BASED_CODE szID[] = "Chen Jun.Bpt";
	for ( int i(0); i < 0x0C; i++ ) {
		ar >> b;
		if ( szID[i] != b ) {
			// Close file & archive
			ar.Close();
			file.Close();
				
			::SrcDisplayErrorMessage(::errBpFormat);
			return FALSE;
		}
	}
		
	ar >> b;
	if ( 0xCC != b ) {
		// Close file & archive
		ar.Close();
		file.Close();
			
		::SrcDisplayErrorMessage(::errBpFormat);
		return FALSE;
	}

	WORD w;
	ar >> w;
	if ( HIBYTE(w) != b1 || LOBYTE(w) != b2 ) {
		// Close file & archive
		ar.Close();
		file.Close();
				
		::SrcDisplayErrorMessage(::errBpFormat);
		return FALSE;
	}

	// Set BP to list-box
	m_lstBp.ResetContent();
	int nLen(0);
	for ( i = 0; i < int(w); i++ ) {
		CBpData BpData;
		BpData.Serialize(ar);
		
		CString strOneBp;
		if ( GetOneBp(&BpData, strOneBp) ) {
			nLen = max(nLen, strOneBp.GetLength());
			m_lstBp.AddString(strOneBp);
		}
		else {
			// Skip the error node
			continue;
		}
	}
	
	// Select 1st bp
	if ( m_lstBp.GetCount() > 0 ) {
		m_lstBp.SetCurSel(0);
	}
	m_lstBp.SetHorizontalExtent(8*(nLen+1));

	// Close file & archive
	ar.Close();
	file.Close();

	return TRUE;
}

BOOL CSourceBreakpointPage::SetAllBp()
{
	// Clear all bp
	CSourceAddr Addr(0,0);
	if ( !::BptClrBp(Addr) ) {
		::SrcDisplayErrorMessage(::errBpSet);
		return FALSE;
	}

	// Through the bp list
	CObList* pList = ::BptGetBpList();
	POSITION pos = pList->GetHeadPosition();
	while ( pos ) {
		CBpData* pObj = (CBpData*)pList->GetNext(pos);
		if ( pObj->IsEnable() ) {
			if ( !::BptSetBp(pObj->GetAddr()) ) {
				::SrcDisplayErrorMessage(::errBpSet);
				return FALSE;
			}
		}
	}
	
	// Update source window
	::SrcUpdateSourceWindow(::srcBp);

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CSourceBreakpointPage message handlers

void CSourceBreakpointPage::OnOK()
{
	// Get data
	UpdateData();
	
	// Set BP info
	SetBPInfo();

	// Changed this page
	SetModified(FALSE);
	CancelToClose();

	// Set all bp
	SetAllBp();
}

BOOL CSourceBreakpointPage::OnSetActive()
{
	// TODO: Add your message handler code here

	return CPropertyPage::OnSetActive();
}

BOOL CSourceBreakpointPage::OnInitDialog() 
{
	CPropertyPage::OnInitDialog();
	
	// TODO: Add extra initialization here
	
	// Set list font
    HFONT hFont = (HFONT)::GetStockObject(ANSI_FIXED_FONT);
    CFont font;
	m_lstBp.SetFont(font.FromHandle(hFont));

	// Get Module info
	GetModuleInfo();
	
	// Get BP info
	GetBPInfo();
	
	// Enable controls
	EnableControl();
	
	UpdateData(FALSE);
	
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CSourceBreakpointPage::OnButtonSrcbpAdd() 
{
	// TODO: Add your control notification handler code here

	// Get data
	UpdateData();

	// Add one bp
	if ( !AddOneBp() ) {
		return;
	}

	// Enable controls
	EnableControl();
	SetModified();
	
	UpdateData(FALSE);
}

void CSourceBreakpointPage::OnButtonSrcbpEnable() 
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Enable this bp
	int nSelect = m_lstBp.GetCurSel();
	CString strItem;
	m_lstBp.GetText(nSelect, strItem);
	strItem.SetAt(1, '+');
	m_lstBp.DeleteString(nSelect);
	m_lstBp.InsertString(nSelect, strItem);
	m_lstBp.SetCurSel(nSelect);

	// Enable controls
	EnableControl();
	SetModified();
	
	UpdateData(FALSE);
}

void CSourceBreakpointPage::OnButtonSrcbpDisable() 
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Disable this bp
	int nSelect = m_lstBp.GetCurSel();
	CString strItem;
	m_lstBp.GetText(nSelect, strItem);
	strItem.SetAt(1, '-');
	m_lstBp.DeleteString(nSelect);
	m_lstBp.InsertString(nSelect, strItem);
	m_lstBp.SetCurSel(nSelect);

	// Enable controls
	EnableControl();
	SetModified();
	
	UpdateData(FALSE);
}

void CSourceBreakpointPage::OnButtonSrcbpDelete() 
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Delete one item from list-box
	int nSelect = m_lstBp.GetCurSel();
    m_lstBp.DeleteString(nSelect);
    if ( --nSelect >= 0 ) {
    	m_lstBp.SetCurSel(nSelect);
    }
    else if ( m_lstBp.GetCount() >= 1 ) {
		m_lstBp.SetCurSel(0);
	}

	// Enable controls
	EnableControl();
	SetModified();
	
	UpdateData(FALSE);
}

void CSourceBreakpointPage::OnButtonSrcbpDelall() 
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Delete All of list-box contents
	m_lstBp.ResetContent();

	// Enable controls
	EnableControl();
	SetModified();
	
	UpdateData(FALSE);
}

void CSourceBreakpointPage::OnSelchangeComboSrcbpModule() 
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Update Function combo-box
	GetFunctionInfo();

	// Enable controls
	EnableControl();
	
	UpdateData(FALSE);
}

void CSourceBreakpointPage::OnSelchangeComboSrcbpFunction() 
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Set Address edit box
	SetBpAddr();

	// Enable controls
	EnableControl();
	
	UpdateData(FALSE);
}

void CSourceBreakpointPage::OnSelchangeListSrcbpBp() 
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Enable controls
	EnableControl();
	
	UpdateData(FALSE);
}

void CSourceBreakpointPage::OnDblclkListSrcbpBp() 
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Enable/Disable this bp
	int nSelect = m_lstBp.GetCurSel();
	CString strItem;
	m_lstBp.GetText(nSelect, strItem);
	char ch = strItem[1] == '+' ? '-' : '+';
	strItem.SetAt(1, ch);
	m_lstBp.DeleteString(nSelect);
	m_lstBp.InsertString(nSelect, strItem);
	m_lstBp.SetCurSel(nSelect);

	// Enable controls
	EnableControl();
	SetModified();
	
	UpdateData(FALSE);
}

void CSourceBreakpointPage::OnButtonSrcbpSave()
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Open Save Breakpoint dialog
	if ( SaveBpFile() ) {

		// Enable controls
		EnableControl();
		SetModified(FALSE);
		CancelToClose();

		// Set all bp
		SetAllBp();
		
		UpdateData(FALSE);
	}
}

void CSourceBreakpointPage::OnButtonSrcbpRestore()
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Open Restore Breakpoint dialog
	if ( RestoreBpFile() ) {

		// Enable controls
		EnableControl();
		SetModified(FALSE);
		CancelToClose();

		// Set all bp
		SetAllBp();

		UpdateData(FALSE);
	}
}

void CSourceBreakpointPage::OnChangeEditSrcbpAddr()
{
	// TODO: Add your control notification handler code here
	
	// Get data
	UpdateData();

	// Enable controls
	EnableControl();
	
	UpdateData(FALSE);
}
