/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      $Id: debuggerpl.cpp,v 1.8 2001/04/04 12:42:36 aleksandras Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "pmgui.cpp"
    #pragma interface "pmgui.h"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#include "debuggerpl.h"

#include "wx/pmf/inf/textediting.h"

static wxsDebuggerEngine* _gpDbgPlugin = NULL;

wxsDebuggerEngine& wxsGetDbg()
{
	wxASSERT( _gpDbgPlugin );

	return *_gpDbgPlugin;
}

void wxsSetDbg( wxsDebuggerEngine* pPlugin )
{
	_gpDbgPlugin = pPlugin;
}

/***** Implementation for class wxsDbgMirror *****/

wxsDbgMirror::wxsDbgMirror()

	: mId( INVALID_DBG_OBJ_ID )
{}

wxsDbgMirror::~wxsDbgMirror()
{}

DbgObjId_t wxsDbgMirror::GetId()
{
	return mId;
}

void wxsDbgMirror::SetId( DbgObjId_t id )
{
	mId = id;
}

wxsDebuggerEngine& wxsDbgMirror::GetDebugger() const
{
	return wxsGetDbg();
}

/***** Implementation for class wxsDbgCompositeType *****/

wxsDbgCompositeType::wxsDbgCompositeType()

	: mMethodsLoaded( FALSE ),
	  mFieldsLoaded ( FALSE )
{}

wxsDbgCompositeType::~wxsDbgCompositeType()
{
	WX_CLEAR_ARRAY( mFields );
	WX_CLEAR_ARRAY( mMethods );
}

wxsDbgMirrorListT& wxsDbgCompositeType::GetMethods()
{
	if ( !mMethodsLoaded ) GetDebugger().LoadMethods( *this );

	return mMethods;
}

wxsDbgMirrorListT& wxsDbgCompositeType::GetFields()
{
	if ( mFieldsLoaded ) GetDebugger().LoadFields( *this );

	return mFields;
}

void wxsDbgCompositeType::SetMethods( wxsDbgMirrorListT& methods )
{
	mMethods       = methods;
	mMethodsLoaded = TRUE;
}

void wxsDbgCompositeType::SetFields( wxsDbgMirrorListT& fields )
{
	mFields       = fields;
	mFieldsLoaded = TRUE;
}

/***** Implementation for class wxsDbgType *****/

wxsDbgType::wxsDbgType()

	: mFlags( 0 ),
	  mTypeCode( -1 )
{}

wxString wxsDbgType::GetName()
{
	return mName;
}

void wxsDbgType::SetName( const wxString& name )
{
	mName = name;
}

wxString wxsDbgType::GetSignature()
{
	return mSignature;
}

void wxsDbgType::SetSignature( const wxString& s )
{
	mSignature = s;
}

int  wxsDbgType::GetFlags()
{
	return mFlags;
}

void wxsDbgType::SetFlags( int flags )
{
	mFlags = flags;
}

int wxsDbgType::GetTypeCode() { return mTypeCode; }

void wxsDbgType::SetTypeCode( int tcode )
{
	mTypeCode = tcode;
}

/***** Implementation for class wxsDbgMethod *****/

wxsDbgMethod::wxsDbgMethod()

	: mLineInfoLoaded( FALSE ),
	  mArgInfoLoaded ( FALSE ),
	  mpContainingType( NULL )
{}

wxsDbgMethod::~wxsDbgMethod()
{
	WX_CLEAR_ARRAY( mArgTypes );
}

wxArrayInt& wxsDbgMethod::GetLines()
{
	if ( !mLineInfoLoaded ) GetDebugger().LoadLines( *this );

	return mLines;
}

wxArrayInt& wxsDbgMethod::GetCodeIndexes()
{
	if ( !mLineInfoLoaded ) GetDebugger().LoadLines( *this );

	return mCodeIndexes;
}

wxsDbgMirrorListT& wxsDbgMethod::GetArgumentTypes()
{
	if ( !mArgInfoLoaded ) GetDebugger().LoadArguments( *this );

	return mArgTypes;
}

wxStringList& wxsDbgMethod::GetArgumentNames()
{
	if ( !mArgInfoLoaded ) GetDebugger().LoadArguments( *this );

	return mArgNames;
}

void wxsDbgMethod::SetLines( wxArrayInt& lines )
{
	mLines          = lines;
	mLineInfoLoaded = TRUE;
}

void wxsDbgMethod::SetCodeIndexes( wxArrayInt& lst )
{
	mCodeIndexes   = lst;
	mLineInfoLoaded = TRUE;
}

void wxsDbgMethod::SetArgTypes( wxsDbgMirrorListT& types )
{
	mArgTypes	   = types;
	mArgInfoLoaded = TRUE;
}

void wxsDbgMethod::SetArgNames( wxStringList& names )
{
	mArgNames      = names;
	mArgInfoLoaded = TRUE;
}

wxsDbgCompositeType* wxsDbgMethod::GetContainingType()
{
	return mpContainingType;
}

void wxsDbgMethod::SetContainingType( wxsDbgCompositeType* pType )
{
	mpContainingType = pType;
}

/***** Implementation for class wxsDbgField *****/

wxsDbgField::wxsDbgField()

	: mpDeclType( NULL ),
	  mpContainingType( NULL )
{}

wxString& wxsDbgField::GetName()
{
	return mName;
}

wxsDbgType& wxsDbgField::GetDeclaringType()
{
	if ( mpDeclType == NULL )

		GetDebugger().LoadDeclaringType( *this );

	return *mpDeclType;
}

DbgObjId_t wxsDbgField::GetDeclaringTypeId()
{
	return mDeclTypeId;
}

void wxsDbgField::SetDeclaringType( wxsDbgType* pType )
{
	mpDeclType = pType;
}

void wxsDbgField::SetName( const wxString& name )
{
	mName = name;
}

void wxsDbgField::SetSignature( const wxString& sign )
{
	mSignature = sign;
}

const wxString& wxsDbgField::GetSignature()
{
	return mSignature;
}

wxsDbgCompositeType* wxsDbgField::GetContainingType()
{
	return mpContainingType;
}

void wxsDbgField::SetContainingType( wxsDbgCompositeType* pType )
{
	mpContainingType = pType;
}

/***** Implementation for class wxsDbgObjectValue *****/

int wxsDbgObjectValue::GetGeneralType()
{
	return wxsDbg_COMPOSITE_TYPE;
}

wxsDbgType* wxsDbgObjectValue::GetDynamicType()
{
	// FOR NOW::
	return mpType;
}

wxsDbgMirrorListT& wxsDbgObjectValue::GetFieldValues( wxsDbgType& forType )
{
	// FOR NOW:: forType is ignored
	return mValues;
}

/***** Implementation for class wxsDbgArrayValue *****/

int wxsDbgArrayValue::GetGeneralType()
{
	// TBD::
	return wxsDbg_COMPOSITE_TYPE | wxsDbg_ARRAY_TYPE_FLAG;
}

size_t wxsDbgArrayValue::GetCount()
{
	return mValues.Count();
}

wxsDbgMirrorListT& wxsDbgArrayValue::GetValues()
{
	return mValues;
}

bool wxsDbgArrayValue::HasStringValue()
{
	// TBD::
	return FALSE;
}

wxString wxsDbgArrayValue::GetStringValue()
{
	// TBD::
	return "";
}

/***** Implementation for class wxsDbgPrimitiveValue *****/

int wxsDbgPrimitiveValue::GetGeneralType()
{
	// TBD::
	return mDecValue;
}

long wxsDbgPrimitiveValue::GetDecimalValue()
{
	// TBD::
	return 0;
}

wxString wxsDbgPrimitiveValue::GetStringValue()
{
	// TBD::
	return "";
}

/***** Implementation for class wxsDbgPrimitiveValue:: *****/

wxsDbgValueTreeNode::wxsDbgValueTreeNode()
{
	// TBD::
}

wxsDbgValueTreeNode::~wxsDbgValueTreeNode()
{
	// TBD::
}

void wxsDbgValueTreeNode::AddChild( wxsDbgValueTreeNode* pNode )
{
	mChildNodes.Add( (wxsDbgMirror*)pNode );
}

wxsDbgMirrorListT& wxsDbgValueTreeNode::GetChildren()
{
	return mChildNodes;
}

wxString wxsDbgValueTreeNode::GetErrorMessage()
{
	return mErrorMsg;
}

wxString& wxsDbgValueTreeNode::GetName()
{
	return mName;
}

wxsDbgMirrorListT& wxsDbgValueTreeNode::GetFieldValues()
{
	return mFieldValues;
}

wxsDbgValue& wxsDbgValueTreeNode::GetValue()
{
	return *mpValue;
}

bool wxsDbgValueTreeNode::IsResolved()
{
	return mValueResolved;
}
void wxsDbgValueTreeNode::SetIsResolved( bool resolved )
{
	mValueResolved = resolved;
}

void wxsDbgValueTreeNode::SetValue( wxsDbgValue* pValue )
{
	// TBD::
}

void wxsDbgValueTreeNode::SetField( wxsDbgField* pField )
{
	// TBD::
}

void wxsDbgValueTreeNode::SetDeclaringType( wxsDbgType* pType )
{
	// TBD::
}

void wxsDbgValueTreeNode::InvalidateNode()
{
	SetIsResolved( FALSE );

    size_t i;
	for( i = 0; i != mChildNodes.Count(); ++i )
    {
		((wxsDbgValueTreeNode*)mChildNodes[i])->InvalidateNode();
    }
}

bool wxsDbgValueTreeNode::IsExpanded()
{
	return mIsExpanded;
}

void wxsDbgValueTreeNode::SetFieldValues( wxsDbgMirrorListT& fieldValues )
{
	// TBD::
}

wxsDbgType& wxsDbgValueTreeNode::GetType()
{
	// TBD::
	return *mpDeclType;
}

/***** Implementation for class wxsDbgStackFrame *****/

wxsDbgStackFrame::wxsDbgStackFrame()

	: mLocalsLoaded( FALSE ),
	  mValuesLoaded( FALSE )
{}

wxsDbgStackFrame::~wxsDbgStackFrame()
{
	// TBD::
}

void wxsDbgStackFrame::SetLocation( const wxsDbgLocation& loc ) 
{ 
	mLocation = loc; 
}

wxsDbgLocation& wxsDbgStackFrame::GetLocation() 
{ 
	return mLocation; 
}

void wxsDbgStackFrame::SetDisplayText( const wxString& text )
{
	mDisplayText = text;
}

wxString& wxsDbgStackFrame::GetDisplayText()
{
	return mDisplayText;
}

wxsDbgMirrorListT& wxsDbgStackFrame::GetLocals()
{
	return mLocals;
}

wxsDbgMirrorListT& wxsDbgStackFrame::GetValuesOfLocals()
{
	return mValues;
}

void wxsDbgStackFrame::SetLocals( wxsDbgMirrorListT& locals )
{
	mLocals		  = locals;
	mLocalsLoaded = TRUE;
}

void wxsDbgStackFrame::SetValuesOfLocals( wxsDbgMirrorListT& values )
{
	mValues		  = values;
	mValuesLoaded = TRUE;
}

wxsDbgValue* wxsDbgStackFrame::GetThisObject()
{
	// TBD::
	return NULL;
}

void wxsDbgStackFrame::SetThisThisObject( wxsDbgValue* pThisValue )
{
	mpThisObj = pThisValue;
}

/***** Implementation for class wxsDbgBreakpoint *****/

wxsDbgBreakpoint::wxsDbgBreakpoint()

	: mIsEnabled( TRUE )
{}

const wxsDbgLocation& wxsDbgBreakpoint::GetLocation()
{
	return mLocation;
}

void wxsDbgBreakpoint::SetLocation( const wxsDbgLocation& where )
{
	mLocation = where;
}

void wxsDbgBreakpoint::SetEnabled( bool enabled )
{
	mIsEnabled = enabled;
}

bool wxsDbgBreakpoint::IsEnabled()
{
	return mIsEnabled;
}

/***** Implementation for class wxsDbgLocation *****/

wxsDbgLocation::wxsDbgLocation()

	: mState( INVALID )
{}

wxsDbgLocation::wxsDbgLocation( const wxPMFileInfo& file, size_t line )

	: mLine( line ),
	  mFile( file ),
	  mState( RESOLVED_FOR_FRONTEND )
{}

wxsDbgLocation::wxsDbgLocation( DbgObjId_t classId,
								DbgObjId_t methodId,
								DbgObjId_t instructionId )

	: mClassId      ( classId ),
	  mMethodId     ( methodId ),
	  mInstructionId( instructionId ),
	  mState        ( RESOLVED_FOR_BACKEND )
{}

bool wxsDbgLocation::IsResolved() const
{
	return mState == wxsDbgLocation::RESOLVED;
}

int  wxsDbgLocation::GetResolutionState() const
{
	return mState;
}

void wxsDbgLocation::SetResolutionState( int state )
{
	mState = state;
}

void wxsDbgLocation::SetupForBackend( DbgObjId_t classId,
									  DbgObjId_t methodId,
									  DbgObjId_t instructionId )
{
	mClassId       = classId;
	mMethodId      = methodId;
	mInstructionId = instructionId;
	mState = RESOLVED_FOR_BACKEND;
}

void wxsDbgLocation::SetupForFrontend( const wxPMFileInfo& file, size_t line )
{
	mFile = file;
	mLine = line;
}

void wxsDbgLocation::SetFile( const wxPMFileInfo& file )
{
	mFile = file;
	mState = RESOLVED_FOR_FRONTEND;
}

void wxsDbgLocation::SetLine( size_t line )
{
	mLine = line;
	mState = RESOLVED_FOR_FRONTEND;
}

bool wxsDbgLocation::Resolve() const
{
	if ( mState != RESOLVED )

		return GetDebugger().ResolveLocation( *(wxsDbgLocation*)this );
	else
		return TRUE;
}

wxString wxsDbgLocation::GetDisplayText() const
{
	if ( mState != RESOLVED_FOR_FRONTEND && mState != RESOLVED ) 

		GetDebugger().ResolveLocation( *(wxsDbgLocation*)this );

	// NOTE:: assuming that the resolution succeeded

	char buf[32];
	sprintf( buf, "%d", mLine );

	return mFile.GetPath() + ":" + wxString( buf );
}

size_t wxsDbgLocation::GetLine() const
{
	if ( mState == RESOLVED_FOR_BACKEND ||
		 mState == INVALID )

		 GetDebugger().ResolveLocation( *(wxsDbgLocation*)this );
		 
	return mLine;
}

const wxPMFileInfo& wxsDbgLocation::GetFile() const
{
	if ( mState == RESOLVED_FOR_BACKEND ||
		 mState == INVALID )

		 GetDebugger().ResolveLocation( *(wxsDbgLocation*)this );
		 
	return mFile;
}

DbgObjId_t wxsDbgLocation::GetClassId() const
{
	if ( mState == RESOLVED_FOR_FRONTEND ||
		 mState == INVALID )

		 GetDebugger().ResolveLocation( *(wxsDbgLocation*)this );
		 
	return mClassId;
}

DbgObjId_t wxsDbgLocation::GetMethodId() const
{
	if ( mState == RESOLVED_FOR_FRONTEND ||
		 mState == INVALID )

		 GetDebugger().ResolveLocation( *(wxsDbgLocation*)this );
		 
	return mMethodId;
}

DbgObjId_t wxsDbgLocation::GetInstructionId() const
{
	if ( mState == RESOLVED_FOR_FRONTEND ||
		 mState == INVALID )

		 GetDebugger().ResolveLocation( *(wxsDbgLocation*)this );
		 
	return mInstructionId;
}

bool wxsDbgLocation::operator==( const wxsDbgLocation& other ) const
{
	if ( mState == RESOLVED_FOR_BACKEND )
	{
		return this->mClassId == other.mClassId &&
			   this->mMethodId == other.mMethodId &&
			   this->mInstructionId == other.mInstructionId;
	}
	else
	{
		return this->mFile == other.mFile &&
			   this->mLine == other.mLine;
	}
}

/***** Implementation for class wxsDebuggerEngine *****/

wxsDebuggerEngine::wxsDebuggerEngine()

	: mTypeNamesHash( wxKEY_STRING ),
	  mCurrentStackFrame( 0 ),
	  mpCurrentThread( NULL ),
	  mpBackend( NULL )
{
	wxsSetDbg( this );
}

wxsDebuggerEngine::~wxsDebuggerEngine()
{}

void wxsDebuggerEngine::AddListener( wxsDebuggerListener* pListener )
{
	mListeners.Add( pListener );
}

void wxsDebuggerEngine::RemoveListener( wxsDebuggerListener* pListener )
{
	mListeners.Remove( pListener );
}

void wxsDebuggerEngine::SetBackend( wxsDebuggerBackend* pBackend )
{
	mpBackend = pBackend;
}

bool wxsDebuggerEngine::GetLocalSymbols( wxStringList& lst )
{
	return TRUE;
}

bool wxsDebuggerEngine::GetSymbolValue( const wxString& name, 
										wxString& value, 
										bool& isComposite )
{
	// TBD::
	return TRUE;
}

wxsDbgType* wxsDebuggerEngine::FindTypeBySignature( const wxString& sign )
{
	// look up type-cache first

	wxsDbgType* pType = (wxsDbgType*)mTypeNamesHash.Get( sign );

	if ( pType ) return pType;

	pType = mpBackend->LoadTypeBySignature( sign );

	if ( !pType ) return NULL;

	// put type-object into cache

	mTypeIdsHash.Put( (long)pType->GetId(), (wxObject*)pType );
	mTypeNamesHash.Put( pType->GetSignature(), (wxObject*)pType );

	return pType;
}

void wxsDebuggerEngine::LoadNodeValuesRecursive( wxsDbgValueTreeNode* pNode )
{
	// fields can be loaded only for composite types

	if ( pNode->GetType().GetFlags() & wxsDbg_COMPOSITE_TYPE == 0 )	

		return;

	wxsDbgObjectValue* pValue = (wxsDbgObjectValue*)&pNode->GetValue();

	wxsDbgCompositeType* pDType = (wxsDbgCompositeType*)pValue->GetDynamicType();

	wxsDbgMirrorListT& fields = pValue->GetFieldValues( *pDType );

	pNode->SetFieldValues( fields );

	if ( !pNode->IsExpanded() ) return;

	wxsDbgMirrorListT& children = pNode->GetChildren();

    size_t i;
	for( i = 0; i != children.Count(); ++i )
    {
		LoadNodeValuesRecursive( (wxsDbgValueTreeNode*)children[i] );
    }
}

bool wxsDebuggerEngine::ResolveRoots( wxsDbgMirrorListT& trees, bool localsOnly )
{
	wxsDbgStackFrame* pFrame = GetCurrentStackFrame();

	wxsDbgMirrorListT& locals = pFrame->GetLocals(); 
	wxsDbgMirrorListT& localValues = pFrame->GetValuesOfLocals();

    size_t i;
	for( i = 0; i != trees.Count(); ++i )
	{
		wxsDbgValueTreeNode& root = *((wxsDbgValueTreeNode*)trees[i]);
		wxString rootName = root.GetName();
		root.InvalidateNode();

		root.SetIsResolved( FALSE );

		// search through the locals (on-stack variables)

		size_t n = 0;
		for( ; n != locals.Count(); ++n )
		
			if ( ((wxsDbgField*)locals[n])->GetName() == rootName )
			{
				wxsDbgField* pField = (wxsDbgField*)locals[n];

				root.SetDeclaringType( &pField->GetDeclaringType() );

				root.SetValue( (wxsDbgValue*)localValues[n] );

				root.SetIsResolved( TRUE );
				break;
			}

		// search within the fields of "this" object

		if ( !localsOnly )
		{
			// value of "this" is never of a primiteve type

			wxsDbgObjectValue* pThisValue = (wxsDbgObjectValue*)pFrame->GetThisObject();

			if ( pThisValue )
			{
				// "this" is always represented by a composite type

				wxsDbgCompositeType* pType = 
					(wxsDbgCompositeType*)pThisValue->GetDynamicType();

				wxsDbgMirrorListT& fields = pType->GetFields();

                size_t k;
				for( k = 0; k != fields.Count(); ++k )
				
					if ( ((wxsDbgField*)fields[i])->GetName() == rootName )
					{
						root.SetField( (wxsDbgField*)fields[i] );

						root.SetValue( pThisValue );

						root.SetIsResolved( TRUE );

						break;
					}
			}
		}

		if ( root.IsResolved() )
		
			if ( root.IsExpanded() ) LoadNodeValuesRecursive( &root );

	} // end of for(...)

	return TRUE; // TBD:: meaning
}

bool wxsDebuggerEngine::LoadValueTrees( wxsDbgMirrorListT& trees, bool localsOnly )
{
	// TBD::
	return TRUE;
}

size_t wxsDebuggerEngine::GetStackFrameCount()
{
	return mStackFrames.Count();
}

bool wxsDebuggerEngine::GetStackFrames( size_t from, size_t till, wxsDbgMirrorListT& lst )
{
    size_t i;
	for( i = 0; i != till; ++i )
    {
		lst.Add( mStackFrames[i] );
    }
	return TRUE;
}

size_t wxsDebuggerEngine::GetCurrentStackFrameNo()
{
	return mCurrentStackFrame;
}
		
bool wxsDebuggerEngine::SetCurrentStackFrameNo( size_t no )
{
	// TBD:: assertions

	mCurrentStackFrame = no;

	return TRUE;
}
	
wxsDbgStackFrame* wxsDebuggerEngine::GetCurrentStackFrame()
{
	if ( mCurrentStackFrame < mStackFrames.Count() )

		return (wxsDbgStackFrame*)mStackFrames[mCurrentStackFrame];

	return NULL;
}

bool wxsDebuggerEngine::SetCurrentThread( wxsDbgThread* pThread )
{
	mpCurrentThread = pThread;

	return TRUE;
}

wxsDbgThread* wxsDebuggerEngine::GetCurrentThread()
{
	return mpCurrentThread;
}

wxsDbgMirrorListT& wxsDebuggerEngine::GetBreakpoints()
{
	return mBreakpoints;
}

wxsDbgBreakpoint* wxsDebuggerEngine::AddBreakpoint( const wxsDbgLocation& where, int hitCount )
{
	DbgObjId_t id = INVALID_DBG_OBJ_ID;

	bool ok = mpBackend->AddBreakpoint( where, id, hitCount );

	if ( ok )
	{
		wxsDbgBreakpoint* pBrk = new wxsDbgBreakpoint();
		pBrk->SetId( id );
		pBrk->SetLocation( where );
		pBrk->SetEnabled( TRUE );

		mBreakpoints.Add( pBrk );

		return pBrk;
	}

	return NULL;
}

bool wxsDebuggerEngine::RemoveBreakpoint( const wxsDbgLocation& where )
{
    size_t i;
	for( i = 0; i != mBreakpoints.Count(); ++i )
	{
		wxsDbgBreakpoint* pBrk = (wxsDbgBreakpoint*)mBreakpoints[i];

		const wxsDbgLocation& loc = pBrk->GetLocation();

		if ( loc == where )
		
			return RemoveBreakpoint( (wxsDbgBreakpoint*)pBrk );
	}

	return FALSE;
}

bool wxsDebuggerEngine::RemoveBreakpoint( wxsDbgBreakpoint* pBrkPoint )
{
	bool ok = mpBackend->RemoveBreakpoint( pBrkPoint->GetId() );

	if ( ok )

		mBreakpoints.Remove( pBrkPoint );

	return ok;
}

bool wxsDebuggerEngine::RemoveAllBreakpoints()
{
	return mpBackend->RemoveAllBreakpoints();
}

bool wxsDebuggerEngine::GetTopLevelThreadGroups( wxsDbgMirrorListT& lst )
{
	if ( mAllThreadGroups.Count() != 0 )
	{
		// FOR NOW::
		lst = mAllThreadGroups;
		return TRUE;
	}
	wxsDbgObjectIdListT ids;
	mpBackend->GetAllThreadGroups( ids );

    size_t i;
	for( i = 0; i != ids.Count(); ++i )
	{
		wxsDbgThreadGroup* pThreadGroup = new wxsDbgThreadGroup();
		pThreadGroup->SetId( ids[i] );
		wxString name;
		mpBackend->GetThreadGroupName( ids[i], name );
		pThreadGroup->SetName( name );

		mAllThreadGroups.Add( pThreadGroup );
	}

	lst = mAllThreadGroups;

	return TRUE;
}

bool wxsDebuggerEngine::GetThreadsInGroup( wxsDbgThreadGroup* pGroup, 
										   wxsDbgMirrorListT& lst )
{
	if ( mAllThreads.Count() != 0 )
	{
		// FOR NOW::
		lst = mAllThreads;
		return TRUE;
	}
	wxsDbgObjectIdListT ids;
	mpBackend->GetAllThreads( ids );

    size_t i;
	for( i = 0; i != ids.Count(); ++i )
	{
		wxsDbgThread* pThread = new wxsDbgThread();
		pThread->SetId( ids[i] );
		wxString name;
		mpBackend->GetThreadName( ids[i], name );

		pThread->SetName( name );

		mAllThreads.Add( pThread );
	}

	lst = mAllThreads;

	return TRUE;
}

bool wxsDebuggerEngine::GetGroupsInGroup( wxsDbgThreadGroup* pGroup, 
										  wxsDbgMirrorListT& lst )
{
	// TBD::
	return TRUE;
}

bool wxsDebuggerEngine::SuspendThread( wxsDbgThread* pThread )
{
	return mpBackend->SuspendThread( pThread->GetId() );
}

bool wxsDebuggerEngine::ResumeThread( wxsDbgThread* pThread )
{
	return mpBackend->ResumeThread( pThread->GetId() );
}

wxString wxsDebuggerEngine::GetDebbugerBackendVersion()
{
	return mpBackend->GetDebuggerBackendVersion();
}

bool wxsDebuggerEngine::InterruptCurrentThread()
{
	return mpBackend->InterruptThread( mpCurrentThread->GetId() );
}

bool wxsDebuggerEngine::StepCurrentThread( wxsDbgStepTypesEnum stepType )
{
	return mpBackend->StepThread( mpCurrentThread->GetId(), stepType );
}

bool wxsDebuggerEngine::ResumeCurrentThread()
{
	return mpBackend->ResumeThread( mpCurrentThread->GetId() );
}

// utilities called by mirror classes for loading 
// of their internal infos on-demand

bool wxsDebuggerEngine::LoadMethods( wxsDbgCompositeType& ctype )
{
	return mpBackend->LoadMethods( &ctype );
}

bool wxsDebuggerEngine::LoadFields( wxsDbgCompositeType& ctype )
{
	return mpBackend->LoadFields( &ctype );
}

bool wxsDebuggerEngine::LoadDeclaringType( wxsDbgField& field )
{
	wxsDbgType* pType = FindTypeBySignature( field.GetSignature() );

	field.SetDeclaringType( pType );

	return pType != NULL;
}

bool wxsDebuggerEngine::LoadLines( wxsDbgMethod& mtd )
{
	wxsDbgMirrorListT lst; lst.Add( &mtd );

	mpBackend->LoadLineTables( lst );

	return TRUE;
}

bool wxsDebuggerEngine::LoadArguments( wxsDbgMethod& mtd )
{
	// TBD::
	return TRUE;
}

bool wxsDebuggerEngine::ResolveLocation( wxsDbgLocation& loc )
{
	return mpBackend->ResolveLocation( loc );
}

bool wxsDebuggerEngine::WaitForEvents()
{
	return mpBackend->WaitForEvents();
}

// lower-level queries invoked by back-end plugins

wxsDbgType* wxsDebuggerEngine::FindTypeById( DbgObjId_t typeId )
{
	wxsDbgType* pType = (wxsDbgType*)mTypeIdsHash.Get( (long)typeId );

	return pType;
}

bool wxsDebuggerEngine::AddType( wxsDbgType* pType )
{
	if ( mTypeIdsHash.Get( (long)pType->GetId() ) != NULL )
	{
		delete pType;
		return FALSE;
	}

	mTypes.Add( pType );
	mTypeIdsHash.Put( (long)pType->GetId(), (wxObject*)pType );
	mTypeNamesHash.Put( pType->GetSignature(), (wxObject*)pType );

	return TRUE;
}

//////////////////////////////////////////////////////////
// Notifications received from backend-debugger plugins	//
//////////////////////////////////////////////////////////

#define NOTIFY_DBG_LISTENERS( msg )                  \
	for( size_t i = 0; i != mListeners.Count(); ++i ) \
		mListeners[i]->msg;

void wxsDebuggerEngine::OnThreadStarted( DbgObjId_t threadId )
{
	wxsDbgThread* pThread = new wxsDbgThread();
	pThread->SetId( threadId );

	mAllThreads.Add( pThread );

	NOTIFY_DBG_LISTENERS( OnThreadStarted( pThread ) )
}

void wxsDebuggerEngine::OnThreadFinished( DbgObjId_t threadId )
{
    size_t i;
	for( i = 0; i != mAllThreads.Count(); ++i )
	{
		wxsDbgThread* pThread = ((wxsDbgThread*)mAllThreads[i]);
        size_t p = i;
		if ( pThread->GetId() == threadId )
		{
			NOTIFY_DBG_LISTENERS( OnThreadFinished( pThread ) )

			delete pThread;
			mAllThreads.Remove( p );
			break;
		}
	}
}

void wxsDebuggerEngine::OnThreadResumed( DbgObjId_t threadId )
{
}

void wxsDebuggerEngine::OnThreadSuspended( DbgObjId_t threadId )
{
}

void wxsDebuggerEngine::OnActiveThreadSet( DbgObjId_t threadId )
{
}

void wxsDebuggerEngine::OnBreakpointHit( DbgObjId_t     brkId, 
										 DbgObjId_t     threadId,
										 wxsDbgLocation where )
{
	wxsDbgBreakpoint* pBrk = FindBreakpointById( brkId );
	wxsDbgThread* pThread  = FindThreadById( threadId );

	NOTIFY_DBG_LISTENERS( OnBreakpointHit( pBrk, pThread ) )
}

void wxsDebuggerEngine::OnCurrentStackFrameChanged( DbgObjId_t frameId, int no )
{
	// TBD:: perhaps not necessary? FramePushed/Popped will do
}

void wxsDebuggerEngine::OnStepCompleted( DbgObjId_t threadId, wxsDbgLocation where )
{
	wxsDbgThread* pThread  = FindThreadById( threadId );

	NOTIFY_DBG_LISTENERS( OnStepCompleted( pThread, where ) )
}

void wxsDebuggerEngine::OnProcessStarted( int pid )
{
}

void wxsDebuggerEngine::OnProcessTerminated( int pid )
{
}

void wxsDebuggerEngine::OnStackFramePushed( DbgObjId_t threadId, wxsDbgLocation where )
{
}

void wxsDebuggerEngine::OnStackFramePopped( DbgObjId_t threadId, wxsDbgLocation where )
{
}

bool wxsDebuggerEngine::StopDebugger()
{
	mpBackend->StopProcess();
	return TRUE;
}

bool wxsDebuggerEngine::DetachDebugger()
{
	mpBackend->DetachFromProcess();
	return TRUE;
}

wxsDbgBreakpoint* wxsDebuggerEngine::FindBreakpointById( DbgObjId_t brkPointId )
{
	// FOR NOW:: slow loops

    size_t i;
	for( i = 0; i != mBreakpoints.Count(); ++i )
    {
		if ( mBreakpoints[i]->GetId() == brkPointId )
			return (wxsDbgBreakpoint*)mBreakpoints[i];
    }
	return NULL;
}

wxsDbgThread* wxsDebuggerEngine::FindThreadById( DbgObjId_t threadId )
{
	// FOR NOW:: slow loops

    size_t i;
	for( i = 0; i != mAllThreads.Count(); ++i )
    {
		if ( mAllThreads[i]->GetId() == threadId )
			return (wxsDbgThread*)mAllThreads[i];
    }
	return NULL;
}

void wxsDebuggerEngine::SetLastError( const wxString& msg )
{
	mLastErrorMsg = msg;
}

wxString wxsDebuggerEngine::GetLastError()
{
	return mLastErrorMsg;
}

bool wxsDebuggerEngine::AttachToProcess( const wxString& args )
{
	return mpBackend->AttachToProcess( args );
}

bool wxsDebuggerEngine::StartProcess( const wxString& args )
{
	return mpBackend->StartProcess( args );
}

bool wxsDebuggerEngine::StopProcess()
{
	return mpBackend->StopProcess();
}

bool wxsDebuggerEngine::RunProcess()
{
	return mpBackend->RunProcess();
}

/***** Implementation for class wxsDebuggerBackendPlugin *****/

// #include <process.h>

wxsJDWPBackend::wxsJDWPBackend()

	: mClassesPreloaded( FALSE )
{}

wxsJDWPBackend::~wxsJDWPBackend()
{
}

bool wxsJDWPBackend::AttachToProcess( const wxString& args )
{
	wxString ip, port;

	size_t i;
	for( i = 0; args[i] != ':'; ++i )
		ip += args[i];

	for( ++i; i != args.length(); ++i )
		ip = args[i];

	int p = 0;
	sscanf( (const char*)port, "%d", &p );

	wxIPV4address addr;
	addr.Hostname( ip );
	addr.Service( p );

	mSock.Connect( addr );

	mSock.SetFlags( wxSOCKET_BLOCK ); // prevents wxSocketBase from calling wxYield() 
									  // for blocking operations, otherwise
									  // we have a deadlock between worker
									  // thread doing blocking call and gui-thread
									  // waiting for a packet to arrive

	if ( !mSock.Ok() ) return FALSE;

	//launch_thread( *this, &wxsJDWPBackend::PacketReceiverThread   );

	return TRUE;
}

#ifdef __WINDOWS__
	#include "process.h"
#endif

bool wxsJDWPBackend::StartProcess( const wxString& args )
{
// FIXME:: HACK::
#ifdef __WINDOWS__
	mDbgPID = _spawnl( _P_NOWAIT, "java", "java",
					    "-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=8000",
					    (const char*)args, // fully qualified class name
						NULL
					  );
#endif

#if 0
	mDbgPID = _execlp( "msdev.exe", "msdev.exe", "", NULL );
#endif
	
	if ( mDbgPID == -1 ) return FALSE;

	// connecting 
	// FOR NOW:: just testing..

	wxIPV4address addr;
	addr.Hostname( "127.0.0.1" );
	addr.Service( 8000 );

	mSock.Connect( addr );

	if ( !mSock.IsConnected() ) return FALSE;

	// performing undocumented handshake

	wxString shake = "JDWP-Handshake";
	mSock.Write( shake, shake.Length() );
	
	char buf[43] = "";

	size_t len = 0;

	while( len < 43 )
	{
		mSock.Read( buf+len, 43 );

		if ( mSock.LastCount() == 0 ) return FALSE;

		if ( buf[0] != 'J' ) return FALSE;

		len += mSock.LastCount();
	}

	// ok, now we're ready

	//launch_thread( *this, &wxsJDWPBackend::PacketReceiverThread   );

	this->Create();

	this->Run();

	bool running = IsRunning();

	return TRUE;
}

bool wxsJDWPBackend::RunProcess()
{
	wxsJDWPPacket req( CS_VirtualMachine, CMD_ResumeVM ), resp;

	WaitForResponse( SendPacket( req ), resp );

	if ( !mClassesPreloaded )
	
		LoadAllClasses();

	return TRUE;
}

void wxsJDWPBackend::LoadAllClasses()
{
	if ( mClassesPreloaded ) return;

	mClassesPreloaded = TRUE;

	wxsJDWPPacket req( CS_VirtualMachine, CMD_AllClasses ), resp;

	WaitForResponse( SendPacket( req ), resp );

	int n = resp.ReadInt();

    int i;
	for( i = 0; i != n; ++i )
	{
		int refTag = (int)resp.ReadByte();
		DbgObjId_t refTypeId = resp.ReadObjId();
		wxString sign = resp.ReadString();
		int status = resp.ReadInt();

		wxsDbgCompositeType* pType = new wxsDbgCompositeType();

		pType->SetId( refTypeId );
		pType->SetTypeCode( wxsDbg_COMPOSITE_TYPE );
		pType->SetFlags( 0 );
		pType->SetSignature( sign );

		mpEngine->AddType( pType );
	}
}

void wxsJDWPBackend::DetachFromProcess()
{
	// TBD::
}

bool wxsJDWPBackend::StopProcess()
{
	// TBD::
	return 0;
}

wxString wxsJDWPBackend::GetDebuggerBackendVersion()
{
	wxsJDWPPacket req( CS_VirtualMachine, CMD_Version ), resp;

	SendPacket( req );

	//return "V2";
	WaitForResponse( SendPacket( req ), resp );

	char buf[32];
	wxString desc, vmVer, vmName;
	int jwpdMinor = 0, jwpdMajor = 0;

	desc      = resp.ReadString();
	jwpdMajor = resp.ReadInt();
	jwpdMinor = resp.ReadInt();
	vmVer     = resp.ReadString();
	vmName    = resp.ReadString();

	sprintf( buf, "%d.%d", jwpdMajor, jwpdMinor );

	return "JWPD-Version: " + wxString( buf ) + " vmVersion: " + vmVer +
		   " vmName: " + vmName + " Description: " + desc;
}

void wxsJDWPBackend::SetFrontend( wxsDebuggerEngine* pFrontend )
{
	mpEngine = pFrontend;
}

bool wxsJDWPBackend::AddBreakpoint( const wxsDbgLocation& where, 
									DbgObjId_t& brkPointId, int hitCount )
{
	if ( !where.IsResolved() &&
		 !where.Resolve() )

		 return FALSE;

	wxsJDWPPacket req( CS_EventRequest, CMD_Set ), resp;

	req.WriteByte( EVT_BREAKPOINT );
	req.WriteByte( SPOLICY_EVENT_THREAD );
	req.WriteInt ( ( hitCount == 0 ) ? 1 : 2 ); // # of modifies
	req.WriteByte( EMODIFIER_LOCATION_ONLY );
	req.WriteLocation( where );

	if ( hitCount != 0 )
	{
		req.WriteByte( EMODIFIER_COUNT );
		req.WriteInt( hitCount );
	}

	WaitForResponse( SendPacket( req ), resp );

	int err = resp.GetErrorCode();

	return TRUE;
}

bool wxsJDWPBackend::RemoveBreakpoint( DbgObjId_t brkPointId )
{
	// FIXME::
	
	wxsJDWPPacket req( CS_EventRequest, CMD_ClearAllBreakpoints );

	req.WriteByte( EVT_BREAKPOINT );
	req.WriteInt( brkPointId );
	SendPacket( req );

	return TRUE;
}

bool wxsJDWPBackend::RemoveAllBreakpoints()
{
	wxsJDWPPacket req( CS_EventRequest, CMD_ClearAllBreakpoints );
	SendPacket( req );

	return TRUE;
}

bool wxsJDWPBackend::StepThread( DbgObjId_t threadId, wxsDbgStepTypesEnum stepType )
{
	wxsJDWPPacket req( CS_EventRequest, CMD_Set );

	req.WriteByte( EVT_SINGLE_STEP );
	req.WriteByte( SPOLICY_EVENT_THREAD ); // suspend thread, which has completed
										   // this step request

	req.WriteInt ( 1 ); // one modfier

	int depth = 0;

	// JWPD's step depth constants:
	//
	// INTO = 0,
	// OVER = 1,
	// OUT  = 2

	// FIXME:: what's the difference between STEP_LINE and STEP_OVER??

	switch ( stepType ) 
	{
		case wxsDbg_STEP_INTO : depth = 0;break;
		case wxsDbg_STEP_LINE : depth = 1;break;
		case wxsDbg_STEP_OUT  : depth = 2;break;
		case wxsDbg_STEP_OVER : depth = 1;break;
		default: ;break;
	}

	req.WriteByte( EMODIFIER_STEP );
	req.WriteObjId( threadId );
	req.WriteInt( 1 );  // step size ( MIN - 0, LINE - 1 )
	req.WriteInt( depth );

	SendPacket( req );

	return TRUE;
}

bool wxsJDWPBackend::InterruptThread( DbgObjId_t threadId )
{
	wxsJDWPPacket req( CS_ThreadReference, CMD_Interrupt );
	req.WriteObjId( threadId );

	return TRUE;
}

bool wxsJDWPBackend::SuspendThread( DbgObjId_t threadId )
{
	wxsJDWPPacket req( CS_ThreadReference, CMD_Suspend );
	req.WriteObjId( threadId );

	return TRUE;
}

bool wxsJDWPBackend::ResumeThread( DbgObjId_t threadId )
{
	wxsJDWPPacket req( CS_ThreadReference, CMD_Resume );
	req.WriteObjId( threadId );

	return TRUE;
}

bool wxsJDWPBackend::GetAllThreads( wxsDbgObjectIdListT& lst )
{
	wxsJDWPPacket req( CS_VirtualMachine, CMD_AllThreads ), resp;

	WaitForResponse( SendPacket( req ), resp );

	int nThreads = resp.ReadInt();

	while( nThreads-- )
	
		lst.Add( resp.ReadObjId() );

	return TRUE;
}

bool wxsJDWPBackend::GetAllThreadGroups( wxsDbgObjectIdListT& lst )
{
	wxsJDWPPacket req( CS_VirtualMachine, CMD_TopLevelThreadGroups ), resp;

	WaitForResponse( SendPacket( req ), resp );

	int nThreads = resp.ReadInt();

	while( nThreads-- )
	
		lst.Add( resp.ReadObjId() );

	return TRUE;
}

bool wxsJDWPBackend::GetThreadName( DbgObjId_t threadId, wxString& name )
{
	wxsJDWPPacket req( CS_ThreadReference, CMD_ThreadName ), resp;
	req.WriteObjId( threadId );

	WaitForResponse( SendPacket( req ), resp );

	name = resp.ReadString();

	return TRUE;
}

bool wxsJDWPBackend::GetThreadGroupName( DbgObjId_t groupId, wxString& name )
{
	wxsJDWPPacket req( CS_ThreadGroupReference, CMD_GroupName ), resp;
	req.WriteObjId( groupId );

	WaitForResponse( SendPacket( req ), resp );

	name = resp.ReadString();

	return TRUE;
}

int wxsJDWPBackend::GetThreadStatus( DbgObjId_t threadId )
{
	wxsJDWPPacket req( CS_ThreadReference, CMD_Status ), resp;
	req.WriteObjId( threadId );

	WaitForResponse( SendPacket( req ), resp );

	int status = resp.ReadInt();
	int suspendState = resp.ReadInt();

	if ( suspendState == 0x1 ) return wxsDbg_THREAD_SUSPENDED;

	// convert from JWPD to constants of generic dbg.plugin

	switch( status )
	{
		case THREAD_RUNNING  : return wxsDbg_THREAD_RUNNING;
		case THREAD_WAIT     : return wxsDbg_THREAD_WAITING;
		case THREAD_SLEEPING : return wxsDbg_THREAD_SLEEPING;
		case THREAD_ZOMBIE   : return wxsDbg_THREAD_ZOMBIE;
		case THREAD_MONITOR  : return wxsDbg_THREAD_MONITOR;
		default : return -1;
	}

	return -1;
}

int wxsJDWPBackend::GetStackFrameCount( DbgObjId_t threadId )
{
	wxsJDWPPacket req( CS_ThreadGroupReference, CMD_FrameCount ), resp;
	req.WriteObjId( threadId );

	WaitForResponse( SendPacket( req ), resp );

	return resp.ReadInt();
}

bool wxsJDWPBackend::GetStackFrames( DbgObjId_t threadId,
											   size_t from, size_t till, 
											   wxsDbgMirrorListT& frames )
{
	wxsJDWPPacket req( CS_ThreadGroupReference, CMD_Frames ), resp;
	req.WriteObjId( threadId );
	req.WriteInt( from );
	req.WriteInt( till - from );

	WaitForResponse( SendPacket( req ), resp );

	int nFrames = req.ReadInt();

	while( nFrames-- )
	{
		wxsDbgStackFrame* pFrm = new wxsDbgStackFrame();

		pFrm->SetId( req.ReadObjId() );
		pFrm->SetLocation( req.ReadLocation() );

		frames.Add( pFrm );
	}

	return TRUE;
}

bool wxsJDWPBackend::WaitForEvents()
{
	mPacketReceivedEvent.Wait();

	return TRUE;
}

bool wxsJDWPBackend::ResolveLocation( wxsDbgLocation& where )
{
	switch( where.GetResolutionState() )
	{
		case wxsDbgLocation::RESOLVED_FOR_FRONTEND :
		{
			// now resolve for backend
		}

		case wxsDbgLocation::RESOLVED_FOR_BACKEND :
		{
			wxString text;

			if ( wxPMBootSvc().GetWorkplace()->GetActiveProject()->
					GetFileTextContent( where.GetFile(), text ) )
			{
				// FOR NOW:: very primitive and unsafe parsing of source
				//			 to find out class and package names

				unsigned int pos = text.Find( "package " );

				if ( (int)pos == -1 ) break;

				wxString package;

				pos += 8; // strlen("package ")

				while( (int)pos < (signed)text.Length() && text[pos] != ';' )

					package += text[pos++];

				pos = text.Find( "public class " );

				if ( (int)pos == -1 ) break;

				pos += 13; // strlen("public class ")

				wxString className;

				while( pos < text.Length() && 
					   text[pos] != ' ' && text[pos] != 13 && text[pos] != 10 )

					   className += text[pos++];

				// TBD:: class vs. interface distinction

				wxString singnature = "L" + package + "/" + className + ";";

				wxsDbgType* pType = mpEngine->FindTypeBySignature( singnature );

				wxASSERT( pType->GetTypeCode() == wxsDbg_COMPOSITE_TYPE ); // DBG::

				wxsDbgCompositeType* pClass = (wxsDbgCompositeType*)pType;

				wxsDbgMirrorListT& mtdLst = pClass->GetMethods();

				int searchedLine = where.GetLine();

				bool found = FALSE;

                size_t i;
				for( i = 0; i != mtdLst.Count(); ++i )
				{
					wxsDbgMethod& mtd = *(wxsDbgMethod*)mtdLst[i];

					wxArrayInt lines        = mtd.GetLines();
					wxArrayInt instructions = mtd.GetCodeIndexes();

                    unsigned int k;
					for( k = 0; k != lines.Count(); ++k )
					{
						int l = lines[k];
						if ( l == searchedLine )
						{
							where.SetupForBackend( pType->GetId(), mtd.GetId(), instructions[k] );

							where.SetResolutionState( wxsDbgLocation::RESOLVED );

							found = TRUE;

							break;
						}
					}
					
					if ( found ) break;
				}

				if ( found ) return TRUE;

			}
			// now resolve for frontend
		}

		default : break;
	}

	return FALSE;
}

wxsDbgType* wxsJDWPBackend::LoadTypeBySignature( const wxString& sign )
{
	wxsJDWPPacket req( CS_VirtualMachine, CMD_ClassesBySignature ), resp;

	req.WriteString( sign );
//req.WriteString( "Ljava/lang/String;" );

	WaitForResponse( SendPacket( req ), resp );

	int n = resp.ReadInt();

    int i;
	for( i = 0; i != n; ++i )
	{
		if ( i == 0 )
		{
			int typeTag     = (int)resp.ReadByte();
			DbgObjId_t refTypeId = resp.ReadObjId();
			int status           = resp.ReadInt();

			if ( typeTag == TAG_CLASS_TYPE ||
				 typeTag == TAG_INTERFACE_TYPE )
			{
				wxsDbgCompositeType* pType = new wxsDbgCompositeType();

				pType->SetId( refTypeId );
				pType->SetTypeCode( wxsDbg_COMPOSITE_TYPE );
				pType->SetSignature( sign );

				return pType;

				// TBD:: retrieve modifiers
			}
			else
			{
				wxsDbgType* pType = new wxsDbgCompositeType();

				int code = -1;

				switch( typeTag )
				{
					case TAG_BYTE    : code = wxsDbg_BYTE_TYPE; break;
					case TAG_CHAR    : code = wxsDbg_CHAR_TYPE; break;
					case TAG_FLOAT   : code = wxsDbg_FLOAT_TYPE; break;
					case TAG_DOUBLE  : code = wxsDbg_DOUBLE_TYPE; break;
					case TAG_INT     : code = wxsDbg_INT_TYPE; break;
					case TAG_LONG    : code = wxsDbg_LONG_TYPE; break;
					case TAG_SHORT   : code = wxsDbg_SHORT_TYPE; break;
					case TAG_VOID    : code = wxsDbg_VOID_TYPE; break;
					case TAG_BOOLEAN : code = wxsDbg_BOOLEAN_TYPE; break;
					case TAG_STRING  : code = wxsDbg_STRING_TYPE; break;

					default : code = wxsDbg_UNKNOWN_TYPE; break;
				}

				pType->SetId( refTypeId );
				pType->SetTypeCode( code);
				pType->SetSignature( sign );

				return pType;
			}
		}
		else break;
	}

	return NULL;
}

bool wxsJDWPBackend::LoadMethods( wxsDbgCompositeType* pType )
{
	wxsJDWPPacket req( CS_ReferenceType, CMD_Methods ), resp;

	req.WriteObjId( pType->GetId() );

	WaitForResponse( SendPacket( req ), resp );

	unsigned int n = resp.ReadInt();

	wxsDbgMirrorListT lst;

    size_t i;
	for( i = 0; i != n; ++i )
	{
		wxsDbgMethod* pMethod = new wxsDbgMethod();

		DbgObjId_t id = resp.ReadMethodId();
		wxString name = resp.ReadString();
		wxString sign = resp.ReadString();
		int mods      = resp.ReadInt();

		pMethod->SetId( id );
		pMethod->SetName( name );
		pMethod->SetSignature( sign );

		pMethod->SetContainingType( pType );

		lst.Add( pMethod );
	}

	pType->SetMethods( lst );

	return TRUE;
}

bool wxsJDWPBackend::LoadFields( wxsDbgCompositeType* pType )
{
	wxsJDWPPacket req( CS_ReferenceType, CMD_Fields ), resp;

	req.WriteObjId( pType->GetId() );

	WaitForResponse( SendPacket( req ), resp );

	unsigned int n = resp.ReadInt();

	wxsDbgMirrorListT lst;

    size_t i;
	for( i = 0; i != n; ++n )
	{
		wxsDbgField* pField = new wxsDbgField();

		pField->SetContainingType( pType );

		DbgObjId_t id = resp.ReadObjId();
		wxString name = resp.ReadString();
		wxString sign = resp.ReadString();
		int mods      = resp.ReadInt();

		pField->SetId( id );
		pField->SetName( name );
		pField->SetSignature( sign );

		lst.Add( pField );
	}

	pType->SetFields( lst );

	return TRUE;
}

bool wxsJDWPBackend::LoadLineTables( wxsDbgMirrorListT& forMethods )
{
	// TBD:: doing req/response per method is slow, implement
	//		 batch sending/retrieval

    unsigned int i;
	for( i = 0; i != forMethods.Count(); ++i )
	{
		wxsJDWPPacket req( CS_Method, CMD_LineTable ), resp;

		wxsDbgMethod& mtd = *(wxsDbgMethod*)forMethods[i];

		req.WriteObjId( mtd.GetContainingType()->GetId() );
		req.WriteMethodId( mtd.GetId() );

		WaitForResponse( SendPacket( req ), resp );

		long start  = resp.ReadLong();
		long end    = resp.ReadLong();
		long nLines = resp.ReadInt();

		wxArrayInt lines;
		wxArrayInt codeIndexes;

        int k;
		for( k = 0; k != nLines; ++k )
		{
			long idx  = resp.ReadLong();
			int  line = resp.ReadInt();

			codeIndexes.Add( idx );
			lines.Add( line );
		}

		mtd.SetLines( lines );
		mtd.SetCodeIndexes( codeIndexes );
	}

	return TRUE;
}

/*** protected methods ***/

void wxsJDWPBackend::PacketReceiverThread()
{
	/*

	// NOTE:: size of single packet should not
	//        exceed the size of buffer!

	char buf[16384];
	size_t  head = 0;

	for(;;)
	{
		wxMutexLocker lock( mIPCChannelLock );

		mSock.Read( buf + head, sizeof(buf) - head );

		if ( mSock.LastError() == wxSOCKET_NOERROR )
		{		
			if ( !mSock.Ok() )
			{
				// TBD:: reasons
				this->StopDeliveryThread( -1 );

				return; // terminates receiver thread
			}
			else
			// otherwise interrupt request was received
			{
				mIPCChannelLock.release();

				mResumeEvent.WaitEvent();

				continue;
			}
		}
		else
			break; // TBD:: error handling

		head += nRead;

		ParsePackets( buf, head );
	}
	*/
}

void wxsJDWPBackend::DeliverPacket( wxsJDWPPacket& pkt )
{
	DbgObjId_t respId = pkt.GetPacketId();

	// chack if anyone's got desired reponses
	
	{
		wxMutexLocker lock( mRequestsLock );

        size_t n;
		for( n = 0; n != mOutgoingRequests.Count(); ++n )

			if ( mOutgoingRequests[n] == respId )
			{
				// FIXME:: reponsce packets should be placed to queue

				mRespPacket = pkt;
				mPacketReceivedEvent.Broadcast();
				mOutgoingRequests.Remove( n );
				break;
			}
	}

	// deliver notifications about arrived events if any

	if ( pkt.GetCommandSetId() == CS_Event &&
		 pkt.GetCommandId()    == CMD_Composite )
	{
		int suspendPolicy = pkt.ReadByte();
		int nEvents       = pkt.ReadInt();

		bool skip = FALSE;
		while( nEvents-- )
		{
			int evtKind = pkt.ReadByte();

			switch ( evtKind )
			{
				case EVT_VM_DISCONNECTED:
				{ 
					int reqId = pkt.ReadInt();

					// FOR NOW:: treat it as process termination
					mpEngine->OnProcessTerminated( -1 ); // TBD:: pid
					break;
				}

				case EVT_METHOD_EXIT:
				{ 
					int        reqId     = pkt.ReadInt();
					DbgObjId_t threadId  = pkt.ReadObjId();
					wxsDbgLocation where = pkt.ReadLocation();

					mpEngine->OnStackFramePopped( threadId, where );
					break;
				}

				case EVT_EXCEPTION_CATCH:
				{ skip = TRUE;
				}

				case EVT_METHOD_ENTRY:
				{ 
					int        reqId     = pkt.ReadInt();
					DbgObjId_t threadId  = pkt.ReadObjId();
					wxsDbgLocation where = pkt.ReadLocation();

					mpEngine->OnStackFramePushed( threadId, where );
					break;
				}

				case EVT_VM_DEATH:
				{ 
					int reqId = pkt.ReadInt();

					mpEngine->OnProcessTerminated( -1 ); // TBD:: pid
					break;
				}

				case EVT_CLASS_UNLOAD:
				{ skip = TRUE;
				}

				case EVT_CLASS_PREPARE:
				{ skip = TRUE;
				}

				case EVT_SINGLE_STEP:
				{ 
					int        reqId     = pkt.ReadInt();
					DbgObjId_t threadId  = pkt.ReadObjId();
					wxsDbgLocation where = pkt.ReadLocation();

					mpEngine->OnStepCompleted( threadId, where );
					break;
				}

				case EVT_FIELD_MODIFICATION:
				{ skip = TRUE;
				}

				case EVT_CLASS_LOAD:
				{ skip = TRUE;
				}

				case EVT_THREAD_START:
				{ 
					int        reqId     = pkt.ReadInt();
					DbgObjId_t threadId  = pkt.ReadObjId();

					mpEngine->OnThreadStarted( threadId );
					break;
				}

				case EVT_FRAME_POP:
				{ skip = TRUE;
				}

				case EVT_VM_INIT:
				{ 
					int        reqId     = pkt.ReadInt();
					DbgObjId_t threadId  = pkt.ReadObjId(); // initial thread

					mpEngine->OnProcessStarted( -1 ); // TBD:: pid
				}

				case EVT_BREAKPOINT:
				{
					int        reqId     = pkt.ReadInt();
					DbgObjId_t threadId  = pkt.ReadObjId();
					wxsDbgLocation where = pkt.ReadLocation();

					mpEngine->OnBreakpointHit( (DbgObjId_t)reqId, threadId, where );
					break;
				}

				case EVT_THREAD_END:
				{ 
					int        reqId     = pkt.ReadInt();
					DbgObjId_t threadId  = pkt.ReadObjId();

					mpEngine->OnThreadFinished( threadId );
					break;
				}

				case EVT_FIELD_ACCESS:
				{ skip = TRUE;
				}

				case EVT_EXCEPTION:
				{ skip = TRUE;
				}

				default: skip = TRUE; break;

			} // end of swtich( evtKind )

			if ( skip ) break; // ignore reminding events in the set

		} // end of while(..)

	} // end of if (...)
}

void wxsJDWPBackend::SuspendReceiver()
{
	//wxMiniSocket::CancelBlockingCall();
}

void wxsJDWPBackend::ResumeReceiver()
{
	mResumeEvent.Broadcast();
}

void wxsJDWPBackend::StopDeliveryThread( int reason )
{
	// TBD::
}

int wxsJDWPBackend::SendPacket( wxsJDWPPacket& packet )
{
	packet.FinalizePacket();

	// unblock receiver from Read() call temporarely
	// FIXME:: is this really needed?

	//SuspendReceiver();

	{
		// request exclusive access to socket

		//Guard g( mIPCChannelLock );

		mSock.Write( packet.GetPacketData(), packet.GetPacketData().Length() );
	}

	//ResumeReceiver();

	return packet.GetPacketId();
}

bool wxsJDWPBackend::WaitForResponse( int reqPacketId, wxsJDWPPacket& respPacket )
{
	for(;;)
	{
		mPacketReceivedEvent.Wait();

		{
			wxMutexLocker removealLock( mPacketRomovalLock );

			{
				wxMutexLocker queueLock( mPacketQueueLock );

                size_t i;
				for( i = 0; i != mPacketQueue.Count(); ++i )
				{
					if ( mPacketQueue[i]->GetPacketId() == reqPacketId )
					{
						respPacket = *mPacketQueue[ i ];
						delete mPacketQueue[i];

						mPacketQueue.Remove( i );

						return TRUE;
					}
				}
			}
		}
	}
}

bool wxsJDWPBackend::GetClassSignatureFromFile( const wxPMFileInfo& file )
{
	return TRUE;
}

void* wxsJDWPBackend::Entry()
{
	char buf[16*1024];
	size_t head = 0;

	wxMutexLocker lock( mIPCChannelLock );

	wxsJDWPPacket* pCurPacket = new wxsJDWPPacket();

	for(;;)
	{
		mSock.SetFlags( wxSOCKET_BLOCK );
		mSock.Read( buf + head, sizeof(buf) - head );

		if ( mSock.LastError() != wxSOCKET_NOERROR )

			break;

		size_t remains = head + mSock.LastCount();

		char* cur = buf;

		{
			wxMutexLocker lock( mPacketQueueLock );

			for(; remains > 0;)
			{
				if ( !pCurPacket->ParseStream( cur, remains ) )
				
					break;

				remains -= pCurPacket->GetSize();
				cur += pCurPacket->GetSize();
				
				mPacketQueue.Add( pCurPacket );

				pCurPacket = new wxsJDWPPacket();
			}
		}

		mPacketReceivedEvent.Broadcast();

		if ( cur != buf && remains != 0 )
		
			memmove( buf, cur, remains );

		head = remains;
	}

	return NULL;

/*
	size_t diff = buf + head - cur;

	if ( diff != 0 )
	{
		memmove( buf, cur, diff );

		head = diff;
	}
	else
		head = 0;
		*/
}

/***** Implementation for class wxsJDWPPacket *****/

int wxsJDWPPacket::mPacketGen = 100;

wxsJDWPPacket::wxsJDWPPacket()

	: mErrorCode( 0 ),
	  mCmdSetId( -1 ),
	  mCmdId( -1 )
{}

wxsJDWPPacket::wxsJDWPPacket( int cmdSetId, int cmdId )

	: mErrorCode( 0 )
{
	Create( cmdSetId, cmdId );
}

void wxsJDWPPacket::Create( int cmdSetId, int cmdId )
{
	mCmdSetId = cmdSetId;
	mCmdId    = cmdId;
	mPacketId = mPacketGen++;
}

// FOR NOW::

// #include <winsock.h>

typedef long my_int32;

static my_int32 read_int32( char* buf )
{
	return ntohl ( *((long*)buf) );
}

static void write_int32( my_int32 l, char*buf )
{
	*((long*)(buf)) = htonl( l );
}

size_t wxsJDWPPacket::GetSize()
{
	return mData.Length();
}

bool wxsJDWPPacket::ParseStream( char* buf, size_t len )
{
	if ( len < 4 ) return FALSE;

	size_t packetLen = (size_t)read_int32( buf );

	if ( packetLen > len ) return FALSE;

	mPacketId = read_int32( buf + 4 );

	mFlags = *( buf + 8 );

	mErrorCode = ntohs( *((unsigned short*)(buf + 9)) );

	mData = "";
	mData.append( buf, packetLen );

	mReadHead = 11;

	return TRUE;
}

void wxsJDWPPacket::WriteByte( char ch )
{
	mData += ch;
}

void wxsJDWPPacket::WriteInt( int i )
{
	char buf[ sizeof(int) ];

	write_int32( i, buf ); // FIXME:: FOR NOW:: sizeof(int) == sizeof(my_int32) assumed!

	mData.append( buf, sizeof(buf) );		
}

void wxsJDWPPacket::WriteLong( long l )
{
	// FIXME:: wrong wrong...

	WriteInt( 0 );
	WriteInt( (int)l );
}

void wxsJDWPPacket::WriteObjId( DbgObjId_t id )
{
	// FIXME:: wrong wrong...

	WriteInt( (int)0 );

	WriteInt( (int)id );
}

void wxsJDWPPacket::WriteMethodId( DbgObjId_t id )
{
	// FIXME:: wrong wrong...

	WriteInt( (int)id );
}

void wxsJDWPPacket::WriteString( const wxString& s )
{
	WriteInt( s.length() );
	mData += s;
}

void wxsJDWPPacket::WriteLocation( const wxsDbgLocation& where )
{
	WriteByte( TAG_CLASS_TYPE ); // FOR NOW::
	WriteObjId( where.GetClassId() );
	WriteMethodId( where.GetMethodId() );
	WriteLong ( where.GetInstructionId() );
}

wxsDbgLocation wxsJDWPPacket::ReadLocation()
{
	wxsDbgLocation where;

	char typeTag = (char)ReadByte();

	where.SetupForBackend( ReadObjId(),     // classId
						   ReadObjId(),     // methodId
						   ReadLong()   );  // instructionId

	return where;
}

unsigned char wxsJDWPPacket::ReadByte()
{
	return mData[mReadHead++];
}

int	wxsJDWPPacket::ReadInt()
{
	int i =  read_int32( ((char*)(const char*)mData) + mReadHead );

	mReadHead += sizeof( my_int32 );

	return i;
}

long wxsJDWPPacket::ReadLong()
{
	// FIXME:: wrong wrong...

	long l1 = (long)ReadInt();

	long l2 = (long)ReadInt();

	return l2;
}

DbgObjId_t wxsJDWPPacket::ReadObjId()
{
	// FIXME:: wrong wrong...

	int i1 =  ReadInt();
	int i2 =  (DbgObjId_t)ReadInt();

	return i2;
}

DbgObjId_t wxsJDWPPacket::ReadMethodId()
{
	// FIXME:: wrong wrong...

	return ReadInt();
}

wxString wxsJDWPPacket::ReadString()
{
	size_t len = ReadInt();
	wxString s( (const char*)mData + mReadHead, len );

	mReadHead += len;

	return s;
}

void wxsJDWPPacket::FinalizePacket()
{
	char buf[11];

	write_int32( mData.length() + 11, buf + 0 );
	write_int32( mPacketId, buf + 4 );
	buf[8] = 0x0; // flags
	buf[9]  = mCmdSetId;
	buf[10] = mCmdId;

	wxString hdr;
	hdr.append( buf, 11 );
	hdr.append( mData, mData.length() );

	mData = hdr;
}

wxString& wxsJDWPPacket::GetPacketData()
{
	return mData;
}

DbgObjId_t wxsJDWPPacket::GetPacketId()
{
	return mPacketId;
}

bool wxsJDWPPacket::HasError()
{
	return mErrorCode != 0;
}

int wxsJDWPPacket::GetErrorCode()
{
	return mErrorCode;
}

int wxsJDWPPacket::GetCommandSetId()
{
	return mCmdSetId;
}

int wxsJDWPPacket::GetCommandId()
{
	return mCmdId;
}

/***** Implementation for class wxsDebuggerService *****/

static wxCommandInfo _gDebuggerCommands[] = {

	wxCommandInfo( wxsDebuggerService::ID_START, "&Start", "", "F5" ),
	wxCommandInfo( wxsDebuggerService::ID_END, "&End", "", "Shift+F5" ),
	wxCommandInfo( -1, "SEPARATOR" ),

	wxCommandInfo( wxsDebuggerService::ID_CONTINUE, "&Continue", "", "F6" ),
	wxCommandInfo( wxsDebuggerService::ID_BREAK, "&Break", "", "Ctrl+B" ),
	wxCommandInfo( wxsDebuggerService::ID_GOTOSOURCE, "&Goto source", "", "Ctrl+G" ),
	wxCommandInfo( -1, "SEPARATOR" ),

	wxCommandInfo( wxsDebuggerService::ID_STEP_INTO, "Step into", "", "F8" ),
	wxCommandInfo( wxsDebuggerService::ID_STEP_OVER, "Step over", "", "F10" ),
	wxCommandInfo( wxsDebuggerService::ID_TOGGLE_BREAKPOINT, "Toggle breakpoint", "", "F9" ),
	wxCommandInfo( wxsDebuggerService::ID_ADD_WATCH, "Add &watch", "", "Ctrl+W" ),
	wxCommandInfo( -1, "SEPARATOR" ),

	wxCommandInfo( wxsDebuggerService::ID_BACKEND_VERSION, "&Version.." ),

	wxCommandInfo( 0, "" ) // terminator
};

static wxPMMergableMenuChain _gDebuggerChains[] = {

	wxPMMergableMenuChain( "/&Project/&Debug/",  _gDebuggerCommands ),

	wxPMMergableMenuChain( "", NULL ) // terminator
};

IMPLEMENT_DYNAMIC_CLASS( wxsDebuggerService, wxEvtHandler )

BEGIN_EVENT_TABLE( wxsDebuggerService, wxEvtHandler )

	EVT_MENU( ID_START, wxsDebuggerService::OnStart )
	EVT_MENU( ID_CONTINUE, wxsDebuggerService::OnContinue )
	EVT_MENU( ID_BREAK, wxsDebuggerService::OnBreak )
	EVT_MENU( ID_GOTOSOURCE, wxsDebuggerService::OnGotoSource )

	EVT_MENU( ID_STEP_INTO, wxsDebuggerService::OnStepInto )
	EVT_MENU( ID_STEP_OVER, wxsDebuggerService::OnStepOver	 )
	EVT_MENU( ID_TOGGLE_BREAKPOINT, wxsDebuggerService::OnToggleBreakpoint )
	EVT_MENU( ID_ADD_WATCH, wxsDebuggerService::OnAddWatch )

	EVT_MENU( ID_BACKEND_VERSION, wxsDebuggerService::OnBackendVersion )

END_EVENT_TABLE()

wxsDebuggerService::wxsDebuggerService()
{
	SetRefData( (wxObjectRefData*)(wxPMService*)this );
}

wxsDebuggerService::~wxsDebuggerService()
{
	SetRefData( NULL );
}

bool wxsDebuggerService::Start()
{
	mpBackend = new wxsJDWPBackend();
	mpEngine = new wxsDebuggerEngine();

	mpEngine->SetBackend( mpBackend );
	mpBackend->SetFrontend( mpEngine );

	return TRUE;
}

bool wxsDebuggerService::Stop()
{
	return TRUE;
}

wxPMMergableMenuChain* wxsDebuggerService::GetMergableMenuChain()
{
	return _gDebuggerChains;
}

wxString wxsDebuggerService::GetDisplayName()
{
	return "Generic Debugger Service";
}

wxString wxsDebuggerService::GetDescription()
{
	return "Generic Debugger Service description (TBD)";
}

wxObject* wxsDebuggerService::GetWxObject()
{
	return (wxObject*)this;
}

/* event handlers */

void wxsDebuggerService::OnStart( wxCommandEvent& event )
{
	mpEngine->StartProcess("p1.zx" );

	mpEngine->RunProcess();
}

void wxsDebuggerService::OnContinue( wxCommandEvent& event )
{
}

void wxsDebuggerService::OnBreak( wxCommandEvent& event )
{
}

void wxsDebuggerService::OnGotoSource( wxCommandEvent& event )
{
}

void wxsDebuggerService::OnStepInto( wxCommandEvent& event )
{
}

void wxsDebuggerService::OnStepOver( wxCommandEvent& event )
{
}

void wxsDebuggerService::OnToggleBreakpoint( wxCommandEvent& event )
{
	wxPMDocument* pDoc = (wxPMDocument*)wxPMBootSvc().GetDocManager()->GetCurrentDocument();

	I_ContainingText* pTextInf 
		= (I_ContainingText*)pDoc->GetInterface( "I_ContainingText" );

	I_MarkableText* pMarkingInf 
		= (I_MarkableText*)pDoc->GetInterface( "I_MarkableText" );

	if ( !pTextInf || !pMarkingInf ) return;

	int line = 0, col = 0;

	pTextInf->GetCursorPosition( &line, &col );

	if ( pMarkingInf->IsMarked( I_MarkableText::BREAKPOINT, line ) )
	{
		pMarkingInf->RemoveMarkAt( I_MarkableText::BREAKPOINT, line );

		mpEngine->RemoveBreakpoint( wxsDbgLocation( *pDoc->GetFileInfo(), line+1 ) );
	}
	else
	{
		if ( mpEngine->AddBreakpoint( wxsDbgLocation( *pDoc->GetFileInfo(), line+1 ) ) )

			pMarkingInf->AddMark( I_MarkableText::BREAKPOINT, line );
	}
}

void wxsDebuggerService::OnAddWatch( wxCommandEvent& event )
{
}

void wxsDebuggerService::OnBackendVersion( wxCommandEvent& event )
{
	wxMessageBox( "Back-end debugger version:\n" + mpEngine->GetDebbugerBackendVersion() );
}
