text: /////////////////////////////////////////////////////////////////////////////
  // Name:        No names yet.
  // Purpose:     Contrib. demo
  // Author:      Aleksandras Gluchovas
  // Modified by:
  // Created:     22/09/98
  // RCS-ID:      $Id: scriptbinder.cpp,v 1.5 2001/11/18 12:32:15 GD Exp $
  // Copyright:   (c) Aleskandars Gluchovas
  // Licence:   	wxWindows licence
  /////////////////////////////////////////////////////////////////////////////
  
  // 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
  
  #ifndef __DARWIN__
  #  include <malloc.h>
  #endif
  #include <string.h>
  #include <memory.h>
  
  #include <stdio.h>   // import sprintf() (for doubles)
  #include <stdlib.h>  // import atoi()    (for integers)
  
  #include "scriptbinder.h"
  
  // helper functions
  
  static size_t log2(size_t nr)
  {
  	size_t tmp = 0;
  	while (nr >= 2 ) 
  	{
  		nr /= 2;
  		++tmp;
  	}
  
  	return tmp;
  }
  
  /***** Implementation for class ScriptStream *****/
  
  ScriptStream::ScriptStream()
  	: mpBuf(0),
  	  mSize(0),
  	  mCapacity(0)
  {}
  
  ScriptStream::~ScriptStream()
  {
  	if ( mpBuf ) delete mpBuf;
  }
  
  void ScriptStream::WriteBytes( const void* srcBuf, size_t count )
  {
  	if ( !count ) return;
  
  	// increase the capacity if necessary
  	if ( mSize + count > mCapacity )
  	{
  			mCapacity = 
  				(  0x2 << (log2( mSize + count ) + 1 ) );
  
  			if ( mCapacity < 128 ) mCapacity = 128;
  
  			char* oldBuf = mpBuf;
  
  			mpBuf = new char[mCapacity];
  
  			if ( oldBuf )
  			{
  				memcpy( mpBuf, oldBuf, mSize );
  				delete oldBuf;
  			}
  	}
  
  	// append new data
  	memcpy( &mpBuf[mSize], srcBuf, count );
  
  	mSize += count;
  }
  
  ScriptStream& ScriptStream::operator<<( const char* str )
  {
  	WriteBytes( str, strlen( str ) );
  
  	return *this;
  }
  
  ScriptStream& ScriptStream::operator<<( const string& str )
  {
  	if ( str.length() < 512 )
  	{
  		char buf[512];
  		size_t len = str.length();
  
  		for( size_t i = 0; i != len; ++i )
  			buf[i] = str[i];
  
  		WriteBytes( buf, len );
  	}
  	else
  		WriteBytes( str.c_str(), str.length() );
  
  	return *this;
  }
  
  ScriptStream& ScriptStream::operator<<( char ch )
  {
  	WriteBytes( &ch, 1 );
  
  	return *this;
  }
  
  void ScriptStream::endl()
  {
  	char ch = '\n';
  	WriteBytes( &ch, 1 );
  }
  
  /***** Implementation for class ScriptTemplate *****/
  
  ScriptTemplate::ScriptTemplate( const string& templateText )
  {
  	string tmp = templateText;
  
  	mTText = (char*)malloc( tmp.length() + 1 );
  
  	strcpy( mTText, tmp.c_str() );
  }
  
  ScriptTemplate::~ScriptTemplate()
  {
  	for( size_t i = 0; i != mVars.size(); ++i )
  
  		delete mVars[i];
  
  	free( mTText );
  }
  
  bool ScriptTemplate::HasVar( const char* name )
  {
  	for( size_t i = 0; i != mVars.size(); ++i ) 
  
  		if ( strcmp( mVars[i]->mName, name ) == 0 )
  
  			return 1;
  
  	return 0;
  }
  
  void ScriptTemplate::AddStringVar ( const char* name, int ofs )
  {
  	mVars.push_back( new TVarInfo( name, ofs, TVAR_STRING ) );
  }
  
  void ScriptTemplate::AddIntegerVar( const char* name, int ofs )
  {
  	mVars.push_back( new TVarInfo( name, ofs, TVAR_INTEGER ) );
  }
  
  void ScriptTemplate::AddDoubleVar ( const char* name, int ofs )
  {
  	mVars.push_back( new TVarInfo( name, ofs, TVAR_DOUBLE ) );
  }
  
  void ScriptTemplate::AddObjectRefArray( const char*     name,
  										int			    ofsRefToFirstObj,
  										int			    ofsObjSizeInt,
  										int			    ofsObjRefTempl
  									  )
  {
  	TArrayInfo* pInfo = new TArrayInfo( name );
  
  	mVars.push_back( pInfo );
  
  	pInfo->mRefOfs         = ofsRefToFirstObj;
  	pInfo->mSizeIntOfs     = ofsObjSizeInt;
  	pInfo->mObjRefTemplOfs = ofsObjRefTempl;
  }
  
  inline void ScriptTemplate::PrintVar( TVarInfo*   pInfo, 
  								    void*       dataObj, 
  									ScriptStream& stm )
  {
  	char buf[128];
  
  	switch ( pInfo->mType )
  	{
  		case TVAR_INTEGER :
  			{
  				sprintf(buf, "%d",*( (int*) ((char*)dataObj + pInfo->mOfs) ) );
  				
  				stm.WriteBytes( buf, strlen(buf ) );
  				break;
  			}
  
  		case TVAR_STRING :
  			{
  				string& str = *( (string*) ((char*)dataObj+pInfo->mOfs) );
  
  				const char* cs = str.c_str();
  #ifdef DEBUG_WEIRED_OFFSETS
  				cout << "DBG:: cs address is " << (int)cs << endl;
  				cout << "DBG:: str address is " << (int)(&str) << endl;
  				cout << "DBG:: dataObj points to " << (int)dataObj << endl;
  				cout << "DBG:: pInfo->mOfs value is " << (int)pInfo->mOfs << endl;
  				cout << "DBG:: d+pInfo->mOfs is " << (int)((char*)dataObj + pInfo->mOfs) << endl;
  				cout << "DBG:: pInfo->mName is " << pInfo->mName << endl;
  				cout << "DBG:: pInfo->mType is " << pInfo->mType << endl;
  				cout << "DBG:: end of dump. " << endl;
  
  				cout << "DBG:: cs value is " << endl << cs << endl;
  #endif
  				stm.WriteBytes( cs, strlen(cs) );
  				break;
  			}
  
  		case TVAR_DOUBLE :
  			{
  				sprintf( buf, "%f", 
  					     *( (double*)( (char*)dataObj+pInfo->mOfs)  ) );
  
  				stm.WriteBytes( buf, strlen(buf ) );
  				break;
  			}
  
  		case TVAR_REF_ARRAY :
  			{
  				TArrayInfo& info = *((TArrayInfo*)pInfo);
  
  				int sz = *((int*) ( (char*)dataObj+info.mSizeIntOfs ));
  				if ( !sz ) 
  				{
  					// DBG::
  					int u = 0;
  					++u;
  					break;
  				}
  
  				int*   array = *((int**)( (char*)dataObj+info.mRefOfs ));
  	
  				ScriptTemplate* pRefTempl;
  
  				for( int i = 0; i != sz; ++i )
  				{
  					pRefTempl = 
  						*((ScriptTemplate**)((char*)(array[i])+info.mObjRefTemplOfs));
  
  					pRefTempl->PrintScript( (void*)array[i], stm );
  				}
  
  				break;
  			}
  
  		default : break;
  	}
  }
  
  void ScriptTemplate::PrintScript( void* dataObj, ScriptStream& stm )
  {
  	char* cur   = mTText;
  
  	// template parsing loop
  	do
  	{
  		char* start = cur;
  
  		while( *cur != '\0' && *cur != '$' ) ++cur;
  
  		// flush text collected between variables
  		stm.WriteBytes( start, cur - start );
  
  		if ( *cur == '\0' ) break;
  
  		cur += 2; // skip to the name of the var
  
  		start = cur;
  
  		while( *cur != ')' ) ++cur;
  
  		// put terminating zero temorarely
  
  		*cur = '\0';
  
  		// look up variable
  
  		size_t sz = mVars.size();
  		bool found = 0;
  
  		for( size_t i = 0; i != sz; ++i ) 
  		{
  			if ( strcmp( mVars[i]->mName, start ) == 0 )
  			{
  				PrintVar( mVars[i], dataObj, stm );
  
  				*cur = ')';	// remove terminating zero
  				++cur;
  				found = 1;
  				break;
  			}
  		}
  
  		// variable referred by template script is not
  		// registered to this tempalte object
  		// ASSERT( found ); 
  
  	} while(1);
  }
  
  /***** implementation for class ScriptSection *****/
  
  int ScriptSection::mIdCounter = 0;
  
  ScriptSection::ScriptSection( const string&   name,
  						      const string&   body,
  							  ScriptTemplate* pSectionTemplate,
  							  ScriptTemplate* pReferenceTemplate,
  							  bool            autoHide,
  							  bool            sorted
  							)
  	: mpParent ( NULL ),
  
  	  mName    ( name ),
  	  mBody    ( body ),
  
  	  mAutoHide( autoHide ),
  	  mSortOn  ( sorted ),
  
  	  mpSectTempl( pSectionTemplate ),
  	  mpRefTempl ( pReferenceTemplate ),
  
  	  mRefCount( 0 ),
  	  mArrSize( 0 )
  {
  	// generate GUID
  
  	char buf[32];
  	sprintf( buf, "%d", ++mIdCounter );
  	mId = buf;
  }
  
  ScriptSection::~ScriptSection()
  {
  	SectListT lst = mSubsections;
  
  	while( mSubsections.size() )
  
  		mSubsections[0]->RemoveRef();
  
  	for( size_t i = 0; i != mReferences.size(); ++i )
  
  		mReferences[i]->RemoveRef();
  }
  
  void ScriptSection::AddRef()
  {
  	++mRefCount;
  }
  
  void ScriptSection::RemoveRef()
  {
  	if ( !mRefCount || !(--mRefCount) )
  	{
  		if (mpParent)
  		{
  			// remove ourselves from parent's list
  
  			SectListT& lst = mpParent->mSubsections;
  			for( size_t i = 0; i != lst.size(); ++i )
  
  				if ( lst[i] == this )
  				{
  					lst.erase( &lst[i] );
  					break;
  				}
  		}
  
  		delete this;
  	}
  }
  
  ScriptSection* ScriptSection::GetSubsection( const char* name )
  {
  	// FOR NOW:: fixed section name length
  	char buf[128];
  
  	size_t cur = 0;
  
  	while( name[cur] && name[cur] != '/' )
  	{
  		buf[cur] = name[cur];
  		++cur;
  	}
  
  	// ASSERT( cur < sizeof(buf) );
  
  	buf[cur] = '\0';
  
  	size_t sz = mSubsections.size();
  
  	for( size_t i = 0; i != sz; ++i )
  	{
  		// DBG::
  		//ScriptSection& sect = *mSubsections[i];
  
  		if ( mSubsections[i]->mName == buf )
  		{
  			if ( name[cur] == '/' )
  
  				// search recursivelly
  				return mSubsections[i]->GetSubsection( &name[cur+1] );
  			else
  				return mSubsections[i];
  		}
  	}
  
  	return 0;
  }
  
  void ScriptSection::AddSection( ScriptSection* pSection,
  							    bool addToReferencesToo
  							  )
  {
  	mSubsections.push_back( pSection );
  
  	pSection->AddRef();
  
  	// can add section to multiple containers
  	// ASSERT( pSection->mpParent == 0 );
  
  	pSection->mpParent = this;
  
  	if ( addToReferencesToo )
  
  		AddReference( pSection );
  }
  
  void ScriptSection::AddReference( ScriptSection* pReferredSection )
  {
  	mReferences.push_back( pReferredSection );
  
  	pReferredSection->AddRef();
  
  	// set up mandatory fields used by ScriptTemplate
  	mArrSize  = mReferences.size();
  	if ( mArrSize )
  		mRefFirst = (void*)&mReferences[0];
  }
  
  SectListT& ScriptSection::GetSubsections()
  {
  	return mSubsections;
  }
  
  // static method:
  void ScriptSection::RegisterTemplate( ScriptTemplate& sectionTempalte )
  {
  	int nameOfs, bodyOfs, idOfs,
  		arrRefOfs, arrSizeOfs, refTemplOfs;
  
  	// obtaining offsets of member vars
  	
  	GET_VAR_OFS( ScriptSection, mName,     &nameOfs    )
  	GET_VAR_OFS( ScriptSection, mBody,     &bodyOfs    )
  	GET_VAR_OFS( ScriptSection, mId,       &idOfs      )
  	GET_VAR_OFS( ScriptSection, mRefFirst, &arrRefOfs  )
  	GET_VAR_OFS( ScriptSection, mArrSize,  &arrSizeOfs )
  
  	GET_VAR_OFS( ScriptSection, mpRefTempl, &refTemplOfs )
  
  	// registering member variables with given script template
  
  	sectionTempalte.AddStringVar( "NAME", nameOfs );
  	sectionTempalte.AddStringVar( "BODY", bodyOfs );
  	sectionTempalte.AddStringVar( "ID",   idOfs   );
  
  	sectionTempalte.AddObjectRefArray( "REFLIST",
  									   arrRefOfs, arrSizeOfs, refTemplOfs );
  }
  
  void ScriptSection::Print( ScriptStream& stm )
  {
  	// TBD:: sorting
  
  	// print out this content first
  	if ( mpSectTempl )
  
  		mpSectTempl->PrintScript( this, stm );
  
  	// attach contents subsections at the end of this content
  
  	for( size_t i = 0; i != mSubsections.size(); ++i )
  
  		mSubsections[i]->Print( stm );
  }
  
  void ScriptSection::DoRemoveEmptySections(int& nRemoved, SectListT& removedLst)
  {
  	for( size_t i = 0; i != mSubsections.size(); ++i )
  	{
  		ScriptSection& sect = *mSubsections[i];
  
  		sect.DoRemoveEmptySections( nRemoved, removedLst );
  
  		if (sect.mAutoHide )
  			
  			if ( sect.mReferences.size() == 0 )
  			{
  				bool found = 0;
  				for( size_t k = 0; k != removedLst.size(); ++k )
  
  					if ( removedLst[k] == &sect )
  					{
  						found = 1;
  						break;
  					}
  				
  				if ( !found )
  				{
  					removedLst.push_back( &sect );
  					++nRemoved;
  
  					delete &sect;
  					--i;
  				}
  			}
  	}
  }
  
  void ScriptSection::DoRemoveDeadLinks( SectListT& removedLst)
  {
  	size_t dsz = removedLst.size();
  
  	for( size_t i = 0; i != mSubsections.size(); ++i )
  	{
  		mSubsections[i]->DoRemoveDeadLinks( removedLst );
  	}
  
  	for( size_t n = 0; n != mReferences.size(); ++n )
  	{
  		for( size_t k = 0; k != dsz; ++k )
  
  			if ( removedLst[k] == mReferences[n] )
  			{
  				mReferences.erase( &mReferences[n] );
  				--n;
  
  				// set up mandatory fields used by ScriptTemplate
  				mArrSize  = mReferences.size();
  				if ( mArrSize )
  					mRefFirst = (void*)&mReferences[0];
  
  				break;
  			}
  	}
  }
  
  
  void ScriptSection::RemoveEmptySections()
  {
  	// FIXME:: this is very_very_very slow alg.! +"doesn't work"
  
  	int nRemoved = 0;
  
  	do
  	{
  		SectListT removedLst;
  		nRemoved = 0;
  
  		DoRemoveEmptySections( nRemoved, removedLst );
  
  		DoRemoveDeadLinks( removedLst );
  	}
  	while( nRemoved );
  }
  
  /***** Iimplementation for class DocGeneratorBase *****/
  
  bool DocGeneratorBase::SaveDocument( const char*    fname, 
  								     const char*    fopenOptions,
  								     ScriptSection* pFromSection
  								   )
  {
  	FILE* fp = fopen( fname, fopenOptions );
  
  	if ( (int)fp == -1 ) return 0;
  
  	ScriptStream stm;
  
  	// check if derived class agrees about saving it
  	if ( !OnSaveDocument( stm ) ) return 0;
  
  	if ( pFromSection )
  
  		pFromSection->Print( stm );
  	else
  	{
  		ScriptSection* pTopSect = GetTopSection();
  		// ASSERT( pTopSect );
  		pTopSect->Print( stm );
  	}
  
  	size_t nWrite = fwrite( stm.GetBuf(), 1, stm.GetBufSize(), fp );
  
  	if ( nWrite != stm.GetBufSize() ) return 0;
  
  	fclose( fp );
  
  	return 1;
  
  	// that^s it
  }
  