// testRooDll.cpp

// build:
//  cl /GX testRooDll.cpp

# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include <iostream.h>
# include <fstream.h>
# include <strstrea.h>
# include <tchar.h>
# include <ctype.h>

# include <set>

# include <string>

# include <vector>

using namespace std;

# define WIN32_LEAN_AND_MEAN
# include <windows.h>

# include "roodll.h"

// the 'GlobalHandleStore' class is a handy wrapper class for a global handle
// that is returned by a roo.dll entry point. A helpful casting operator converts
// a global handle to a safe 'const char*'. This obviates the use of actual Global*
// function calls within the body of the testRooDll.cpp program. In addition,
// the class implicitly frees the handle when the instance context concludes.

class GlobalHandleStore
{
public :

	GlobalHandleStore( HGLOBAL hGlobal=NULL )
	{
		_hGlobal = hGlobal;
	}

	virtual ~GlobalHandleStore()
	{
		if( _hGlobal )
			GlobalFree( _hGlobal );
	}

	operator const char* () const
	{
		const char* pResult = IsValid() ? (const char*) GlobalLock( _hGlobal ) : "";
		if( IsValid() )
			GlobalUnlock( _hGlobal );
		return pResult;
	}

protected :

	bool IsValid() const
	{
		return
			_hGlobal && GlobalFlags( _hGlobal ) != GMEM_INVALID_HANDLE ? true
			: false;
	}

protected :

	HGLOBAL _hGlobal;
};

extern "C"
{
	// the PerformCallback function is invoked by the roo! 'callback' built-in function

	GLOBALHANDLE CALLBACK PerformCallback(
		LPCSTR invocationContext,
		LPCSTR pCallbackOption,
		... )
	{
		cout << "testRooDll callback, option '" << pCallbackOption << "' received arguments:" << endl;

		// process callback arguments
		// a variable number of arguments follow the pMethod parameter of this function
		
		try
		{
			va_list pStrs; // variable value name iteration list
			
			// establish initial position
			
			va_start( pStrs, pCallbackOption );

			int argno=1;
			
			for( LPCTSTR pCurrentStr = va_arg( pStrs, LPCTSTR ); // advance to parameter after pMethod
			
			/**/ pCurrentStr != NULL; // continue for all parameter strings
			
			/**/ pCurrentStr = va_arg( pStrs, LPCTSTR ) ) // advance to next parameter string
			{
				
				cout << argno++ << ". " << pCurrentStr << endl;
			}
			
			// va_end( pStrs ); // conclude key name iteration
		}
		
		catch( ... )
		{
			cerr << "A bad string locator was passed to the testRooDll callback" << endl;
		}

		// invoke the method that is being called back

		string sResult( "all is swell" );

		// convert the method result to a global handle

		int lenText = sResult.size();

		GLOBALHANDLE gHandle = GlobalAlloc( GHND, sizeof( TCHAR ) * ( lenText + 1 ) );

		if( gHandle )
		{
			_tcscpy( (LPTSTR) (LPVOID) GlobalLock( gHandle ), sResult.c_str() );
			GlobalUnlock( gHandle );
		}

		// return the result global handle
		// to the function in the program associated with the ExternalClass.
		// the global handle must be freed by the external program !

		return gHandle;
	}

	typedef GLOBALHANDLE (WINAPI* entry_point_with_callback_t ) (
		LPCSTR invocationContext,
		LPCTSTR pCallbackOption,
		LPCSTR pEntryPointName,
		... );

	typedef GLOBALHANDLE (WINAPI* execute_function_t ) (
		LPCSTR pBuiltinFunctionName,
		... );

	typedef GLOBALHANDLE (WINAPI* remove_instance_t ) ( LPCSTR pInstance, ... );

	typedef GLOBALHANDLE (CALLBACK* callback_method_t ) ( LPCSTR invocationContext, LPCTSTR pCallbackOption, ... );

	typedef void (WINAPI* set_callback_method_t ) ( LPCSTR invocationContext, callback_method_t, ... );

	typedef void (WINAPI* terminate_method_t ) ( LPCSTR invocationContext, ... );
};

int main( int argc, char* argv[] )
{
	cout << "testRooDll loading roo.dll library" << endl;
 
	HINSTANCE _hInstLib = LoadLibraryA( "roo.dll" );
	if( _hInstLib == NULL )
	{
		cerr << "Program roo.dll was not found." << endl;
		exit( 1 );
	}

	{
		cout << "testRooDll setting callback" << endl;

		set_callback_method_t pMethod = (set_callback_method_t) GetProcAddress( _hInstLib, "SetCallback" ) ;

		if( ! pMethod )
		{
			cerr << "Could not locate 'SetCallback' roo.dll library entry." << endl;
			exit( 1 );
		
		}

		pMethod( "_", PerformCallback ); 
	}

	{
		cout << "testRooDll performing procedure: extroo.rooProgram (with callback: 'another')" << endl;

		entry_point_with_callback_t pMethod = (entry_point_with_callback_t) GetProcAddress( _hInstLib, "PerformWithCallback" ) ;

		if( ! pMethod )
		{
			cerr << "Could not locate 'Perform' roo.dll library entry." << endl;
			exit( 1 );
		}

		GlobalHandleStore globalHandleStore( pMethod( "_", "another", "extroo.rooProgram", "arg1", "arg2", NULL ) ); 

		string sResult = (const char*) globalHandleStore; // any result ?

		cout << "Perform: " << sResult.c_str() << endl;
	}

	{
		cout << "testRooDll executing built-in function: SquareRoot" << endl;

		execute_function_t pMethod = (execute_function_t) GetProcAddress( _hInstLib, "ExecuteFunction" ) ;

		if( ! pMethod )
		{
			cerr << "Could not locate 'ExecuteFunction' roo.dll library entry." << endl;
			exit( 1 );
		}

		GlobalHandleStore globalHandleStore( pMethod( "SquareRoot", "31", NULL ) ); 

		string sResult = (const char*) globalHandleStore; // any result ?

		cout << "ExecuteFunction: " << sResult.c_str() << endl;
	}

	string sInstance;

	{
		cout << "testRooDll creating class: vector" << endl;

		entry_point_with_callback_t pMethod = (entry_point_with_callback_t) GetProcAddress( _hInstLib, "CreateClassWithCallback" ) ;

		if( ! pMethod )
		{
			cerr << "Could not locate 'CreateClass' roo.dll library entry." << endl;
			exit( 1 );
		}

		GlobalHandleStore globalHandleStore( pMethod( "_", "", "vector", "arg1", "arg2", NULL ) ); 

		sInstance = (const char*) globalHandleStore; // any result ?

		cout << "CreateClass: " << sInstance.c_str() << endl;
	}

	{
		cout << "testRooDll performing: vector ~ tostring" << endl;

		entry_point_with_callback_t pMethod = (entry_point_with_callback_t) GetProcAddress( _hInstLib, "InvokeMethodWithCallback" ) ;

		if( ! pMethod )
		{
			cerr << "Could not locate 'InvokeMethod' roo.dll library entry." << endl;
			exit( 1 );
		}

		GlobalHandleStore globalHandleStore( pMethod( "_", "", sInstance.c_str(), "tostring", NULL ) ); 

		string sResult = (const char*) globalHandleStore; // any result ?

		cout << "InvokeMethod: " << sResult.c_str() << endl;
	}

	{
		cout << "testRooDll removing vector instance" << endl;

		remove_instance_t pMethod = (remove_instance_t) GetProcAddress( _hInstLib, "RemoveInstance" ) ;

		if( ! pMethod )
		{
			cerr << "Could not locate 'RemoveInstance' roo.dll library entry." << endl;
			exit( 1 );
		}

		GlobalHandleStore globalHandleStore( pMethod( sInstance.c_str(), NULL ) ); 

		string sResult = (const char*) globalHandleStore; // any result ?

		cout << "RemoveInstance: " << sResult.c_str() << endl;
	}

	return 0;
}
