/***************************************************************************
**
**    $Header$
**
**    $Log$
** 
****************************************************************************/

// bptsvr.cpp : implementation file
//

#include "stdafx.h"
#include "bptdll.h"

#include "srcaddr.h"
#include "bptexp.h"
#include "bptdata.h"
#include "bptsvr.h"
#include "srcexp.h"
#include "srcimp.h"

#include "abiextfn.h"

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

/////////////////////////////////////////////////////////////////////////////
// Global variable

CObList* pBpDataList = 0;

static char BASED_CODE szKeywordALL[] = "ALL";

/////////////////////////////////////////////////////////////////////////////
// Exported function

#ifdef __cplusplus
extern "C" {
#endif	// __cplusplus

void WINAPI BreakpointCmd(int nArgc, char* pszArgv[])
{
	// Breakpoint [adr]
	CBpServer* pCmd = new CBpServer;
	pCmd->BpAction(nArgc, pszArgv);
	delete pCmd;
}

void WINAPI ClearCmd(int nArgc, char* pszArgv[])
{
	// CLear {ALl|adr}
	CBpServer* pCmd = new CBpServer;
	pCmd->ClearAction(nArgc, pszArgv);
	delete pCmd;
}

BOOL WINAPI BptClrBp(const CSourceAddr& Addr)
{
	// Clear one / all BP
	::ADDR addr;
	addr.addrType = Addr.GetType();
	addr.addr = Addr.GetAddr();

	return ( ICE_OK == ::emuClrBp(addr) );
}

BOOL WINAPI BptSetBp(const CSourceAddr& Addr)
{
	// Set one BP
	::ADDR addr;
	addr.addrType = Addr.GetType();
	addr.addr = Addr.GetAddr();

	return ( ICE_OK == ::emuSetBp(addr) );
}

void WINAPI BptClrBpFromList(const CSourceAddr& Addr)
{
	// Query within in the BP list
	POSITION pos = ::pBpDataList->GetHeadPosition();
	while ( pos ) {
		POSITION posOld = pos;
		CBpData* pObj = (CBpData*)::pBpDataList->GetNext(pos);
		if ( pObj->GetAddr() == Addr ) {
			// Clear this BP
			::ADDR addr;
			addr.addrType = Addr.GetType();
			addr.addr = Addr.GetAddr();
			if ( ICE_OK == ::emuClrBp(addr) ) {
				delete pObj;
				::pBpDataList->RemoveAt(posOld);
				
				// Mark BP
				::SrcUpdateSourceWindow(::srcBp);
			}
			else {
				::SrcDisplayErrorMessage(::errBpClr);
			}
        	return;
		}
	}

	// No this BP
	::SrcDisplayErrorMessage(::errBpNoExist);
}

void WINAPI BptSetBpFromList(const CSourceAddr& Addr)
{
	// Query within in the BP list
	POSITION pos = ::pBpDataList->GetHeadPosition();
	while ( pos ) {
		CBpData* pObj = (CBpData*)::pBpDataList->GetNext(pos);
		if ( pObj->GetAddr() == Addr ) {
			::SrcDisplayErrorMessage(::errBpExist);
			return;
		}
	}
	
	// Set this BP
	::ADDR addr;
	addr.addrType = Addr.GetType();
	addr.addr = Addr.GetAddr();
	if ( ICE_OK == ::emuSetBp(addr) ) {
		::pBpDataList->AddTail(new CBpData(Addr, "", TRUE));

		// Mark BP
		::SrcUpdateSourceWindow(::srcBp);
	}
	else {
		::SrcDisplayErrorMessage(::errBpSet);
	}
}

CObList* WINAPI BptGetBpList()
{
	// Get the BP data list
	return ::pBpDataList;
}

void WINAPI BptInitBpEnv()
{
	// Allocate breakpoint data list
	ASSERT(!::pBpDataList);
	::pBpDataList = new CObList;
	
	// Clear all bp
	CSourceAddr Addr(0, 0);
	::BptClrBp(Addr);
}

void WINAPI BptFreeBpEnv()
{
	// Free breakpoint data list
	if ( ::pBpDataList ) {
		POSITION pos = ::pBpDataList->GetHeadPosition();
		while ( pos ) {
			CBpData* pObj = (CBpData*)::pBpDataList->GetNext(pos);
			delete pObj;
		}
		::pBpDataList->RemoveAll();
		delete ::pBpDataList;
		::pBpDataList = 0;
	}
}

BOOL WINAPI BptIsBreakpoint(const CSourceAddr& Addr)
{
	// Query within in the BP list
	POSITION pos = ::pBpDataList->GetHeadPosition();
	while ( pos ) {
		CBpData* pObj = (CBpData*)::pBpDataList->GetNext(pos);
		if ( pObj->GetAddr() == Addr ) {
			return TRUE;
		}
	}
	
	return FALSE;
}

#ifdef __cplusplus
}
#endif	// __cplusplus

/////////////////////////////////////////////////////////////////////////////
// CBpServer

void CBpServer::BpAction(int nArgc, char* pszArgv[])
{
	// Breakpoint [adr]
	if ( 1 == nArgc ) {
		// Breakpoint
		DisplayAllBp();
	}
	else if ( 2 == nArgc ) {
		// Breakpoint [adr]
		if ( GetBpData(pszArgv[1]) ) {
			SetOneBp();
		}
		
		// Update source window
		::SrcUpdateSourceWindow(::srcBp);
	}
	else {
		ASSERT(FALSE);
	}
}

void CBpServer::ClearAction(int nArgc, char* pszArgv[])
{
	// CLear {ALl|adr}
	if ( 2 == nArgc ) {
		CString strArgv(pszArgv[1]);
		strArgv.MakeUpper();
		if ( ::szKeywordALL == strArgv ) {
			// CLear ALl
			ClearAllBp();
		}
		else if ( GetBpData(pszArgv[1]) ) {
			// CLear adr
			ClearOneBp();
		}
		
		// Update source window
		::SrcUpdateSourceWindow(::srcBp);
	}
	else {
		ASSERT(FALSE);
	}
}

void CBpServer::DisplayAllBp()
{
	// Get total bp count
	int nCount = ::pBpDataList->GetCount();

	HINSTANCE hInstEXE = AfxGetResourceHandle();
	AfxSetResourceHandle(::BptGetDLLHandle());

	CString strFormat, strCount;
	strFormat.LoadString(IDS_BP_COUNT);
	strCount.Format(strFormat, nCount);

	AfxSetResourceHandle(hInstEXE);

	::SrcShowLine(strCount);

	if ( 0 == nCount ) {
		return;
	}
	
	// Walk through the bp list
	POSITION pos = ::pBpDataList->GetHeadPosition();
	while ( pos ) {
		CBpData* pObj = (CBpData*)::pBpDataList->GetNext(pos);
		// Show one bp
		DisplayOneBp(pObj);
	}
}

void CBpServer::SetOneBp()
{
	// Is already exist this bp
	POSITION pos = ::pBpDataList->GetHeadPosition();
	while ( pos ) {
		POSITION posOld(pos);
		CBpData* pObj = (CBpData*)::pBpDataList->GetNext(pos);
		if ( pObj->GetAddr() == m_BpData.GetAddr() ) {
			if ( pObj->IsEnable() ) {
				::SrcDisplayErrorMessage(::errBpExist, TRUE);
			}
			else {
				pObj->Enable();
				::pBpDataList->SetAt(posOld, pObj);
				DisplayOneBp(pObj);
			}
			return;
		}
	}
			
	// ABI routine
	if ( ::BptSetBp(m_BpData.GetAddr()) ) {
		// Append to bp list
		CBpData* pObj = new CBpData(m_BpData);
		::pBpDataList->AddTail(pObj);
		DisplayOneBp(pObj);
	}
	else {
		::SrcDisplayErrorMessage(::errBpSet, TRUE);
	}
}

void CBpServer::ClearAllBp()
{
	// Is there any BPs
	if ( ::pBpDataList->IsEmpty() ) {
		::SrcDisplayErrorMessage(::errBpNo, TRUE);
		return;
	}
	
	// Clear all bp
	CSourceAddr Addr(0, 0);
	if ( ::BptClrBp(Addr) ) {
		// Empty bp list
		POSITION pos = ::pBpDataList->GetHeadPosition();
		while ( pos ) {
			CBpData* pObj = (CBpData*)::pBpDataList->GetNext(pos);
			delete pObj;
		}
		::pBpDataList->RemoveAll();

		// Show message
		HINSTANCE hInstEXE = AfxGetResourceHandle();
		AfxSetResourceHandle(::BptGetDLLHandle());

		CString strMessage;
		strMessage.LoadString(IDS_BP_CLEARALL);
		
		AfxSetResourceHandle(hInstEXE);
		
		::SrcShowLine(strMessage);
	}
	else {
		::SrcDisplayErrorMessage(::errBpClr, TRUE);
	}
}

void CBpServer::ClearOneBp()
{
	// Is already exist this bp
	BOOL bExist(FALSE);
	POSITION pos = ::pBpDataList->GetHeadPosition();
	POSITION posOld;
	while ( pos ) {
		posOld = pos;
		CBpData* pObj = (CBpData*)::pBpDataList->GetNext(pos);
		if ( pObj->GetAddr() == m_BpData.GetAddr() ) {
			bExist = TRUE;
			break;
		}
	}

	// No such bp
	if ( !bExist ) {
		::SrcDisplayErrorMessage(::errBpNoExist, TRUE);
		return;
	}
			
	// ABI routine
	if ( ::BptClrBp(m_BpData.GetAddr()) ) {
		// Delete from bp list
		::pBpDataList->RemoveAt(posOld);
		DisplayOneBp((CBpData*)::pBpDataList->GetAt(posOld), FALSE);
	}
	else {
		::SrcDisplayErrorMessage(::errBpClr, TRUE);
	}
}

BOOL CBpServer::GetBpData(char* pszArgv)
{
	// Get address: Address server
	CSourceAddr Addr;
	if ( !::SrcTextToAddr(CString(pszArgv), Addr) ) {
		::SrcDisplayErrorMessage(::errAddr, TRUE);
		return FALSE;
	}

	// Get symbol if exist
	CString strSymbol;
	if ( !::SrcAddrToSymbol(Addr, strSymbol) ) {
		strSymbol = "";
	}

	// Set bp data
	m_BpData.SetAddr(Addr);
	m_BpData.SetSymbol(strSymbol);
	m_BpData.Enable();
	
	return TRUE;
}

void CBpServer::DisplayOneBp(const CBpData* pObj, BOOL bSet)
{
	// Get Addr
	CSourceAddr Addr = pObj->GetAddr();
	CString strAddr;
	if ( !::SrcAddrToText(Addr, strAddr) ) {
		::SrcDisplayErrorMessage(::errAddr, TRUE);
	}
	else {
		// Get Enable/Disable
		HINSTANCE hInstEXE = AfxGetResourceHandle();
		AfxSetResourceHandle(::BptGetDLLHandle());

		UINT uID = pObj->IsEnable() ? IDS_BP_ENABLE : IDS_BP_DISABLE;
		CString strEnable;
		strEnable.LoadString(uID);

		// Get format string
		uID = bSet ? IDS_BP_FORMAT : IDS_BP_CLEAR;
		CString strFormat;
		strFormat.LoadString(uID);
				
		AfxSetResourceHandle(hInstEXE);

		CString strBpMsg;
		strBpMsg.Format(strFormat, strAddr);
		if ( bSet ) {
			strBpMsg += strEnable;
		}

		// Show message
		::SrcShowLine(strBpMsg);
	}
}
