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

#ifdef __GNUG__
    #pragma implementation "eventgen.cpp"
    #pragma interface "eventgen.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 "wx/datetime.h"

#include "eventgen.h"
#include "txtrecset.h"

/***** Implementation for class GenRuleInfo ******/

GenRuleInfo::GenRuleInfo()

	: mFlags( 0 )
{}

/***** Implementation for class GenRuleSet *****/

GenRuleSet::~GenRuleSet()
{
	WX_CLEAR_ARRAY( mRules );
}

/***** Implementation for class GenBindRuleSet *****/

GenBindRuleSet::~GenBindRuleSet()
{
	WX_CLEAR_ARRAY( mRules );
}

GenBindRuleInfo* GenBindRuleSet::FindRule( const wxString& ctrlClass )
{
    size_t i;
	for( i = 0; i != mRules.Count(); ++i )
    {
		if ( mRules[i]->mCtlClass == ctrlClass )
			return mRules[i];
    }

	return NULL;
}


/***** Implementation for class GenSourceTemplate *****/

GenSourceTemplate::GenSourceTemplate()

	: mHasHeader( FALSE ),
	  mpRuleSet( NULL ),
	  mpBindRuleSet( NULL )
{}

GenSourceTemplate::~GenSourceTemplate()
{}

GenRuleInfo* GenSourceTemplate::FindRule( const wxString& forTag )
{
	wxASSERT( mpRuleSet ); // DBG::

    size_t i;
	for( i = 0; i != mpRuleSet->mRules.Count(); ++i )
    {
		if ( mpRuleSet->mRules[i]->mTag == forTag )
			return mpRuleSet->mRules[i];
    }

	return NULL;
}

GenBindRuleInfo* GenSourceTemplate::FindBindRule( const wxString& forCtrlClass )
{
	wxASSERT( mpBindRuleSet ); // DBG::

    size_t i;
	for( i = 0; i != mpBindRuleSet->mRules.Count(); ++i )
    {
		if ( mpBindRuleSet->mRules[i]->mCtlClass == forCtrlClass )
			return mpBindRuleSet->mRules[i];
    }

	return NULL;
}

/***** Implementation for class GenTaggedBlockInfo *****/

GenTaggedBlockInfo::GenTaggedBlockInfo()

	: mFromIdx( (size_t)(-1) ),
	  mTillIdx( (size_t)(-1) )
{}

/***** Implementation for class GenSourceInfo *****/

GenSourceInfo::~GenSourceInfo()
{
	WX_CLEAR_ARRAY( mBlocks );
}

GenSourceInfo::GenSourceInfo()

	: mIsHeader( FALSE ),
	  mpTempl( NULL )
{}

/***** Implementation for class GenScriptTemplate *****/

GenScriptTemplate::GenScriptTemplate( ScriptProcessor* pProc )

	: BasicScriptProcessorTemplate( pProc )
{}


wxString GenScriptTemplate::HandleSpecialSubstitution( char* at, ScriptTemplate& templ )
{
	char* start = at;

	while( *at && *at != '[' && *at != ')' ) ++at;

	wxString tagName;
	ConcatString( tagName, start, size_t( at - start ) );

	if ( tagName == "subscript" )

		return Handle_subscript( at, templ );
	else
	if ( tagName == "event_body_at" )

		return Handle_event_body_at( at, templ );
	else
	if ( tagName == "event_list" )

		return Handle_event_list( at, templ );
	else
	if ( tagName == "member_list" )

		return Handle_member_list( at, templ );
	else
	if ( tagName == "assign_var" )

		return Handle_assign_var( at, templ );
	else
	if ( tagName == "inc_var" )

		return Handle_inc_var( at, templ );
	else
		return "[ERROR: Unrecognized special-substitution tag: " + tagName + "]";
}

/*** protected methods ***/

wxString GenScriptTemplate::Handle_event_body_at( char* at, ScriptTemplate& templ )
{
	// format: "@subscript[counter_var,alt_subscript]"

	wxString result;

	++at; // skip '['

	char* start = at;
	while( *at && *at != ']' && *at != ',' ) ++at;


	wxString counterVar;
	ConcatString( counterVar, start, size_t(at - start) );

	++at; // skip ','

	start = at;
	while( *at && *at != ']' && *at != ',' ) ++at;

	wxString altSubscript;
	ConcatString( altSubscript, start, size_t(at - start) );

	char* val = templ.FindValue( counterVar );

	size_t idx = 0;

	if ( val != NULL )

		idx = (size_t)atoi( val );

	GenSourceProcessor* proc = (GenSourceProcessor*)mpProc;

	if ( idx < proc->GetEvents().Count() &&
		 proc->GetEvents()[idx]->mBody.Length() )

		return proc->GetEvents()[idx]->mBody;
	else
	{
		ScriptTemplate* pTempl = proc->FindSubscriptByName( altSubscript );

		if ( pTempl == NULL )

			return "[@event_body:ERROR: Requested alternative-subscript " + altSubscript + " not found!]";

		pTempl->Reset();
		pTempl->SetParentTemplate( &templ );

		wxString body = pTempl->SubstitueVariables();

		if ( result.Length() ) return result + body;

		return body;
	}
}

wxString GenScriptTemplate::Handle_event_list( char* at, ScriptTemplate& templ )
{
	// format: "@event_list[entry_subscript]"

	wxString result;

	++at; // skip '['

	char* start = at;
	while( *at && *at != ']' && *at != ',' ) ++at;


	wxString script;
	ConcatString( script, start, size_t(at - start) );

	GenSourceProcessor* proc = (GenSourceProcessor*)mpProc;

	EventEntryInfoListT& lst = proc->GetEvents();

	ScriptTemplate* pTempl = proc->FindSubscriptByName( script );

	if ( pTempl == NULL )

		return "[@event_list:ERROR: Requested entry-subscript " + script + " not found!]";

	pTempl->Reset();
	pTempl->SetParentTemplate( &templ );

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{
		EventEntryInfo& info = *lst[i];

		pTempl->Reset();

		pTempl->SetVar( "EVT_MACRO_NAME", MakeTmpString( info.mpEventInfo->mEventMacro ) );

		if ( info.mId1.Length() ) 

			pTempl->SetVar( "ID_1", MakeTmpString( info.mId1 ) );

		if ( info.mId2.Length() ) 

			pTempl->SetVar( "ID_2", MakeTmpString( info.mId2 ) );

		if ( info.mType.Length() )

			pTempl->SetVar( "EVT_TYPE", MakeTmpString( info.mType ) );

		pTempl->SetVar( "EVENT_CLASS", MakeTmpString( info.mpEventInfo->mEventClass ) );
		pTempl->SetVar( "METHOD_NAME", MakeTmpString( info.mMethod ) );

		result += pTempl->SubstitueVariables();
	}

	return result;
}

wxString GenScriptTemplate::Handle_member_list( char* at, ScriptTemplate& templ )
{
	// format: "@member_list[entry_subscript]"

	++at; // skip '['

	char* start = at;
	while( *at && *at != ']' && *at != ',' ) ++at;


	wxString typeTxt;
	ConcatString( typeTxt, start, size_t(at - start) );

	// FIXME:: scripting design issues...

	int type = 0;

	if ( typeTxt == "decl_source" ) 
		
		type = decl_source;
	else
	if ( typeTxt == "ctl_decl_source" ) 
		
		type = ctl_decl_source;
	else
	if ( typeTxt == "init_source" ) 
		
		type = init_source;
	else
	if ( typeTxt == "ctl_init_source" ) 
		
		type = ctl_init_source;
	else
	if ( typeTxt == "to_ctl_source" ) 
		
		type = to_ctl_source;
	else
	if ( typeTxt == "to_member_source" ) 
		
		type = to_member_source;
	else
	if ( typeTxt == "getter_decl_source" ) 
		
		type = getter_decl_source;
	else
	if ( typeTxt == "setter_decl_source" ) 
		
		type = setter_decl_source;
	else
	if ( typeTxt == "getter_imp_source" ) 
		
		type = getter_imp_source;
	else
	if ( typeTxt == "setter_imp_source" ) 
		
		type = setter_imp_source;

	GenSourceProcessor* proc = (GenSourceProcessor*)mpProc;

	ResourceItemInfoListT& items = proc->GetResourceItems();

	wxString result;

    size_t i;
	for( i = 0; i != items.Count(); ++i )
	{
		ResourceItemInfo& item = *items[i];

		if ( !item.mpRule )
            continue;

		ScriptTemplate* pScript = NULL;
		bool ok = false;

		switch( type )
		{
			case ctl_decl_source	: pScript = &item.mpRule->mCtlDeclSource; 
									  ok = (item.mCtrlMember.Length() != 0); break;
			case ctl_init_source	: pScript = &item.mpRule->mCtlInitSource;
									  ok = (item.mCtrlMember.Length() != 0); break;
			case getter_decl_source : pScript = &item.mpRule->mGetterDeclSource;
									  ok = (item.mGetter.Length() != 0); break;
			case setter_decl_source : pScript = &item.mpRule->mSetterDeclSource;
									  ok = (item.mSetter.Length() != 0); break;
			case getter_imp_source	: pScript = &item.mpRule->mGetterImpSource;
									  ok = (item.mGetter.Length() != 0); break;
			case setter_imp_source	: pScript = &item.mpRule->mSetterImpSource;
									  ok = (item.mSetter.Length() != 0); break;
			case init_source		: pScript = &item.mpVarType->mInitSource;
									  ok = (item.mMember.Length() != 0); break;
			case decl_source		: pScript = &item.mpVarType->mDeclSource;
									  ok = (item.mMember.Length() != 0); break;
			case to_ctl_source		: pScript = &item.mpVarType->mToCtlSource;
									  ok = (item.mMember.Length() != 0); break;
			case to_member_source	: pScript = &item.mpVarType->mToMemberSource;
									  ok = (item.mMember.Length() != 0); break;
			default : break;
		}

		if ( ok )
		{
			proc->SetCommonItemVars( item, *pScript );

			result += pScript->SubstitueVariables();
		}
	}

	return result;
}

/***** Implementation for class GenSourceProcessor *****/

GenSourceProcessor::GenSourceProcessor()
{
	SetProcessorTemplate( new GenScriptTemplate( this ) );
}

GenSourceProcessor::~GenSourceProcessor()
{
	CleanUp();
}

void GenSourceProcessor::CleanUp()
{
	WX_CLEAR_ARRAY( mTemplates );
	WX_CLEAR_ARRAY( mRuleSets );
	WX_CLEAR_ARRAY( mBindRuleSets );
	WX_CLEAR_ARRAY( mSubscripts );
	WX_CLEAR_ARRAY( mEvents );
	WX_CLEAR_ARRAY( mResItems );

	mSubscriptHash.Clear();
}

bool GenSourceProcessor::ProcessSection( Record& rec )
{
	if ( rec.GetName() == "generation_rules" )
	{
		GenRuleSet* pSet = new GenRuleSet();

		pSet->mName = rec[ "name" ];

        size_t i;
		for( i = 0; i != rec.GetFieldCount(); ++i )
		{
			if ( rec.GetFieldByIndex(i).GetType() == FT_RECORD )
			{
				Record& ruleRec = rec.GetRecordByIndex(i);

				GenRuleInfo* pRule = new GenRuleInfo();
				
				pRule->mTag = ruleRec[ "tag" ];

				if ( ruleRec.FindField( "collect_event_entries" ) &&
					 ruleRec[ "collect_event_entries" ] == "true" )

					 pRule->mFlags |= GenRuleInfo::COLLECT_EVENT_ENTRIES;

				if ( ruleRec.FindField( "collect_event_bodies" ) &&
					 ruleRec[ "collect_event_bodies" ] == "true" )

					 pRule->mFlags |= GenRuleInfo::COLLECT_EVENT_BODIES;

				// binding related "collects"

				if ( ruleRec.FindField( "collect_ctl_members" ) &&
					 ruleRec[ "collect_ctl_members" ] == "true" )

					 pRule->mFlags |= GenRuleInfo::COLLECT_CTL_MEMBERS;

				if ( ruleRec.FindField( "collect_members" ) &&
					 ruleRec[ "collect_members" ] == "true" )

					 pRule->mFlags |= GenRuleInfo::COLLECT_MEMBERS;


				if ( ruleRec.FindField( "collect_setters" ) &&
					 ruleRec[ "collect_setters" ] == "true" )

					 pRule->mFlags |= GenRuleInfo::COLLECT_SETTERS;

				if ( ruleRec.FindField( "collect_getters" ) &&
					 ruleRec[ "collect_getters" ] == "true" )

					 pRule->mFlags |= GenRuleInfo::COLLECT_GETTERS;


				pRule->mSubscript.SetScript( ruleRec[ "content" ] );
				pRule->mSubscript.SetParentTemplate( mpTopTempl );

				pSet->mRules.Add( pRule );
			}
		}

		mRuleSets.Add( pSet );
	}
	if ( rec.GetName() == "binding_rules" )
	{
		GenBindRuleSet* pSet = new GenBindRuleSet();

		pSet->mName = rec[ "name" ];

        size_t i;
		for( i = 0; i != rec.GetFieldCount(); ++i )
		{
			if ( rec.GetFieldByIndex(i).GetType() == FT_RECORD )
			{
				Record& ruleRec = rec.GetRecordByIndex(i);

				GenBindRuleInfo* pRule = new GenBindRuleInfo();
				
				pRule->mCtlClass = ruleRec[ "control_class" ];
				pRule->mCtlMemberNameHint.SetScript( ruleRec[ "ctl_member_name_hint" ] );
				pRule->mCtlDeclSource.SetScript( ruleRec[ "ctl_decl_source" ] );
				pRule->mCtlInitSource.SetScript( ruleRec[ "ctl_init_source" ] );

				pRule->mCtlMemberNameHint.SetParentTemplate( mpTopTempl );
				pRule->mCtlDeclSource.SetParentTemplate( mpTopTempl );
				pRule->mCtlInitSource.SetParentTemplate( mpTopTempl );

				pRule->mCanHaveMember = ruleRec.FindField( "can_have_member" ) &&
										ruleRec[ "can_have_member" ] == "true" ;

				if ( pRule->mCanHaveMember )
				{
					pRule->mGetterDeclSource.SetScript( ruleRec[ "getter_decl_source" ] );
					pRule->mGetterImpSource.SetScript( ruleRec[ "getter_imp_source" ] );
					pRule->mSetterDeclSource.SetScript( ruleRec[ "setter_decl_source" ] );
					pRule->mSetterImpSource.SetScript( ruleRec[ "setter_imp_source" ] );

					pRule->mGetterNameHint.SetScript( ruleRec[ "getter_name_hint" ] );
					pRule->mSetterNameHint.SetScript( ruleRec[ "setter_name_hint" ] );

					pRule->mGetterDeclSource.SetParentTemplate( mpTopTempl );
					pRule->mGetterImpSource.SetParentTemplate( mpTopTempl );
					pRule->mSetterDeclSource.SetParentTemplate( mpTopTempl );
					pRule->mSetterImpSource.SetParentTemplate( mpTopTempl );

					pRule->mGetterNameHint.SetParentTemplate( mpTopTempl );
					pRule->mSetterNameHint.SetParentTemplate( mpTopTempl );

                    size_t k;
					for( k = 0; k != ruleRec.GetFieldCount(); ++k )
					{
						if ( ruleRec.GetFieldByIndex(k).GetType() == FT_RECORD )
						{
							Record& typeRec = ruleRec.GetRecordByIndex(k);

							GenBindRuleVarTypeInfo* pType = new GenBindRuleVarTypeInfo();

							pType->mBoundAs = typeRec[ "bound_as" ];
							pType->mMemberNameHint.SetScript( typeRec[ "member_name_hint" ] );
							pType->mDeclSource.SetScript( typeRec[ "decl_source" ] );
							pType->mInitSource.SetScript( typeRec[ "init_source" ] );
							pType->mToCtlSource.SetScript( typeRec[ "to_ctl_source" ] );
							pType->mToMemberSource.SetScript( typeRec[ "to_member_source" ] );

							pType->mMemberNameHint.SetParentTemplate( mpTopTempl );
							pType->mDeclSource.SetParentTemplate( mpTopTempl );
							pType->mInitSource.SetParentTemplate( mpTopTempl );
							pType->mToCtlSource.SetParentTemplate( mpTopTempl );
							pType->mToMemberSource.SetParentTemplate( mpTopTempl );

							pRule->mVarTypes.Add( pType );
						}
					}
				}

				pSet->mRules.Add( pRule );
			}
		}

		mBindRuleSets.Add( pSet );
	}
	else
	if ( rec.GetName() == "source_template" )
	{
		GenSourceTemplate* pTempl = new GenSourceTemplate();

		pTempl->mName = rec[ "name" ];
		pTempl->mLang = rec[ "language" ];
		pTempl->mHasHeader = rec.FindField("has_header" ) && 
							 rec[ "has_header" ] == "true";

		pTempl->mImpExt = rec[ "imp_ext" ];
		if ( pTempl->mHasHeader ) pTempl->mHdrExt = rec[ "header_ext" ];

		pTempl->mpRuleSet = NULL;

		wxString ruleSetName = rec[ "rules" ];
		pTempl->mpRuleSet = NULL;

        size_t i;
		for( i = 0; i != mRuleSets.Count(); ++i )
		{
			if ( mRuleSets[i]->mName == ruleSetName )
			{
				pTempl->mpRuleSet = mRuleSets[i];
				break;
			}
		}

		pTempl->mpBindRuleSet = NULL; // it's possible that template does not
									  // support binding rules

		if ( rec.FindField( "binding_rules" ) )
		{
			wxString name = rec[ "binding_rules" ];

            size_t k;
			for( k = 0; k != mBindRuleSets.Count(); ++k )
			{
				if ( mBindRuleSets[k]->mName == name ) 
				{
					pTempl->mpBindRuleSet = mBindRuleSets[k];

					break;
				}
			}
		}

		// TBD:: error checks...

		if ( pTempl->mHasHeader )
		{
			pTempl->mHdrSubscript.SetScript( rec[ "header_source" ] );
			pTempl->mHdrSubscript.SetParentTemplate( mpTopTempl );
		}

		// header subscript is mandatory for any language

		pTempl->mImpSubscript.SetScript( rec[ "implementation_source" ] );
		pTempl->mImpSubscript.SetParentTemplate( mpTopTempl );

		mTemplates.Add( pTempl );
	}

	return TRUE;
}

GenSourceTemplateListT& GenSourceProcessor::GetTemplates()
{
	return mTemplates;
}

GenSourceTemplate* GenSourceProcessor::FindTemplate( const wxString& name )
{
    size_t i;
	for( i = 0; i != mTemplates.Count(); ++i )
    {
		if ( mTemplates[i]->mName == name )
			return mTemplates[i];
    }

	return NULL;
}

void GenSourceProcessor::InitScaner()
{
	mHdrSrc.mText = "";
	mImpSrc.mText = "";

	WX_CLEAR_ARRAY( mImpSrc.mBlocks );
	WX_CLEAR_ARRAY( mHdrSrc.mBlocks );
	WX_CLEAR_ARRAY( mResItems );

	ClearTmpStrings();

	mCurBodyCount = 0;

	WX_CLEAR_ARRAY( mEvents );
}

bool GenSourceProcessor::ScanHeader( const wxString& hdrFName, const wxString& src )
{
	mHdrSrc.mText = src;
	mHdrSrc.mFName = hdrFName;
	mImpSrc.mOtherFName = hdrFName;

	DoScanSource( mHdrSrc );
	DoProcessSource( mHdrSrc );

	return TRUE;
}

bool GenSourceProcessor::ScanImplementation( const wxString& impFName, const wxString& src )
{
	mImpSrc.mText = src;
	mImpSrc.mFName = impFName;
	mHdrSrc.mOtherFName = impFName;

	DoScanSource( mImpSrc );
	DoProcessSource( mImpSrc );

	return TRUE;
}

bool GenSourceProcessor::IsGeneratedSource( const wxString& srcText )
{
	return srcText.Find( "%%INFO-{" ) != -1;
}

bool GenSourceProcessor::ScanSources( const wxString& impFName, 
									  const wxString& impSrc, 
									  const wxString& hdrFName, 
									  const wxString& hdrSrc )
{
	return ScanHeader( hdrFName, hdrSrc ) &&
		   ScanImplementation( impFName, impSrc );
}

EventEntryInfoListT& GenSourceProcessor::GetEvents()
{
	return mEvents;
}

void GenSourceProcessor::ReGenerateSources()
{
	if ( mImpSrc.mpTempl->mHasHeader )

		DoRegenerateSource( mHdrSrc );

	DoRegenerateSource( mImpSrc );
}

void GenSourceProcessor::GenerateNewSources( const wxString& fname, 
											 const wxString& className,
											 const wxString& resFile,
											 const wxString& wndRes, 
											 const wxString& menuRes, 
											 GenSourceTemplate& templ )
{
	mImpSrc.mFName = fname + templ.mImpExt;
	mImpSrc.mResFile = resFile;
	mImpSrc.mWndRes = wndRes;
	mImpSrc.mMenuRes = menuRes;
	mImpSrc.mClassName = className;
	mImpSrc.mIsHeader = FALSE;
	mImpSrc.mpTempl = &templ;

	if ( templ.mHasHeader )

		mImpSrc.mOtherFName = fname + templ.mHdrExt;

	DoGenerateNewSource( mImpSrc, templ.mImpSubscript );

	if ( templ.mHasHeader )
	{
		mHdrSrc.mFName = fname + templ.mHdrExt;
		mHdrSrc.mResFile = resFile;
		mHdrSrc.mWndRes = wndRes;
		mHdrSrc.mMenuRes = menuRes;
		mHdrSrc.mClassName = className;
		mHdrSrc.mIsHeader = TRUE;
		mHdrSrc.mpTempl = &templ;

		mHdrSrc.mOtherFName = fname + templ.mImpExt;

		DoGenerateNewSource( mHdrSrc, templ.mHdrSubscript );
	}
}

void GenSourceProcessor::SetWindowResource( const wxString& wndRes )
{
	mHdrSrc.mWndRes = wndRes;
	mImpSrc.mWndRes = wndRes;
}

void GenSourceProcessor::SetMenuResource( const wxString& menuRes )
{
	mHdrSrc.mMenuRes = menuRes;
	mImpSrc.mMenuRes = menuRes;
}

void GenSourceProcessor::SetResourceFile( const wxString& resFile )
{
	mHdrSrc.mResFile = resFile;
	mImpSrc.mResFile = resFile;
}

size_t GenSourceProcessor::GetSourceCodeLineNoForEvent( EventEntryInfo* pEntry )
{
	if ( pEntry->mSrcPos == (size_t)(-1) )

		return (size_t)(-1);

	const char* cur = (const char*)mImpSrc.mText;
	const char* end = cur + pEntry->mSrcPos;

	size_t curLine = 0;

	while( cur < end )
	{
		if ( *cur == 10 ) ++curLine;

		++cur;
	}

	return curLine;
}

ResourceItemInfoListT& GenSourceProcessor::GetResourceItems()
{
	return mResItems;
}

bool GenSourceProcessor::FlushSourcesToDisk()
{
	WriteFileContent( mImpSrc.mFName, mImpSrc.mText );

	if ( mImpSrc.mpTempl->mHasHeader )

		WriteFileContent( mHdrSrc.mFName, mHdrSrc.mText );

	return TRUE;
}

GenSourceInfo& GenSourceProcessor::GetImpSource()
{
	return mImpSrc;
}

GenSourceInfo& GenSourceProcessor::GetHdrSource()
{
	return mHdrSrc;
}

// helpers

static void scan_past_eol( char*& cur, char* end ) // bk added void to give type
{
	while( cur < end && *cur != 10 && *cur != 13 ) ++cur;

	if ( cur < end && *cur == 13 ) ++cur;

	if ( cur < end ) ++cur; 
}

static void scan_eol_back( char*& cur, char* begin ) // bk added void to give type
{
	while( cur > begin && *cur != 10 && *cur != 13 ) --cur;

	++cur;
}

/*** protected methods ***/

bool GenSourceProcessor::DoScanSource( GenSourceInfo& info )
{
	char* cur = (char*)(const char*)info.mText;
	char* begin = cur;
	char* end = cur + info.mText.Length();
	GenTaggedBlockInfo* pCurBlock = NULL;

	while( cur < end )
	{
		char* found = (char*)memchr( cur, '%', (size_t)( end- cur ) );

		if ( found == NULL ) break;

		cur = found + 1;

		if ( *(found + 1) == '%' )
		{
			++cur;

			if ( pCurBlock ) // already open? the next '%%' ocurrance closes it
			{
				scan_eol_back( cur, begin );

				pCurBlock->mTillIdx = (size_t)(cur-begin);

				pCurBlock = NULL;

				scan_past_eol( cur, end );

				continue;
			}

			char* start = cur;

			while( cur < end && *cur != '-' ) ++cur;

			GenTaggedBlockInfo* pBlock = new GenTaggedBlockInfo();
			ConcatString( pBlock->mTag, start, (size_t)(cur-start) );

			scan_past_eol( cur, end );

			pBlock->mFromIdx = (size_t)(cur-begin);

			pCurBlock = pBlock;

			info.mBlocks.Add( pBlock );
		}
	}

	if ( pCurBlock )
	{
		wxFAIL_MSG( "Tagged block (" + pCurBlock->mTag + " is not closed" );
	}

	return TRUE;
}

void GenSourceProcessor::PrepareResourceItems()
{
    size_t n;
	for( n = 0; n != mResItems.Count(); ++n )
	{
		ResourceItemInfo& item = *mResItems[n];

		item.mpRule = 
			mImpSrc.mpTempl->mpBindRuleSet->FindRule( item.mClass );

		if ( item.mpRule && item.mType.Length() )
		{
			item.mpVarType = NULL;

            size_t j;
			for( j = 0; j != item.mpRule->mVarTypes.Count(); ++j )
			{
				GenBindRuleVarTypeInfo& type = *(item.mpRule->mVarTypes[j]);

				if ( type.mBoundAs == item.mType )
				{
					item.mpVarType = &type;
					break;
				}
			}
		}

	}
}

bool GenSourceProcessor::ProcessBindings( GenSourceInfo& info )
{
	char* cur = (char*)(const char*)info.mText;
	char* begin = cur;
	char* end = cur + info.mText.Length();

	wxHashTable itemsHash( wxKEY_STRING );
    size_t n;
	for( n = 0; n != mResItems.Count(); ++n )
    {
		itemsHash.Put( mResItems[n]->mId, (wxObject*)mResItems[n] );
    }
	
    size_t i;
	for( i = 0; i != info.mBlocks.Count(); ++i )
	{
		GenTaggedBlockInfo& block = *info.mBlocks[i];

		GenRuleInfoListT& rules = info.mpTempl->mpRuleSet->mRules;

		GenRuleInfo* pRule = info.mpTempl->FindRule( block.mTag );

		if ( pRule && 
			pRule->mFlags & (  GenRuleInfo::COLLECT_CTL_MEMBERS | 
							   GenRuleInfo::COLLECT_MEMBERS |
							   GenRuleInfo::COLLECT_SETTERS | 
							   GenRuleInfo::COLLECT_GETTERS )
		   )
		{
			const char* cur = begin + block.mFromIdx;
			const char* end = begin + block.mTillIdx;

			while( cur < end )
			{
				while( cur < end && *cur != '{' ) ++cur;

				if ( cur >= end ) break;

				const char* start = cur;
				while( cur < end && *cur != '}' ) ++cur;

				++cur; // skip '}'

				wxString propsTxt;
				ConcatString( propsTxt, start, size_t( cur - start ) );

				Record rec;
				rec.LoadFromString( propsTxt );

				ResourceItemInfo* pItem = (ResourceItemInfo*)itemsHash.Get( rec[0] );

				if ( pItem )
				{
					if ( pRule->mFlags & GenRuleInfo::COLLECT_CTL_MEMBERS )

						pItem->mCtrlMember = rec[1];
					else
					if ( pRule->mFlags & GenRuleInfo::COLLECT_MEMBERS )
					{
						pItem->mType = rec[1];
						pItem->mMember = rec[2];
						pItem->mInitValue = rec[3];
					}
					else
					if ( pRule->mFlags & GenRuleInfo::COLLECT_SETTERS )
					
						pItem->mSetter = rec[1];
					else
					if ( pRule->mFlags & GenRuleInfo::COLLECT_GETTERS )

						pItem->mGetter = rec[1];

					if ( pItem->mpRule && pItem->mType.Length() )
					{
						pItem->mpVarType = NULL;

                        size_t j;
						for( j = 0; j != pItem->mpRule->mVarTypes.Count(); ++j )
						{
							GenBindRuleVarTypeInfo& type = *(pItem->mpRule->mVarTypes[j]);

							if ( type.mBoundAs == pItem->mType )
							{
								pItem->mpVarType = &type;
								break;
							}
                        }  // for
                    }  // if
                }  // if (pItem)
            }  // while
        }  // if (pRule...)
    }  // for(i...)

	return TRUE;
}

bool GenSourceProcessor::DoProcessSource( GenSourceInfo& info )
{
	char* cur = (char*)(const char*)info.mText;
	char* begin = cur;
	char* end = cur + info.mText.Length();

	// first, find meta-info tag which describes this source

	info.mpTempl = NULL;

	size_t i;
	for( i = 0; i != info.mBlocks.Count(); ++i )
	{
		GenTaggedBlockInfo& block = *info.mBlocks[i];

		if ( block.mTag == "INFO" )
		{
			char* cur = begin + block.mFromIdx;

			while( cur < end && *cur != '{' )
				++cur;
			
			if ( cur < end ) 
			{
				char* last = begin + block.mTillIdx;
				char tmp = *last;
				*last = '\0'; // terminate meta-info line temporarely
				Record rec; bool ok = rec.LoadFromString( cur );

				info.mResFile   = rec[ "RES_FILE" ];
				info.mWndRes    = rec[ "WND_RES" ];
				info.mMenuRes   = rec[ "MENU_RES" ];
				info.mClassName = rec[ "CLASS" ];

				if ( rec.FindField( "DECL_FILE" ) )
				{
					info.mIsHeader = FALSE;
					info.mOtherFName = rec[ "DECL_FILE" ];
				}

				if ( rec.FindField( "IMP_FILE" ) )
				{
					info.mIsHeader = TRUE;
					info.mOtherFName = rec[ "IMP_FILE" ];
				}

				wxString templ  = rec[ "TEMPL" ];

                size_t k;
				for( k = 0; k != mTemplates.Count(); ++k )
				{
					if ( mTemplates[k]->mName == templ )
					{
						info.mpTempl = mTemplates[k];
						break;
					}
				}

				wxASSERT_MSG( info.mpTempl, "Template " + templ + " is not present in the current config!" );

				*last = tmp; // unterminate

				break;
			}
		}
	}

	// scan blocks contained in the source and prcoess them
	// according to the template which was determined in the first step

	for( i = 0; i != info.mBlocks.Count(); ++i )
	{
		GenTaggedBlockInfo& block = *info.mBlocks[i];

		GenRuleInfoListT& rules = info.mpTempl->mpRuleSet->mRules;

		GenRuleInfo* pRule = info.mpTempl->FindRule( block.mTag );

		if ( pRule )
		{
			if ( pRule->mFlags & GenRuleInfo::COLLECT_EVENT_ENTRIES )
			
				CollectEvents( info, block, begin + block.mFromIdx, begin + block.mTillIdx );

			if ( pRule->mFlags & GenRuleInfo::COLLECT_EVENT_BODIES )

				CollectEventBodies( info, block, begin + block.mFromIdx, begin + block.mTillIdx );
		}
	}

	return TRUE;
}

#define IS_BLANK(s)     ( *s == ' ' || *s == '\t' || *s == 10 || *s == 13 )
#define IS_NOT_BLANK(s) ( *s != ' ' && *s != '\t' && *s != 10 && *s != 13 )

void GenSourceProcessor::CollectEvents( GenSourceInfo& info, GenTaggedBlockInfo& block, 
										char* begin, char* end )
{
	char* cur = begin;

	for(;;)
	{
		while( cur < end && IS_BLANK(cur) ) ++cur;

		if ( cur >= end ) break;

		char* start = cur;

		while( cur < end && *cur != '(' && IS_NOT_BLANK(cur) ) ++cur;

		wxString macro;
		ConcatString( macro, start, size_t(cur-start) );

		while( cur < end && *cur != '(' ) ++cur;

		++cur; // skip '('

		StrListT args;

		// collect comma-separted words

		for(;;)
		{
			while( cur < end && IS_BLANK(cur) ) ++cur;

			if ( cur >= end ) break;

			if ( *cur == ')' ) break;

			start = cur;

			while( cur < end && IS_NOT_BLANK(cur) && *cur != ',' ) ++cur;

			wxString word;
			ConcatString( word, start, size_t(cur-start) );

			args.Add( word );

			if ( *cur == ',' ) ++cur;
		}

		++cur; // skip ')'

		if ( args.Count() > 0 && args[0] == "self" ) 

			args.Remove( 0 );

		EventEntryInfo& entry = *new EventEntryInfo();

		entry.mpEventInfo = EventInfo::FindEventInfoByMacro( macro );

		if ( args.Count() == 4 ) 
		{
			entry.mId1 = ExtractId( args[0] );
			entry.mId2 = ExtractId( args[1] );
			entry.mType = args[2];
			entry.mMethod = ExtractMethod( args[3] );
		}
		else
		if ( args.Count() ==  3 )
		{
			entry.mId1 = ExtractId( args[0] );
			entry.mId2 = ExtractId( args[1] );
			entry.mMethod = ExtractMethod( args[2] );
		}
		else
		if ( args.Count() == 2 )
		{
			entry.mId1 = ExtractId( args[0] );
			entry.mMethod = ExtractMethod( args[1] );
		}
		else
		if ( args.Count() == 1 )
		
			entry.mMethod = ExtractMethod( args[0] );

		mEvents.Add( &entry );
	}
}

void GenSourceProcessor::CollectEventBodies( GenSourceInfo& info, 
											 GenTaggedBlockInfo& block, 
											 char* begin, char* end )
{
	char* cur = begin;

	size_t srcPos = (size_t)(-1);

	if ( info.mpTempl->mLang == "Python" )
	{
		for(;;)
		{
			char* start = cur;

			if ( mCurBodyCount >= mEvents.Count() ) break;

			for(;;)
			{
 				while( cur < end && IS_BLANK(cur) ) ++cur;

				if ( size_t(end - cur) > 4 &&
					 *cur	  == 'd' && 
					 *(cur+1) == 'e' &&
					 *(cur+2) == 'f' &&
					 *(cur+3) == ' ' ) 
				{
					char* tmp = cur;
					scan_past_eol( cur, end );

					srcPos = block.mFromIdx + size_t( cur - begin );

					cur = tmp;
					cur += 4; // skip "def "
					break;
				}

				while( cur < end && IS_NOT_BLANK(cur) ) ++cur;

				if ( cur >= end ) break;
			}

			if ( cur >= end ) break;

			// FOR NOW:: very simplistic

			for(;;)
			{
 				while( cur < end && IS_BLANK(cur) ) ++cur;

				if ( ( size_t(end - cur) > 4 &&
					   *cur     == 'd' && 
					   *(cur+1) == 'e' &&
					   *(cur+2) == 'f' &&
					   *(cur+3) == ' ' 
					 ) ||
					 cur >= end - 3 // FIXME:: HACK::... ("no way" to determine end-of-method in Python)
				   )
				{
					while( cur > begin && *cur != 10 ) --cur;

					EventEntryInfo& entry = *mEvents[ mCurBodyCount ];

					entry.mSrcPos = srcPos;;

					ConcatString( entry.mBody, start, size_t(cur-start) );

					++mCurBodyCount;

					break;
				}

				while( cur < end && IS_NOT_BLANK(cur) ) ++cur;

				if ( cur >= end ) break;
			}
		}
	}
	else
		// otherwise C++/Java/whatever

		for(;;)
		{
			if ( mCurBodyCount >= mEvents.Count() ) break;

			if ( cur >= end ) break;

			char* start = cur;

			int depth = 0;

			// FOR NOW:: very simplistic

			while( cur < end )
			{
				if ( *cur == '{' )
				{
					if ( depth == 0 )
					{
						// remeber the start of body (used later for 
						// quick-jumping to src. code)

						srcPos = block.mFromIdx + size_t( cur -begin );
					}

					++depth;
				}
				else
				if ( *cur == '}' )
				{
					--depth;

					if ( depth == 0 )
					{
						scan_past_eol( cur, end );

						EventEntryInfo& entry = *mEvents[ mCurBodyCount ];

						entry.mSrcPos = srcPos;

						ConcatString( entry.mBody, start, size_t(cur-start) );

						++mCurBodyCount;

						break;
					}
				}

				++cur;
			}
		}
}

void GenSourceProcessor::DoRegenerateSource( GenSourceInfo& info )
{
	wxString result;
	size_t curIdx = 0;

	const char* begin = (const char*)info.mText;

	ScriptTemplate* pScript = &info.mpTempl->mImpSubscript;

	if ( info.mIsHeader )

		pScript = &info.mpTempl->mHdrSubscript;

	SetCommonSourceVars( info, *pScript );

    size_t i;
	for( i = 0; i != info.mBlocks.Count(); ++i )
	{
		GenTaggedBlockInfo& block = *info.mBlocks[i];

		GenRuleInfo* pRule = info.mpTempl->FindRule( block.mTag );

		if ( pRule )
		{
			if ( block.mTag == "EVT_IMPS" )
			{
				// glt -= fixes compiler warning
				int o = 0;
				++o;
			}

			if ( curIdx < block.mFromIdx )
			{
				ConcatString( result, begin + curIdx, 
							  block.mFromIdx - curIdx );

				curIdx = block.mTillIdx;
			}

			pRule->mSubscript.SetParentTemplate( pScript );

			pRule->mSubscript.Reset();

			wxString snippet = pRule->mSubscript.SubstitueVariables();

			result += snippet;
		}
	}

	// append reminder, if any

	if ( curIdx < info.mText.Length() )
	
		ConcatString( result, begin + curIdx, info.mText.Length() - curIdx );

	info.mText = result;
}

void GenSourceProcessor::DoGenerateNewSource( GenSourceInfo& info, 
											  ScriptTemplate& script )
{
	script.Reset();

	SetCommonSourceVars( info, script );

	info.mText = script.SubstitueVariables();

	DoScanSource( info );
	DoProcessSource( info );

	DoRegenerateSource( info );
}

void GenSourceProcessor::SetCommonSourceVars( GenSourceInfo& info, ScriptTemplate& script )
{
	if ( &script != mpTopTempl )

		script.SetParentTemplate( mpTopTempl );

	if ( info.mIsHeader )
	{
		script.SetVar( "IS_HDR",	MakeTmpString( "true" ) );
		script.SetVar( "IMP_FILE",	MakeTmpString( info.mOtherFName ) );
		script.SetVar( "DECL_FILE",	MakeTmpString( info.mFName ) );
	}
	else
	{
		script.SetVar( "IS_IMP",	MakeTmpString( "true" ) );
		script.SetVar( "DECL_FILE",	MakeTmpString( info.mOtherFName ) );
		script.SetVar( "IMP_FILE",	MakeTmpString( info.mFName ) );
	}

	if ( info.mpTempl->mHasHeader )

		script.SetVar( "HAS_HEADER",MakeTmpString( "true" ) );

	script.SetVar( "TEMPL",		MakeTmpString( info.mpTempl->mName ) );
	script.SetVar( "RES_FILE",	MakeTmpString( info.mResFile ) );
	script.SetVar( "WND_RES",	MakeTmpString( info.mWndRes ) );
	script.SetVar( "MENU_RES",	MakeTmpString( info.mMenuRes ) );
	script.SetVar( "CLASS_NAME",MakeTmpString( info.mClassName ) );
	script.SetVar( "FNAME",		MakeTmpString( info.mFName ) );
	script.SetVar( "DATE",		MakeTmpString( GetDateStr() ) );
	script.SetVar( "LANG",		MakeTmpString( info.mpTempl->mLang ) );
	script.SetVar( "FNAME_CAPITALIZED", MakeTmpString( GetCapitalizedFName( info.mFName ) ) );

	script.SetVar( "CUR_EVENT_NO", MakeTmpString( "0" ) );
}

void GenSourceProcessor::SetCommonItemVars( ResourceItemInfo& item, ScriptTemplate& script )
{
		wxString rnameFirstCap = item.mId.Lower();

		if ( rnameFirstCap.Length() )

			rnameFirstCap.SetChar( 0, wxString( rnameFirstCap[0u] ).Upper()[0u] );

		script.Reset();
		script.SetVar( "CLASS_NAME", mImpSrc.mClassName );
		script.SetVar( "RES_NAME", item.mId );
		script.SetVar( "RES_NAME_FIRST_CAP", MakeTmpString( rnameFirstCap ) );
		script.SetVar( "CTL_CLASS", item.mSubclass );
		script.SetVar( "CTL_MEMBER_NAME", item.mCtrlMember );
		script.SetVar( "TYPE", item.mType );
		script.SetVar( "MEMBER_NAME", item.mMember );
		script.SetVar( "INIT_VALUE", item.mInitValue );
		script.SetVar( "GETTER_METHOD", item.mGetter );
		script.SetVar( "SETTER_METHOD", item.mSetter );
}

wxString GenSourceProcessor::GetCapitalizedFName( const wxString& fname )
{
	if ( fname.Length() == 0 ) return "";

	size_t i = fname.Length() - 1;

	while( i > 0 && fname[i] != '.' ) --i;

	wxString name( fname, 0, i );

	name = name.Upper();

	for( i = 0; i != name.Length(); ++i )

		if ( name[i] == ' ' ) name[i] = '_';

	return name;
}

wxString GenSourceProcessor::GetDateStr()
{
	wxDateTime dt = wxDateTime::Now();

	return dt.Format();
}

wxString GenSourceProcessor::ExtractMethod( const wxString& scopedMtd )
{
    size_t i;
	for( i = 0; i != scopedMtd.Length(); ++i )
	{
		// '.' for Python, "::" for C++

		if ( scopedMtd[i] == '.' || 
			 scopedMtd[i] == ':' )
		{
			++i;

			if ( scopedMtd[i] == ':' ) ++i;

			return wxString( scopedMtd, i, scopedMtd.Length() - i );
		}
	}

	return "";
}

wxString GenSourceProcessor::ExtractId( const wxString& id )
{
	// possible formats: XMLID("id") or ID_LITERAL

	const char* cur = (const char*)id;

	while( *cur && *cur != '\"' ) ++cur;

	if ( *cur != '\"' ) return id;

	++cur;

	const char* start = cur;

	while( *cur && *cur != '\"' ) ++cur;

	wxString actualId;
	ConcatString( actualId, start, size_t( cur - start ) );

	return actualId;
}
