/////////////////////////////////////////////////////////////////////////////
// Name:        
// Purpose:     wxBugTracker
// Author:      Aleksandras Gluchovas (mailto:alex@soften.ktu.lt)
// Modified by:
// Created:     20/10/1999
// RCS-ID:      $Id: eq_solver.cpp,v 1.6 2001/04/04 20:00:57 georgetasker Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
#pragma implementation "objstore.h"
// #pragma interface
#endif

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

#ifdef __BORLANDC__
#pragma hdrstop
#endif

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

#include "eq_solver.h"
#include "myutils.h" // IntToStr()
#include <string.h>
#include <stdio.h>


/***** Implementation for class EqSolverVar ******/

EqSolverVar::EqSolverVar()
{
}

EqSolverVar::EqSolverVar( double d )
{
	mType = EQS_DOUBLE;
	mDoubleValue = d;
}

EqSolverVar::EqSolverVar( const wxString& strValue )
{
	mType = EQS_STRING;
	mStrValue = strValue;
}

EqSolverVar::EqSolverVar( const wxString& name, double d )
{
	mType = EQS_DOUBLE;
	mDoubleValue = d;
	mName = name;
}

EqSolverVar::EqSolverVar( const wxString& name, const wxString& strValue )
{
	mType = EQS_STRING;
	mStrValue = strValue;
	mName = name;
}

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

const wxString& EqSolverVar::GetName()
{
	return mName;
}

void EqSolverVar::SetType( int type )
{
	mType = type;
}

int EqSolverVar::GetType()
{
	return mType;
}

void EqSolverVar::SetDouble( const double d )
{
	mType = EQS_DOUBLE;
	mDoubleValue = d;
}

void EqSolverVar::SetString( const wxString& s )
{
	mType = EQS_STRING;
	mStrValue = s;
}

double EqSolverVar::GetDouble()
{
	return mDoubleValue;
}

bool EqSolverVar::GetBool()
{
	return mDoubleValue != 0.0;
}

wxString& EqSolverVar::GetString()
{
	return mStrValue;
}

/*** global opertor evaluation functions ***/

bool eq_op_logical_OR( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	bool ok =  ( arg1.GetDouble() != 0.0 ) || ( arg2.GetDouble() != 0.0 );

	if ( ok )
        result.SetDouble( 1.0 );
    else
        result.SetDouble( 0.0 );

	return TRUE;
}

bool eq_op_logical_AND( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	bool ok =  ( arg1.GetDouble() != 0.0 ) && ( arg2.GetDouble() != 0.0 );

	if ( ok )
        result.SetDouble( 1.0 );
    else
        result.SetDouble( 0.0 );

	return TRUE;
}

bool eq_op_logical_EQUALS( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	bool ok = ( arg1.GetType() == EQS_DOUBLE ) 
			  ? arg1.GetDouble() == arg2.GetDouble()
			  : arg1.GetString() == arg2.GetString();

	if ( ok )
        result.SetDouble( 1.0 );
    else
        result.SetDouble( 0.0 );

	return TRUE;
}

bool eq_op_binary_AND( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	long ok = ((long)arg1.GetDouble()) & ((long)arg2.GetDouble());

	result.SetDouble( (double)ok );

	return TRUE;
}

bool eq_op_binary_OR( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	long l = ((long)arg1.GetDouble()) | ((long)arg2.GetDouble());

	result.SetDouble( (double)l );

	return TRUE;
}

bool eq_op_ADD( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	if ( arg1.GetType() == EQS_STRING )
		result.SetString( arg1.GetString() + arg2.GetString() );
	else	
		result.SetDouble( arg1.GetDouble() + arg2.GetDouble() );
	
	return TRUE;
}

bool eq_op_SUB( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	result.SetDouble( arg1.GetDouble() - arg2.GetDouble() );

	return TRUE;
}

bool eq_op_MUL( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	result.SetDouble( arg1.GetDouble() * arg2.GetDouble() );

	return TRUE;
}

bool eq_op_DIV( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	result.SetDouble( arg1.GetDouble() / arg2.GetDouble() );

	return TRUE;
}

bool eq_op_CONTAINS( EqSolverVar& arg1, EqSolverVar& arg2, EqSolverVar& result )
{
	wxASSERT( arg1.GetType() == EQS_STRING ); // DBG::

	bool ok = arg1.GetString().Find( arg2.GetString() ) != -1;

	if ( ok )
        result.SetDouble( 1.0 );
    else
        result.SetDouble( 0.0 );

	return TRUE;
}

/***** Implementation for class EqSolverNode ******/

IMPLEMENT_ABSTRACT_CLASS( EqSolverNode, wxObject )

bool EqSolverNode::EvaluateBool( EqSolver& solver )
{
	EqSolverVar result; Evaluate( result, solver );

	return result.GetDouble() != 0.0;
}

double EqSolverNode::EvaluateDouble( EqSolver& solver )
{
	EqSolverVar result; Evaluate( result, solver );

	return result.GetDouble();
}

int EqSolverNode::EvaluateInt( EqSolver& solver )
{
	EqSolverVar result; Evaluate( result, solver );

	return (int)result.GetDouble();
}

wxString EqSolverNode::EvaluateString( EqSolver& solver )
{
	EqSolverVar result; bool ok = Evaluate( result, solver );

	if ( ok ) 
		return result.GetString();
	else
		return "[[ERROR]]";
}

/***** Implementation for class EqBinaryOpNode ******/

IMPLEMENT_CLASS( EqBinaryOpNode, EqSolverNode )

EqBinaryOpNode::EqBinaryOpNode( EqSolverEvalFunct evalFunct )

	: mpNode1( NULL ),
	  mpNode2( NULL ),
	  mEvalFunct( evalFunct )
{}

EqBinaryOpNode::EqBinaryOpNode( EqSolverNode* pNode1, EqSolverNode* pNode2, 
							    EqSolverEvalFunct evalFunct )

	: mpNode1( pNode1 ),
	  mpNode2( pNode2 ),
	  mEvalFunct( evalFunct )
{}

bool EqBinaryOpNode::Evaluate( EqSolverVar& result, EqSolver& solver )
{
	EqSolverVar res1, res2;

	if ( !mpNode1->Evaluate( res1, solver ) )
        return FALSE;
	if ( !mpNode2->Evaluate( res2, solver ) )
        return FALSE;

	return (*mEvalFunct)( res1, res2, result );
}

void EqBinaryOpNode::SetLeftNode( EqSolverNode* pLeft )
{
	mpNode1 = pLeft;
}

void EqBinaryOpNode::SetRightNode( EqSolverNode* pRight )
{
	mpNode2 = pRight;
}

/***** Implementation for class EqUnaryNotNode ******/

IMPLEMENT_CLASS( EqUnaryNotNode, EqSolverNode )

EqUnaryNotNode::EqUnaryNotNode( EqSolverNode* pNode1 )

	: mpNode1( pNode1 )
{}

bool EqUnaryNotNode::Evaluate( EqSolverVar& result, EqSolver& solver )
{
	if ( !mpNode1->Evaluate( result, solver ) )
        return FALSE;

	if ( result.GetDouble() == 0.0 )
		result.SetDouble( 1.0 );
	else
		result.SetDouble( 0.0 );

	return FALSE;
}

/***** Implementation for class EqVarNode ******/

IMPLEMENT_CLASS( EqVarNode, EqSolverNode )

EqVarNode::EqVarNode( const wxString& varName )

	: mVarName( varName )
{}

bool EqVarNode::Evaluate( EqSolverVar& result, EqSolver& solver )
{
	EqSolverVar* pFound = solver.FindVar( mVarName );

	if ( !pFound )
        return FALSE;

	result = *pFound;

	return TRUE;
}

/***** Implementation for class EqConstNode ******/

IMPLEMENT_CLASS( EqConstNode, EqSolverNode )

EqConstNode::EqConstNode( const EqSolverVar& constValue )

	: mConstValue( constValue )
{}

bool EqConstNode::Evaluate( EqSolverVar& result, EqSolver& solver )
{
	result = mConstValue;

	return TRUE;
}

/***** Implementation for class EqSolver ******/

EqSolver::EqSolver()

	: mVarsHash( wxKEY_STRING )
{}

EqSolver::~EqSolver()
{
	ResetAllVars();
}

void EqSolver::SetVar( const wxString& name, const wxString& value )
{
	RemoveVar( name );

	bool isNum = TRUE;
    size_t i;
	for( i = 0; i != value.Length(); ++i )
	{
		char ch = value[i];

		if ( !( ch >= '0' && ch <= '9') ) 
		{
			isNum = FALSE;
			break;
		}
	}

	EqSolverVar* pVar = new EqSolverVar();
	pVar->SetName( name );

	if ( isNum )
		pVar->SetDouble( (double)i );
	else
		pVar->SetString( value );

	AddVar( pVar );
}

void EqSolver::SetStringVar( const wxString& name, const wxString& value )
{
	AddVar( new EqSolverVar( name, value ) );
}

void EqSolver::SetIntVar( const wxString& name, int value )
{
	AddVar( new EqSolverVar( name, (double)value ) );
}

void EqSolver::SetDoubleVar( const wxString& name, double d )
{
	AddVar( new EqSolverVar( name, d ) );
}

void EqSolver::RemoveVar( const wxString& name )
{
	EqSolverVar* pVar = (EqSolverVar*)mVarsHash.Get( name );

	if ( pVar == NULL )
        return;

	for( size_t i = 0; i != mVars.Count(); ++i )
    {
		if ( mVars[i] == pVar )
		{
			mVars.Remove( i );
			break;
		}
    }
	mVarsHash.Delete( name );

	delete pVar;
}

EqSolverVar* EqSolver::FindVar( const wxString& name )
{
	EqSolverVar* pVar = (EqSolverVar*)mVarsHash.Get( name );

	return pVar;
}

wxString EqSolver::GetStringValue( const wxString& name )
{
	EqSolverVar* pVar = FindVar( name );

	if ( pVar == NULL )
        return "";

	if ( pVar->GetType() == EQS_INT )
		return IntToStr( (int)pVar->GetDouble() );
	else
    {
    	if ( pVar->GetType() == EQS_STRING )
	    	return pVar->GetString();
    }

	return "";
}

static inline bool get_next_word( char*& cur, char* end )
{
    while( cur < end && 
           ( *cur == ' ' || *cur == 10 || *cur == 13 || *cur == '\t' ) )
    {
        ++cur;
    }
    return ( cur < end );
}

static inline bool skip_word( char*& cur, char* end )
{
	if ( *cur == '\"' )
	{
		++cur;
		while( cur < end && *cur != '\"' )
            ++cur;

		if ( cur < end )
            ++cur; // skip trailing '\"'

		return cur < end;
	}

    while( cur < end && *cur != 10 && *cur != 13 &&
           *cur != ' ' && *cur !='\t' )
    {
        ++cur;
    }
    return ( cur < end );
}

static wxString _gTmpForGetWordAt = "";

static inline wxString& get_word_at( char*& cur, char* end )
{
	char* start = cur;
	skip_word( cur, end );

	// use global to improve performance

	_gTmpForGetWordAt = "";
	ConcatString( _gTmpForGetWordAt, start, size_t(cur-start) );

	return _gTmpForGetWordAt;
}

EqSolverVar* EqSolver::Evaluate( const wxString& exprText )
{
	// FOR NOW:: evaluates expressions which contain
	//			 either || or && operators, but not boths.
	//			 variables can be prefixed with inversion
	//			 operator '!', e.g.:
	//
	//			 !__WINDOWS__ || __UNIX__
	//			 !__WINDOWS__ && !__MAC__

	EqSolverVar* pVar = new EqSolverVar();
	char* cur = (char*)(const char*)exprText;
	char* end = cur + exprText.Length();

	bool isOrExpr = TRUE;

	char* c = cur;
	while( c < end )
	{
		if ( *c == '|' ) 
            break;
		if ( *c == '&' )
        {
            isOrExpr = FALSE;
            break;
        }

		++c;
	}

	bool result = FALSE;

	if (!isOrExpr ) result = TRUE;

	while( get_next_word( cur, end ) )
	{
		bool isInverse = FALSE;

		if ( *cur == '!' ) 
		{ 
			isInverse = TRUE; ++cur;
			if ( !get_next_word( cur, end ) )
                break;
		}

		char* start = cur;
		skip_word( cur, end );

		wxString var = wxString( start, 0, (size_t)(cur-start) );

		if ( var.Length() )
        {
			if ( var[0u] == '&' || var[0u] == '|' )
                continue;
        }
		bool val = HasTureValue( var );
		if ( isInverse )
            val = !val;

		if ( isOrExpr && val == TRUE ) 
		{
			result = TRUE;
			break; // result is TRUE
		}

		if ( !isOrExpr && val == FALSE ) 
		{
			result = FALSE;
			break;
		}
	}

	pVar->SetDouble( (double)result );

	return pVar;
}

bool EqSolver::EvaluateToBool( const wxString& exprText )
{
	EqSolverVar* pVar = Evaluate( exprText );

	wxASSERT( pVar->GetType() == EQS_INT ); // DBG::

	bool result = pVar->GetDouble() != 0.0;

	delete pVar;

	return result;
}

void EqSolver::ResetAllVars()
{
	WX_CLEAR_ARRAY( mVars );

	mVarsHash.Clear();
}

#define MY_HEX_TO_BIN(x) ( x >= 'A' ? 10 + x - 'A' : x - '0' )

EqSolverNode* ParseExpr( const wxString& exprText )
{
	char* cur = (char*)(const char*)exprText;
	char* end = cur + exprText.Length();

	wxStringTokenizer tok( exprText, " \t\r\n" );

	return DoParseExprRecursive( tok, tok.GetNextToken() );
}

EqSolverNode* ParseSingleNode( wxStringTokenizer& tok, const wxString& tk )
{
	if ( tk.Length() == 0 )
        return NULL;

	if ( tk == "==" )
		return new EqBinaryOpNode( eq_op_logical_EQUALS );
	else if ( tk == '+' )
		return new EqBinaryOpNode( eq_op_ADD );
	else if ( tk == '-' )
		return new EqBinaryOpNode( eq_op_SUB );
	else if ( tk == '*' )
		return new EqBinaryOpNode( eq_op_MUL );
	else if ( tk == '/' )
		return new EqBinaryOpNode( eq_op_DIV );
	else if ( tk == "[]" )
		return new EqBinaryOpNode( eq_op_CONTAINS );
	else if ( tk == "&&" )
		return new EqBinaryOpNode( eq_op_logical_AND );
	else if ( tk == "||" )
		return new EqBinaryOpNode( eq_op_logical_OR );
	else if ( tk == "|" )
		return new EqBinaryOpNode( eq_op_binary_OR );
	else if ( tk == "&" )
		return new EqBinaryOpNode( eq_op_binary_AND );
	else if ( tk == '(' )
		return DoParseExprRecursive( tok, tok.GetNextToken() );
	else if ( tk == '!' )
		return new EqUnaryNotNode( ParseSingleNode( tok, tok.GetNextToken() ) );
	else
	{
		char ch = tk[0];

		if ( ( ch >= '0' && ch <= '9' ) || ch == '.' )
		{
			float d = 0.0;
			sscanf( tk.c_str(), "%f", &d );

			return new EqConstNode( EqSolverVar( d ) );
		}
		else if ( ch == '"' )
		{
			wxString constStr;

            size_t i;
			for( i = 1; i != tk.Length(); ++i )
			{
				ch = tk[i];

				if ( ch == '\"' )
                    break;

				if ( tk[i] == '%' ) // handle %XX escaped hexidecimal char-codes
				{
					char ch1 = tk[i+1];
					char ch2 = tk[i+2];
					char c = MY_HEX_TO_BIN( ch1 ) * 16 + MY_HEX_TO_BIN( ch2 );

					i += 2;

					constStr += c;
				}
				else
					constStr += ch;
			}
			
			// strip surrounding dbl. quotes

			return new EqConstNode( EqSolverVar( constStr ) );
		}
		else
		if ( ( ch >= 'a' && ch <= 'z' ) ||
			 ( ch >= 'A' && ch <= 'Z' ) || ch == '_' )
			return new EqVarNode( tk );
		else
		{
			wxFAIL_MSG( "Unrecognized operator found: " + tk + ", eq:" + tok.GetString() );

			// DBG:: error - unrecognized token found!
			return new EqBinaryOpNode( eq_op_logical_AND );;
		}

		return NULL;
	}

	return NULL;
}

EqSolverNode* DoParseExprRecursive( wxStringTokenizer& tok, const wxString& tk )
{
	// simple secenario of node-tree construction:
	//
	// a + b - c + d
	// |   |   |   |
	// \---|   |   |
	//     \---|   |
	//         \---|
	//             |
	//           return;

	if ( tk == ')' )
        return NULL;

	EqSolverNode* pNode1 = ParseSingleNode( tok, tk );

	if ( !pNode1 ) return NULL;

	for(;;)
	{
		if ( !tok.HasMoreTokens() )
			return pNode1;

		wxString t = tok.GetNextToken();

		if ( t == ')' )
            return pNode1;

		EqSolverNode* pNode2 = ParseSingleNode( tok, t );

		if ( !pNode2 )
            return NULL;

		t = tok.GetNextToken();

		EqSolverNode* pNode3 = ParseSingleNode( tok, t );

		if ( !pNode2 )
            return NULL;

		wxASSERT( pNode2->IsKindOf( CLASSINFO( EqBinaryOpNode ) ) );

		EqBinaryOpNode* pOpNode = (EqBinaryOpNode*)pNode2;

		pOpNode->SetLeftNode( pNode1 );
		pOpNode->SetRightNode( pNode3 );

		pNode1 = pOpNode;

		if ( !tok.HasMoreTokens() )
			return pNode1;
	}

	return pNode1; // never reached
}

/*** protected methods ***/

void EqSolver::AddVar( EqSolverVar* pVar )
{
	if ( FindVar( pVar->GetName() ) )
		RemoveVar( pVar->GetName() );

	mVars.Add( pVar );
	mVarsHash.Put( pVar->GetName(), pVar );
}

bool EqSolver::HasTureValue( const wxString& varName )
{
	EqSolverVar* pVar = FindVar( varName );

	if ( pVar == NULL )
        return FALSE;

	if ( pVar->GetType() == EQS_STRING )
		return TRUE;

	if ( pVar->GetType() == EQS_INT )
		return pVar->GetDouble() != 0.0;

	return FALSE;
}

/***** Implementation for class ScriptPreprocessor ******/

ScriptPreprocessor::ScriptPreprocessor()

	: mpSolver( NULL )
{}

ScriptPreprocessor::~ScriptPreprocessor()
{}

wxString ScriptPreprocessor::Process( const wxString& sourceText, EqSolver* pSolver )
{
	mpSolver = pSolver;
	char* cur = (char*)(const char*)sourceText;
	char* end = cur + sourceText.Length();

	mpBuf = new char[ sourceText.Length() + 1 ];
	mpBufPos = mpBuf;

	mBufLocked = FALSE;

	ProcessBlockRecursively( cur, end, FALSE );

	*mpBufPos = '\0';

	wxString result = mpBuf;

	delete [] mpBuf;

	return result;
}

enum PREPOCESSOR_STATES
{
	EXPECT_ELSE,
	EXPECT_ENDIF,
	EXPECT_ANYTHING
};

wxString ScriptPreprocessor::ProcessBlockRecursively( char*& cur, char* end, 
													  bool isNestedBlock )
{
	char tmpCh = *(end+1); // terminate text-block temporarely

	wxString outText = "";

	int state = EXPECT_ANYTHING;
	bool inclMode = TRUE;
	char* curBlock = cur;

	while( cur < end)
	{
		#if 1
			char* found = (char*)memchr( cur, '#', (size_t)(end-cur) );
		#else
			char* found = cur;
			while( *found && *found != '#' ) ++found;
		#endif

		if ( found == NULL )
            break;

		cur = found;

		++cur; 
		char* start = cur;

		// extract the keyword following '#'

		if ( memcmp( start, "if", 2 ) == 0 )
		{
			cur+=2;

			if ( state == EXPECT_ELSE ||
				 state == EXPECT_ENDIF )
			{
				cur = found;

				bool collectText = FALSE;

				bool wasLocked = mBufLocked;

				if ( state == EXPECT_ENDIF && curBlock != NULL )
				{
					OutputBlock( outText, curBlock, found );

					collectText = TRUE;
				}
				else
					mBufLocked = TRUE;

				wxString sub = ProcessBlockRecursively( cur, end, TRUE );

				if ( state == EXPECT_ENDIF && collectText )
				{
					curBlock = cur;
				}

				mBufLocked = wasLocked;
			}
			else
			{
				// otherwise EXPECT_ANYTHING

				OutputBlock( outText, curBlock, found );

				if ( EvalExpr( cur, end ) == TRUE )
				{
					state = EXPECT_ENDIF;
					curBlock = cur;
				}
				else
					state = EXPECT_ELSE;
			}
		}
		else
		if ( memcmp( start, "elif", 4 ) == 0 )
		{
			cur+=4;

			wxASSERT( state != EXPECT_ANYTHING ); // DBG::

			if ( state == EXPECT_ENDIF )
			{
				OutputBlock( outText, curBlock, found );

				SkipExpr( cur, end );
			}
			else
			if ( state == EXPECT_ELSE )
			{
				if ( EvalExpr( cur, end ) == TRUE )
				{
					state = EXPECT_ENDIF;
					curBlock = cur;
				}
			}
		}
		else
		if ( memcmp( start, "else", 4 ) == 0 )
		{
			cur+=4;

			wxASSERT( state != EXPECT_ANYTHING ); // DBG::

			if ( state == EXPECT_ENDIF )
			
				OutputBlock( outText, curBlock, found );
			else
			if ( state == EXPECT_ELSE )
			{
				state = EXPECT_ENDIF;
				curBlock = cur;
			}
		}
		else
		if ( memcmp( start, "endif", 5 ) == 0 )
		{
			cur+=5;

			wxASSERT( state != EXPECT_ANYTHING ); // DBG::

			if ( state == EXPECT_ENDIF )
				OutputBlock( outText, curBlock, found );

			curBlock = cur;

			state = EXPECT_ANYTHING;

			if ( isNestedBlock == TRUE )
                break;
		}
		else
		if ( *start == '#' ) // "##VARNAME##" substitution
		{
			cur+=1; 
			start = cur;
			while( cur < end && *cur != '#' )
                ++cur;

			wxString value = mpSolver->GetStringValue( wxString( start, 0, size_t(cur-start) ) );

			cur += 2; // skip closing "##" pair

			if ( curBlock != NULL )
			{
				OutputBlock( outText, curBlock, found );

				char* valStr = (char*)(const char*)value;
				char* valStrEnd = valStr + value.Length();
				OutputBlock( outText, valStr, valStrEnd );

				curBlock = cur;
			}
		}
		else
		if ( memcmp( start, "undef", 5 ) == 0 )
		{
			cur+=5;

			if ( !get_next_word( cur, end ) )
                break;

			wxString var = get_word_at( cur, end );

			if ( curBlock != NULL )
			{
				mpSolver->RemoveVar( var );

				OutputBlock( outText, curBlock, found );
				curBlock = cur;
			}
		}
		else
		if ( memcmp( start, "define", 6 ) == 0 )
		{
			cur+=6;

			if ( !get_next_word( cur, end ) )
                break;

			wxString var = get_word_at( cur, end );

			if ( !get_next_word( cur, end ) )
                break;

			wxString val = get_word_at( cur, end );

			if ( val.Length() > 1 && val[0u] == '\"' )
            {
				// remove double-quotes if any
				val = wxString( val, 1, val.Length() - 2 );
            }
			if ( curBlock != NULL )
			{
				mpSolver->SetVar( var, val );

				OutputBlock( outText, curBlock, found );
				curBlock = cur;
			}
		}
		else
		{
			// TBD::
		}
	}
	
	if ( isNestedBlock == FALSE )
	
		OutputBlock( outText, curBlock, end );

	*(end+1) = tmpCh; // un-terminate text-block

	return outText;
}

void ScriptPreprocessor::SkipExpr( char*& cur, char* till )
{
    while( cur < till && *cur != 10 && *cur != 13 && *cur != ']' )
		++cur;

	if ( cur < till && *cur == ']' )
        ++cur;
}

bool ScriptPreprocessor::EvalExpr( char*& cur, char* till )
{
	if ( !get_next_word( cur, till ) )
        return FALSE;

	if ( *cur == '[' )
        ++cur;

	if ( !get_next_word( cur, till ) )
        return FALSE;

	char* start = cur;

    while( cur < till && *cur != 10 && *cur != 13 && *cur != ']' )
		++cur;

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

	bool result = mpSolver->EvaluateToBool( expr );

	if ( cur < till && *cur == ']' )
        ++cur;

	return result;
}

void ScriptPreprocessor::OutputBlock( wxString& outText, char*& from, char* till )
{
	if ( from == NULL )
        return;

	//outText += wxString( from, 0, size_t(till-from) );

	if ( !mBufLocked )
	{
		memcpy( mpBufPos, from, size_t(till - from) );

		mpBufPos += size_t(till - from);
	}

	from = NULL; // automatically terminate current block
}

// just test it

void TestEqSolver()
{
	char buf[1024*32];

	FILE* f = fopen( "eqtest.txt", "rt" );

	if ( f == NULL ) 
	{
		printf( "ERROR: cannot open test file eqtest.txt!\n" );
		return;
	}

	int n = fread( buf, 1, sizeof(buf), f );

	buf[n] = '\0';

	ScriptPreprocessor sp;

	EqSolver solver;
	wxString result = sp.Process( buf, &solver );

	printf( "------- Source text -------:\n%s\n-------- Output text --------\n%s\n",
		    buf, (const char*)result );

	fflush( stdout );
}
