/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     22/09/98
// RCS-ID:      $Id: parserbase.cpp,v 1.4 2001/04/21 11:46:10 georgetasker Exp $
// Copyright:   (c) Aleksandars Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
#pragma implementation "srcparser.h"
#pragma interface
#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 <malloc.h>
#include <stdio.h>

#include "parserbase.h"
#include "wx/debug.h"

/***** Implementation for class spVisitor *****/

void spVisitor::VisitAll( spContext& atContext,
						  bool sortContent
						)
{
	mSiblingSkipped = FALSE;
	mChildSkipped   = FALSE;
	mContextMask    = SP_CTX_ANY; // FIXME:: should be an arg.

//	if ( sortContent && !atContext.IsSorted() )

//		atContext.SortMembers();

	mpCurCxt = &atContext; // FIXME:: this is dirty, restoring it each time

	if ( atContext.GetContextType() & mContextMask )

		atContext.AcceptVisitor( *this );

	MMemberListT& members = atContext.GetMembers();

	for( size_t i = 0; i != members.Count(); ++i )
	{
		if ( mSiblingSkipped )
		
			return;

		if ( !mChildSkipped )
		{
			size_t prevSz = members.Count();

			// visit members of the context recursivelly
			VisitAll( *members[i], sortContent );

			if ( members.Count() != prevSz )

				--i; // current member was removed!

			mChildSkipped = 0;
		}
	}
}

void spVisitor::RemoveCurrentContext()
{
	MMemberListT& members = mpCurCxt->GetMembers();

	if ( mpCurCxt->GetParent() )

		mpCurCxt->GetParent()->RemoveChild( mpCurCxt );
}

void spVisitor::SkipSiblings()
{
	mSiblingSkipped = TRUE;
}

void spVisitor::SkipChildren()
{
	mChildSkipped = TRUE;
}

void spVisitor::SetFilter( int contextMask )
{
	mContextMask = contextMask;
}

/***** Implementation for class spComment *****/

bool spComment::IsMultiline()  const
{
	return mIsMultiline;
}

bool spComment::StartsParagraph() const
{
	return mStartsPar;
}

wxString& spComment::GetText()
{
	return mText;
}

wxString spComment::GetText() const
{
	return mText;
}

/***** Implementation for class spContext *****/

spContext::spContext()

	: mpParent        ( NULL ),
	  mFlags          ( NULL ),
	  mpFirstOccurence( NULL ),
	  mAlreadySorted  ( FALSE ),

	  mSrcLineNo    (-1),
	  mSrcOffset    (-1),
	  mContextLength(-1),

	  mpUserData ( (void*)-1 )
{}

void spContext::RemoveChildren()
{
	WX_CLEAR_ARRAY( mMembers );
}

spContext::~spContext()
{
	RemoveChildren();

	WX_CLEAR_ARRAY( mComments );
}

void spContext::SetUserData( void* pUserData )
{ 
	mpUserData = pUserData;
}

void spContext::DetachUserData()
{
	mpUserData = NULL;
}

void spContext::GetContextList( MMemberListT& lst, int contextMask )
{
	for( size_t i = 0; i != mMembers.Count(); ++i )
	{
		spContext& member = *mMembers[i];

		if ( member.GetContextType() & contextMask )

			lst.Add( &member );

		// collect required contexts recursively
		member.GetContextList( lst, contextMask );
	}
}

size_t spContext::CountMembers( int type )
{
	size_t cnt = 0;

	for( size_t i = 0; i != mMembers.Count(); ++i )

		if ( mMembers[i]->GetType() == type )
			++cnt;

	return cnt;
}

bool spContext::HasComments()
{
	return ( mComments.Count() != 0 );
}

void spContext::RemoveChild( spContext* pChild )
{
	mMembers.Remove( pChild );
	delete pChild;
}

spContext* spContext::GetEnclosingContext( int mask )
{
	spContext* cur = this->GetParent();

	while ( cur && !(cur->GetContextType() & mask) ) 
		
		cur = cur->GetParent();

	return cur;
}

#define ALPHA_NUM( ch ) ( ( ch >= 'a' && ch <= 'z' ) || \
	                      ( ch >= 'A' && ch <= 'Z' ) || \
						  ( ch >= '0' && ch <= '9' ) || \
						  ( ch == '_' ) )


wxString spContext::StripTypeModifiers( const wxString& type )
{
	wxString result;

	size_t i = 0;

	// FIXME:: other modifiers...

	if ( type.length() > 5 &&
		 type[0] == 'c' &&
		 type[1] == 'o' &&
		 type[2] == 'n' &&
		 type[3] == 's' &&
		 type[4] == 't' 
	   )
		 i += 5;

	for( ; i != type.length(); ++i )
	{
		char ch = type[i];

		if ( ALPHA_NUM(ch) ) result += ch;
	}

	return result;
}

bool spContext::PositionIsKnown()
{
	return ( mSrcOffset != (-1) && mContextLength != (-1) );
}

bool spContext::IsFirstOccurence()
{
	return ( mpFirstOccurence != 0 );
}

spContext* spContext::GetFirstOccurence()
{
	// this object should not itself be 
	// the first occurence of the context
	wxASSERT( mpFirstOccurence != 0 );

	return mpFirstOccurence;
}

void spContext::AddMember( spContext* pMember )
{
	mMembers.Add( pMember );

	pMember->mpParent = this;
}

void spContext::AddComment( spComment* pComment )
{
	mComments.Add( pComment );
}

MMemberListT& spContext::GetMembers()
{
	return mMembers;
}

spContext* spContext::FindContext( const wxString& identifier,
							       int   contextType,
								   bool  searchSubMembers
								 )
{
	for( size_t i = 0; i != mMembers.Count(); ++i )
	{
		spContext& member = *mMembers[i];

		if ( member.GetName() == identifier && 
			 ( contextType & member.GetContextType() )
		   )

		   return &member;

		if ( searchSubMembers )
		{
			spContext* result = 
				member.FindContext( identifier, contextType, 1 );

			if ( result ) return result;
		}
	}

	return NULL;
}

void spContext::RemoveThisContext()
{
	if ( mpParent )
		mpParent->RemoveChild( this );
	else
	{
		 // context should have a parent
		 int avoidCompilerWarning = 0;
		 wxASSERT(avoidCompilerWarning);
	}
}

spContext* spContext::GetOutterContext()
{
	return mpParent;
}

bool spContext::HasOutterContext()
{
	return ( mpParent != NULL );
}

bool spContext::IsInFile()
{
	return ( GetOutterContext()->GetContextType() == SP_CTX_FILE );
}

bool spContext::IsInNameSpace()
{
	return ( GetOutterContext()->GetContextType() == SP_CTX_NAMESPACE );
}

bool spContext::IsInClass()
{
	if ( !GetParent() ) return FALSE;

	return ( GetOutterContext()->GetContextType() == SP_CTX_CLASS );
}

bool spContext::IsInOperation()
{
	return ( GetOutterContext()->GetContextType() == SP_CTX_OPERATION );
}

spClass& spContext::GetClass()
{
	wxASSERT( GetOutterContext()->GetType() == SP_CTX_CLASS );
	return *((spClass*)mpParent );
}

spFile& spContext::GetFile()
{
	wxASSERT( GetOutterContext()->GetType() == SP_CTX_FILE );
	return *((spFile*)mpParent );
}

spNameSpace& spContext::GetNameSpace()
{
	wxASSERT( GetOutterContext()->GetType() == SP_CTX_NAMESPACE );
	return *((spNameSpace*)mpParent );
}

spOperation& spContext::GetOperation()
{
	wxASSERT( GetOutterContext()->GetType() == SP_CTX_OPERATION );
	return *((spOperation*)mpParent );
}

void spContext::Dump( wxTextOutputStream& stm, const wxString& indent )
{
	stm << indent
		<< "Context of type " 
		<< this->GetType() 
		<< " has it's Dump() method not implemented!\n";
}

/***** Implementation for class spAttribute *****/

void spAttribute::Dump( wxTextOutputStream& stm, const wxString& indent )
{
	stm << indent << mType << " " << mName << ";\n";
}

/***** Implementation for class spClass *****/

void spClass::SortMembers()
{
	// TBD::
}

void spClass::Dump( wxTextOutputStream& stm, const wxString& indent )
{
	stm << indent << "class " << mName;

	if ( mSuperClassNames.GetCount() )
	{
		stm << " : ";

		for( size_t i = 0; i != mSuperClassNames.GetCount(); ++i )
		{
			if ( i != 0 )

				stm << ", ";

			stm << " public " << mSuperClassNames[i];
		}
	}

	wxString indent1 = indent + "\t";

	stm << "\n" << indent << "{\n";

	for( size_t i = 0; i != mMembers.Count(); ++i )
	{
		mMembers[i]->Dump( stm, indent1 );
	}

	stm << indent << "};\n";
}

/***** Implementation for class spOperation *****/

spOperation::spOperation()
{}

spOperation::~spOperation()
{
	WX_CLEAR_ARRAY( mParams );
}

wxString spOperation::FormatSignature( bool withArgNames,
									 bool withInitialValues,
									 bool withReturnValue,
									 bool spaceArguments,
									 bool spaceBrackets
									 )
{
	wxString s = ( withReturnValue ) ? mRetType + " " + mName : mName;

	s += '(';
	if ( GetMembers().Count() && spaceBrackets ) s += ' ';
	
	for( size_t i = 0; i != GetMembers().Count(); ++i )
	{
		if ( i != 0 ) 
		{
			if ( spaceArguments ) 
					
				s += ", ";
			else
				s += ',';
		}

		spParameter* pParam = (spParameter*)GetMembers()[i];

		s += pParam->mType;

		if ( withArgNames )
		{
			s += ' ';
			s += pParam->mName;

			if ( withInitialValues && pParam->mInitVal.length() )
			{
				s += " = ";
				s += pParam->mInitVal;
			}
		}
	}

	if ( GetMembers().Count() && spaceBrackets ) s += ' ';

	s += ')';

	return s;
}

wxString spOperation::GetFullName(MarkupTagsT tags)
{
	wxString txt = tags[TAG_BOLD].start + mRetType;
	txt += " ";
	txt += mName;
	txt += "( ";
	txt += tags[TAG_BOLD].end;
	
	for( size_t i = 0; i != mParams.Count(); ++i )
	{
		spParameter& param = *mParams[i];

		if ( i != 0 )
			txt += ", ";
		
		txt += tags[TAG_BOLD].start;
		
		txt += param.mType;

		txt += tags[TAG_BOLD].end;
		txt += tags[TAG_ITALIC].start;

		txt += " ";
		txt += param.mName;

		if ( param.mInitVal != "" )
		{
			txt += " = ";
			txt += tags[TAG_BOLD].start;

			txt += param.mInitVal;

			txt += tags[TAG_BOLD].end;
		}

		txt += tags[TAG_ITALIC].end;;
	}

	txt += tags[TAG_BOLD].start;
	txt += " )";
	txt += tags[TAG_BOLD].end;

	// TBD:: constantness of method

	return txt;
}

wxString spOperation::GetArgListDigest()
{
	if ( mArgsDigest.Length() )

		return mArgsDigest;

	MMemberListT& lst = GetMembers();

	for( size_t i = 0; i != lst.Count(); ++i )
	{
		spParameter* pPar = (spParameter*)lst[i];

		if ( i != 0 )
			mArgsDigest += "@ ";

		if ( pPar->mType.Length() >= 2 )
		{
			mArgsDigest += pPar->mType[0u];
			mArgsDigest += pPar->mType[1u];
		}
	}

	return mArgsDigest;
}

MParamListT& spOperation::GetParams()
{
	return mParams;
}

void spOperation::AddParameter( spParameter* param )
{
	mParams.Add( param );
}

void spOperation::Dump( wxTextOutputStream& stm, const wxString& indent )
{
	stm << indent << mRetType << " ";
	
	if ( mScope.Length() )

		stm << mScope << "::";

	stm << mName << "(";

	if ( mParams.Count() )
	{
		stm << " ";
		
		for( size_t i = 0; i != mParams.Count(); ++i )
		{
			spParameter* par = mParams[i];

			if ( i != 0 )
				stm << ", ";

			stm << par->mType << " " << par->mName;

			if ( par->mInitVal.Length() )

				stm << " = " << par->mInitVal;
		}

		stm << " )";
	}
	else
		stm << ")";

	if ( mFlags & SP_FLAG_IS_DEFINITION )

		stm << " { /* body ommited*/ }";
	else
		stm << ";";

	stm << "\n";
}

/***** Implemenentation for class spPreprocessorLine *****/

wxString spPreprocessorLine::CPP_GetIncludedFileNeme()
{
	wxASSERT( GetStatementType() == SP_PREP_DEF_INCLUDE_FILE );

	size_t i = 0;

	while( i < mLine.length() && mLine[i] != '"' && mLine[i] != '<' ) 
		
		++i;

	++i;

	size_t start = i;

	while( i < mLine.length() && mLine[i] != '"' && mLine[i] != '>' ) 

		++i;

	if ( start < mLine.length() )
	{
		wxString fname;
		fname.append( mLine, start, ( i - start ) );

		return fname;
	}
	else
		return ""; // syntax error probably
}

/***** Implemenentation for class spPreprocessorLine *****/

void spPreprocessorLine::Dump( wxTextOutputStream& stm, const wxString& indent )
{
	stm << indent << mLine << "\n";
}

/***** Implemenentation for class spTypeDef *****/

void spTypeDef::Dump( wxTextOutputStream& stm, const wxString& indent )
{
	stm << indent << "typedef " << mOriginalType << " " << mOriginalType << ";\n";
}

/***** Implemenentation for class spFile *****/

void spFile::Dump( wxTextOutputStream& stm, const wxString& indent )
{
	for( size_t i = 0; i != mMembers.Count(); ++i )
	{
		if ( i != 0 )

			stm << indent << "\n";

		mMembers[i]->Dump( stm, indent );
	}
}

/***** Implemenentation for class SourceParserBase *****/

SourceParserBase::SourceParserBase()

	: mpFileBuf( NULL ),
	  mFileBufSz( 0 ),

	  mpPlugin( NULL )
{}

SourceParserBase::~SourceParserBase()
{
	if ( mpFileBuf ) free( mpFileBuf );

	if ( mpPlugin ) delete mpPlugin;
}

spFile* SourceParserBase::ParseFile( const char* fname,
									 bool resolveLineNumbers
								   )
{
	// FIXME:: the below should not be fixed!

	const size_t MAX_BUF_SIZE = 1024*256;

	if ( !mpFileBuf ) mpFileBuf = (char*)malloc( MAX_BUF_SIZE );

	mFileBufSz = MAX_BUF_SIZE;

	FILE* fp = fopen( fname, "rt" );

	if ( (int)fp == -1 || !fp ) return NULL;

	int sz = fread( mpFileBuf, 1, mFileBufSz, fp );

	fclose( fp );

	return Parse( mpFileBuf, mpFileBuf + sz );
}

void SourceParserBase::SetPlugin( SourceParserPlugin* pPlugin )
{
	if ( mpPlugin ) delete mpPlugin;

	mpPlugin = pPlugin;
}

void SourceParserBase::ResolveLineNumbersRecursive( spContext* pCtx, 
												    char* start,
												    char*& cur, char* end, 
												    size_t& curLine )
{
	char* ctxStart = pCtx->mSrcOffset + start;

	while( cur < ctxStart )
	{
		if ( *cur == '\n' ) 
			++curLine;

		++cur;
	}

	if (*cur == '\n' ) 
		++curLine;

	++cur;

	pCtx->mSrcLineNo = curLine;

	// FOR NOW:: line-numbers for method/function arguments are not resolved

	if ( pCtx->GetType() == SP_CTX_OPERATION ) 
		return;

	MMemberListT& lst = pCtx->GetMembers();
	size_t sz = lst.Count();

	for( size_t i = 0; i != sz; ++i )
	
		ResolveLineNumbersRecursive( lst[i], start, cur, end, curLine );
}

void SourceParserBase::ResolveLineNumbers( spFile* pFile, char* start, char* end )
{
	size_t lineNo = 0; char* cur = start;

	pFile->mSrcOffset = 0;

	ResolveLineNumbersRecursive( pFile, start, cur, end, lineNo );
}