//
//	SamplePluginLibrary
//
//	Copyright © Diehl Graphsoft, Inc  2000
//	All Rights Reserved.
//
//
//	This file contains sample code for the new SDK Plug-in Library Routine feature in VectorWorks 9.
//	The routines implemented here can be called from VectorScript, another SDK Plug-in, or the main application.
//	To create a new Library, follow these steps:
//
//		- Copy and rename the files of this sample, or just modify it.
//		- Add a VLIB 0 resource to the rsrc file describing the interface to the new library routine.
//			( If there are more than one routine, be sure all VLIBs are numbered sequentially starting at 0)
//		- Define "PluginLibraryMain()" in the .cpp file as defined in "LibraryMain.cpp"
//		- switch on the routineIndex and call the appropriate function
//		- The function should do the following:
//			- get the arguments out of the argTable
//			- do something with those arguments
//			- set any return values in the argTable
//		- Build the SDK Plug-in Library and place it in the "Plug-ins" folder.
//
//
//	Clients can be verify that a particular routine is installed and can then call that routine.
//
//		VectorScript Client:
//
//			if (VerifyLibraryRoutine('MyNewRoutine') = TRUE) THEN 
//				MyNewRoutine(11, TRUE, 'dude')
//			else
//				SomeOtherRoutine();
//			
//
//		SDK Client:
//	
//			if (GS_VerifyPluginLibrary(theCBP, "\pMyNewRoutine") == TRUE) {

//				PluginLibraryArgTable argTable;
//				argTable.args[0].argType = kIntegerArgType;		// see MiniCadCallBacks.h
//				argTable.args[0].intValue = 111;
//				long status;
//				Boolean wasCalled = GS_CallPluginLibrary(theCBP,  "\pMyNewRoutine", &argTable, status);
//			}
//			else
//				SomeOtherRoutine();
//
//
//	Jeff Koppi, June 2000.
//

#if _WINDOWS
#include <Windows.h>
#include <GSWinSDK.h>	// Graphsoft Required.
#endif

#include "MiniCadCallBacks.h"
#include "GSESetup.h"

#ifndef GS_VCOM_TARGET
#ifndef GS_VCOM_CLIENT_TARGET
#ifndef GS_VCOM_PROVIDER_TARGET
#endif	//#ifndef GS_VCOM_PROVIDER_TARGET
#endif	//#ifndef GS_VCOM_CLIENT_TARGET
#endif	//#ifndef GS_VCOM_TARGET

#include "GSString.h"
//#include "TArray.h"
//DEFINE_NOPOP_TARRAY(short);
//DEFINE_NOPOP_TARRAY(Handle);

//===================================================================================
// Because we include LibraryMain.cpp in the project, the low level setup operations
// are performed on our behalf by SDK code. The CallBackPtr is already stored in gCBP
// and can be used throughout this plug-in's code.
// See MiniCADHookIntf.h for a description

//------------------------------------------------------------------
void AddTwoNumbers(void* argTable, long& userData);


//------------------------------------------------------------------
void AddTwoNumbers(void* argTable, long& userData)
//
//	This plug-in library routine simply adds two numbers together and returns the resulting sum.
//	To test this, you can write a VectorScript which calls this routine and displays the result.
//	Hit the "New" button on the Resource palette, choose "VectorScript", then enter the following script:
//
//		Message('Two plus two equals ', AddTwoNumbers(2,2), '.');
//
//	The script will call this routine and then display the sum in the message window.  From the VectorScript's 
//	point of view the call to AddTwoNumbers() uses the same syntax as calling a built-in routine.
//
{
	if (argTable != NULL) {
		//---------------------------
		// Get the argument values out of the argTable that was passed in.
		PluginLibraryArgTable* theArgTable = static_cast<PluginLibraryArgTable*>(argTable);
		long addOne = theArgTable->args[0].longValue;
		long addTwo = theArgTable->args[1].longValue;
		
		// Just testing the userData feature by incrementing it each time this is called.
		// Typically a plug-in would store a pointer into this userData field, and then use it later.
		userData = userData + 100;

		// Do something with the arguments...
		long theSum = (addOne + addTwo);
		
		// Return the result in the functionResult field
		theArgTable->functionResult.argType = 3;		// ***
		theArgTable->functionResult.longValue = theSum;	

		// Or, return the result in a reference argument (known as VAR argument in PASCAL or VectorScript).
		//theArgTable->args[2].longValue = (addOne + addTwo);
	}
}


//------------------------------------------------------------------
// The following constants are used to branch to the routined specified by the 
// routineSelector argument.  The values of these constants must match the 
// resource ids of the 'VLIB' resources that define each library routine.
// They must begin at 0 and proceed in sequence without any gaps in the numbering.
// These constants must be kept in sync with the 'VLIB' resources.
const short kAddRoutineID			= 0;
const short kFactorialRoutineID		= 1;


#ifndef GS_VCOM_TARGET
#ifndef GS_VCOM_CLIENT_TARGET
#ifndef GS_VCOM_PROVIDER_TARGET
//------------------------------------------------------------------
long PluginLibraryMain(long routineSelector, void* argTable, long &ioUserData)
//
//	This is the main entry point for the library.  
//	The library may contain more than one routine, so branch to the specified routine now.
//
{
	switch (routineSelector) {
			
		case kAddRoutineID:
			AddTwoNumbers(argTable, ioUserData);
			break;

		default:
			break;
	}

	return 0L;
}
#endif	//#ifndef GS_VCOM_PROVIDER_TARGET
#endif	//#ifndef GS_VCOM_CLIENT_TARGET
#endif	//#ifndef GS_VCOM_TARGET



#ifdef GS_VCOM_TARGET
	
	
//------------------------ BEGIN VCOM SOURCE TRANSFER --------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//#include <VCOMInterfaces.h>	// placed in MiniCadCallbacks.h temporarily
#include "ExtendedProperties.h"
#include <map>

const short kRegisterInterfaceRoutineID			= 0;
const short kVCOMGetVCOMInterfaceRoutineID		= 1;


// To DO: Move this to SDK header
typedef long VWIMPID;	//  The host application code reference ID as provided in 
						//	kVCOMRegisterInterfaces below

static bool AllocateInterfaceMap();
static void DeallocateInterfaceMap();
static bool RegisterInterface(const VWUUIDBytes& inVIID, VWIMPID inPIL_ID);
static bool DispatchCreateInstance(const VWUUIDBytes& inVIID, void** ppv);
static HRESULT CreateInstance(const VWUUIDBytes& inVIID, void** ppv);

static CallBackPtr gCBP = nil;


static HRESULT GetVCOMInterface(const VWUUIDBytes& iid, iUnknown*& getAPI);

//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
extern "C" long GS_EXTERNAL_ENTRY main(long callSelector, void* argTable, long &ioUserData, CallBackPtr inCBP)
//
//	This is the main entry point for the library.  
//
{
	ioUserData;
	
	long callResult = 0;
	
	switch (callSelector) {
	
		case kPluginLIBInitGlobals: {
		
			if (AllocateInterfaceMap()) {
			
				callResult = 0;
				callResult |= kAddCodeReference;		// 	We keep persistent stl DATA that can not easily be persisted
														//	using userData and GS_NEWPtr();
				callResult |= kProvidesInterfacePtrs;
				callResult |= kUseSingleUserDataFlag;
			} 
			else
				callResult = kFailedToInitLibFlag;
			break;
		}
		
		case kPluginLIBDisposeGlobals: {
			
			DeallocateInterfaceMap();
			break;
		}
		
		case kVCOMRegisterInterfaces: {
		
			// 	After all Plug-in libraries are intialized the host application calls us with the kPluginLIBRegisterInterfaces selector.
			//	argTablePtr->args[0].longValue contains this PIL's unique code reference ID VWIMPID used to 
			//	Register any of our apis using GS_CallPluginLibrary("VCOMRegisterInterface", uiid, VWIMPID);
			PluginLibraryArgTable* argTablePtr = (PluginLibraryArgTable*) argTable;
			if (argTablePtr) {

				//	ArgTable Structure:
				//	IN: 	argTablePtr->args[0].argType = kLongArgType;  	//	This is the code reference ID, VWIMPID, in the host application.
																			//	Use this ID to register the interface in VCOMRegisterInterface call.

				const TXString kRegisterInterfaceRoutinName("VCOMRegisterInterface");
				
				PluginLibraryArgTable at;
				at.args[0].argType = kVIID;
				at.args[0].viid = kVCOMInterfaceID;
				at.args[1].argType = kLongArgType;
				long myCodeRefID = argTablePtr->args[0].longValue;
				at.args[1].longValue = myCodeRefID;
				
				if (gCBP == nil)
					gCBP = (CallBackPtr) argTablePtr->args[1].longValue;
				
				long status = 0;
				argTablePtr->functionResult.argType = kBooleanArgType;	
				argTablePtr->functionResult.boolValue = GS_CallPluginLibrary(inCBP, kRegisterInterfaceRoutinName, &at,  status);
			}
			break;
		}

		case kRegisterInterfaceRoutineID: {			// API for Runtime modules to register the VIIDs they implement
		
			PluginLibraryArgTable* argTablePtr = (PluginLibraryArgTable*) argTable;
			if (argTablePtr) {
				
				//	ArgTable Structure:
				//	IN: 	argTablePtr->args[0].argType = kVIID;
				//	IN:		argTablePtr->args[1].argType = kLongArgType;	the value is the codeRef ID passed to the VCOM provider's kVCOMRegisterInterfaces

				argTablePtr->functionResult.argType = kBooleanArgType;	
				argTablePtr->functionResult.boolValue = ::RegisterInterface(argTablePtr->args[0].viid, argTablePtr->args[1].longValue);
			}				
			break;
		}

		case kVCOMGetVCOMInterfaceRoutineID: {
		
			PluginLibraryArgTable* argTablePtr = (PluginLibraryArgTable*) argTable;
			if (argTablePtr) {


				//	ArgTable Structure:
				//	IN: 	argTablePtr->args[0].argType = kVIID;
				//	OUT:	argTablePtr->args[1].argType = kppv;	poiter to a pointer to VTable.
				
				argTablePtr->functionResult.argType = kLongArgType;	
				HRESULT result = ::CreateInstance(argTablePtr->args[0].viid, argTablePtr->args[1].ppv);
				argTablePtr->functionResult.longValue = result;
			}				
			break;
		}		

		default:
			break;
	}

	return callResult;
}




//:::::::::::::::::									::::::::::::::::::::::::::::::::::::::::
//					VCOM Provider Registration Data							
//:::::::::::::::::									::::::::::::::::::::::::::::::::::::::::
//
//	We store the a map of <VWUUIDBytes, VWIMPID> so we can call the provider to provide
//	interfaces with subsequent client requests.  This map is filled during the Host startup
//	phase.  Plug-in Libraries are abla to specify themselves as providers with return flag  
//	kPluginLIBInitGlobals.  With this flag set, the PIL is called with kVCOMRegisterInterfaces
//	selector.  From this call using the VWIMPID provided, the provider then calls
//	VCOMRegisterInterface to specify the VWUUIDBytes for the interface(s) it provides.
//	
//	When clients request a VWUUIDBytes, VCOM calls the host to dispatch the interface 
//	request to the PIL provider.  The provider is called with a kVCOMGetInterface
//	message and returns the interface pointer in this call.

class VIIDComparer : std::binary_function<VWUUIDBytes, VWUUIDBytes, bool> 
{
public:
	bool operator()(const VWUUIDBytes& id1, const VWUUIDBytes& id2) const;
};

bool VIIDComparer::operator()(const VWUUIDBytes& a, const VWUUIDBytes& b) const
{
	return memcmp(&a, &b, 16) < 0;
}

typedef std::map< VWUUIDBytes, VWIMPID, VIIDComparer > InterfaceMap;


static InterfaceMap* gInterfaceMap = nil;

static bool AllocateInterfaceMap()
{
	try {
		gInterfaceMap = new InterfaceMap;
		return gInterfaceMap != nil;
	}
	catch (std::bad_alloc) {
	
		return false;
	}
}

static InterfaceMap* GetInterfaceMap()
{
	return gInterfaceMap;
}

static void DeallocateInterfaceMap()
{
	delete gInterfaceMap;
	gInterfaceMap = nil;
}
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::




//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
static bool RegisterInterface(const VWUUIDBytes& inVIID, VWIMPID inPIL_ID)
{
	bool retVal = false;
	
	InterfaceMap* mapPtr = GetInterfaceMap();
	
	if (mapPtr) {
	
		try {
			
			std::pair<InterfaceMap::iterator, bool> result(mapPtr->insert(InterfaceMap::value_type(inVIID, inPIL_ID)));
			
			InterfaceMap::const_iterator mapEntry = mapPtr->find(inVIID);
			
			VWUUIDBytes id = (*mapEntry).first;
			VWIMPID hostID = (*mapEntry).second;
			
			retVal = mapEntry != mapPtr->end();
		}
		catch (std::bad_alloc) {
		
			retVal = false;
		}
	}

	return retVal;
}


//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
static bool DispatchCreateInterface_(const VWUUIDBytes& inVIID, VWIMPID inFactoryID, void** ppv)
{
	PluginLibraryArgTable argTable;
	argTable.args[0].argType = kVIID;
	argTable.args[0].viid = inVIID;
	argTable.args[1].argType = kLongArgType;
	argTable.args[1].longValue = inFactoryID;
	argTable.args[2].argType = kppv;
	argTable.args[2].ppv = ppv;
	
	EPE_Kludge in(EPE_Kludge::kKludgeSubSelector17, &argTable);

	bool retVal = gCBP != nil && GS_Kludge(gCBP, EPE_Kludge::kKludgeSelector, &in, nil);
	
	return retVal;
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
static bool DispatchCreateInstance(const VWUUIDBytes& inVIID, void** ppv)
{
	bool retVal = false;
	
	InterfaceMap* mapPtr = GetInterfaceMap();
	
	if (mapPtr) {
	
		InterfaceMap::const_iterator mapEntry = mapPtr->find(inVIID);
		
		if (mapEntry != mapPtr->end()) {
					
			retVal = DispatchCreateInterface_(inVIID, (*mapEntry).second, ppv);
		}
	}
	return retVal;
}


//-------------------------------------------------------------------
class VCOMInterfaceImp : public iUnknown 
{
public:
    virtual HRESULT STDMETHODECALLTYPE QueryInterface(const VWUUIDBytes& iid, void **ppv);
    virtual Uint32  STDMETHODECALLTYPE AddRef();
    virtual Uint32  STDMETHODECALLTYPE Release();
private:
	Uint32 fRefCount;
};

//-------------------------------------------------------------------
HRESULT STDMETHODECALLTYPE VCOMInterfaceImp::QueryInterface(const VWUUIDBytes& inVIID, void **ppv)
{
	HRESULT success = E_FAIL;
	
	if (DispatchCreateInstance(inVIID,  ppv))
		success = S_OK;
	return success;
}

//-------------------------------------------------------------------
Uint32 STDMETHODECALLTYPE VCOMInterfaceImp::AddRef()
{
	return ++fRefCount;
}

//-------------------------------------------------------------------
Uint32 STDMETHODECALLTYPE VCOMInterfaceImp::Release()
{
	return --fRefCount;
}




//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
static HRESULT CreateInstance(const VWUUIDBytes& inVIID, void** ppv)
{
	static VCOMInterfaceImp* gVCOMInterface = nil;

	HRESULT result = E_NOTIMPL;
	
	if (inVIID == kVCOMInterfaceID) 
	{	
		if (gVCOMInterface) {
		
			(*ppv) = gVCOMInterface;
			result = S_OK;
		}
		else {
			try {
				gVCOMInterface = new VCOMInterfaceImp; 
				(*ppv) = gVCOMInterface;
				result = S_OK;
			}
			catch (std::bad_alloc) {
				
				result = E_OUTOFMEMORY;
			}
		}
	}
	return result;
}




//	Provide this in SDK as a convenience:
static HRESULT GetVCOMInterface(const VWUUIDBytes& iid, iUnknown*& getAPI)
{
	HRESULT result = E_FAIL;
	
	const TXString kLibRoutineName("VCOMGetVCOMInterface");
	
	if (GS_VerifyPluginLibrary(gCBP, kLibRoutineName)) {
	
		PluginLibraryArgTable getApi;
		getApi.args[0].argType = kVIID;
		getApi.args[0].viid = iid;
		
		getApi.args[1].argType = kppv;	
		getApi.args[1].ppv = (void**)&getAPI;
		
		
		getApi.functionResult.argType = kLongArgType;	
		long status = 0;
		if (GS_CallPluginLibrary(gCBP, kLibRoutineName, &getApi, status)) {
			result = S_OK;
		}
	}
	return result;
}	

#endif
//-------------------------- END VCOM SOURCE TRANSFER --------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------











#ifdef GS_VCOM_CLIENT_TARGET
//-------------------------- BEGIN VCOM CLIENT SOURCE TRANSFER -----
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//	So VectorScript can call a test routine
const short kTestClientInterfaceRoutineID				= 0;


//------------------------------------------------------------------
//------------------------------------------------------------------
class ClientData
//
//	Here we use the system provided "UserData" to store persistent data.
//	In our case we 
{
public:
	static void Allocate(long& userData, CallBackPtr inCBP);
	static void Deallocate(long& userData, CallBackPtr inCBP);
	static short GetRsrcFileID() { return gMyResFileID; }
	
	ClientData() : fQuotientAPI(nil), fTestVS(0) {}
	
	QuotientInterface* GetQuotientInterface(CallBackPtr inCBP);
	RunVectorScriptInterface* GetRunVectorScriptInterface(CallBackPtr inCBP);
	RunVectorScriptInterface::CompiledScriptRef TestVS() { return fTestVS; }
	void SetTestVS(RunVectorScriptInterface::CompiledScriptRef setVal) { fTestVS = setVal; }
	
private:
	QuotientInterface* fQuotientAPI;
	static short gMyResFileID;	
	RunVectorScriptInterface::CompiledScriptRef fTestVS;
};

short ClientData::gMyResFileID =-1;

//------------------------------------------------------------------
void ClientData::Allocate(long& userData, CallBackPtr inCBP)
{
	void* pRawMemory = GS_NewPtr(inCBP, sizeof(ClientData));
	if (pRawMemory) {
		ClientData* pConstructedMemory = (ClientData*) new(pRawMemory) ClientData();	
		userData = (long) pConstructedMemory;
	}
	else 
		userData = nil;
		
	gMyResFileID = GS_GetCurrentResFile(inCBP);
}

//------------------------------------------------------------------
void ClientData::Deallocate(long& userData, CallBackPtr inCBP)
{
	if (userData) {
	
		ClientData* cd = (ClientData*) userData;
		
		if (cd->fQuotientAPI) {
		
			cd->fQuotientAPI->Release();
			cd->fQuotientAPI = nil;
			
			if(cd->TestVS() != 0) {
				RunVectorScriptInterface* testAPI = cd->GetRunVectorScriptInterface(inCBP);
				if (testAPI) {
					testAPI->AddRef();
					testAPI->ReleaseScriptRef(cd->TestVS());
					cd->SetTestVS(0);
					testAPI->Release();
				}
			}
		}
		
		GS_DisposePtr(inCBP, (void*)userData);
		userData = nil;
	}
}

//------------------------------------------------------------------
QuotientInterface* ClientData::GetQuotientInterface(CallBackPtr inCBP)
{
	if (fQuotientAPI == nil) {
	
		VCOMInterface* api = nil;
		if (SUCCEEDED(GetVCOMLibraryInterface(kVCOMInterfaceID, api, inCBP))) {
			
			iUnknown* iunknown2 = nil;
			
			api->AddRef();
			if (SUCCEEDED(api->QueryInterface(kQuotientInterfaceID, (void**) &iunknown2))) {
			
				if (iunknown2) {
				
					fQuotientAPI = (QuotientInterface*) iunknown2;
					fQuotientAPI->AddRef();
				}
			}
			api->Release();
		}
	}
	return fQuotientAPI;
};

//------------------------------------------------------------------
RunVectorScriptInterface* ClientData::GetRunVectorScriptInterface(CallBackPtr inCBP)
{
	RunVectorScriptInterface* rvsAPI = nil;
	
	VCOMInterface* api = nil;
	if (SUCCEEDED(GetVCOMLibraryInterface(kVCOMInterfaceID, api, inCBP))) {
		
		iUnknown* iunknown2 = nil;
		
		api->AddRef();
		if (SUCCEEDED(api->QueryInterface(kRunVectorScriptInterfaceID, (void**) &iunknown2))) {
		
			if (iunknown2) {
			
				rvsAPI = (RunVectorScriptInterface*) iunknown2;
			}
		}
		api->Release();
	}
	return rvsAPI;
};

//------------------------------------------------------------------
extern "C" long GS_EXTERNAL_ENTRY main(long callSelector, void* argTable, long &ioUserData, CallBackPtr inCBP)

{
	GSExternalSetup(inCBP);

	long	callResult = 0;

	switch (callSelector) {

		//-------------------------------------------
		//-------------------------------------------
		case kPluginLIBInitGlobals:

			callResult = 0;
			//callResult |= kDoNotOpenRsrcFileWithCallFlag;
			
			callResult |= kAddCodeReference;		// 	We keep persistent stl DATA that can not easily be persisted
													//	using userData and GS_NEWPtr();
			callResult |= kProvidesInterfacePtrs;
			callResult |= kUseSingleUserDataFlag;


			break;



		//-------------------------------------------
		//-------------------------------------------
		case kPluginLIBDisposeGlobals:
		
			ClientData::Deallocate(ioUserData, inCBP);
			
			callResult |= kReleaseCodeReference;
			break;


		//-------------------------------------------
		//-------------------------------------------
		case kTestClientInterfaceRoutineID: {				
		
			PluginLibraryArgTable* argTablePtr = (PluginLibraryArgTable*) argTable;
			if (argTablePtr) {
			
				if (ioUserData == 0)	// Lazy Allocate our data
					ClientData::Allocate(ioUserData, inCBP);
					
				if (ioUserData != 0) {
					
					ClientData* cd = (ClientData*) ioUserData;
					double a = 2;
					//	Get the QuotientInterface:
					QuotientInterface* quotientAPI = cd->GetQuotientInterface(inCBP);
					
					if (quotientAPI) {
					
						//quotientAPI->AddRef();	// we manage refcounts inside ClientData

						a = quotientAPI->Quotient(1, 3);
						a = quotientAPI->Quotient(((double)1/3), a);
						a = 2;
						
						//quotientAPI->Release();	// we manage refcounts inside ClientData
					}
					
					RunVectorScriptInterface* testAPI = cd->GetRunVectorScriptInterface(inCBP);
					if (testAPI) {
					
						testAPI->AddRef();
						short saveResFIleID = GS_GetCurrentResFile(inCBP);
						GS_UseResFile(inCBP, ClientData::GetRsrcFileID());
						
						if (cd->TestVS() == 0) {
							RunVectorScriptInterface::CompiledScriptRef getRef = 0;
							testAPI->Compile(128, getRef);
							cd->SetTestVS(getRef);
						}
							
						if (cd->TestVS() != 0)
							testAPI->Run(cd->TestVS());
							
						GS_UseResFile(inCBP, saveResFIleID);
						testAPI->Release();
					}
						
				}
			}
			break;
		}		
		default:

			break;

	}
	GSExternalSetdown(0L);
	
	return callResult;

}

#endif	//#ifdef GS_VCOM_CLIENT_TARGET
//-------------------------- END VCOM CLIENT SOURCE TRANSFER -------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------






#ifdef GS_VCOM_PROVIDER_TARGET
//-------------------------- BEGIN VCOM PROVIDER SOURCE TRANSFER ---
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
static HRESULT CreateInstance(const VWUUIDBytes& inVIID, void** ppv);

//------------------------------------------------------------------
//------------------------------------------------------------------
class ProviderData
//
//	Here we use the system provided "UserData" to store persistent data.
//	In our case we 
{
public:
	static void Allocate(CallBackPtr cbp, long inCodeRefID, long& ioUserData, CallBackPtr inCBP);
	static void Deallocate(long& userData, CallBackPtr inCBP);
	
	ProviderData() : 
		//fCBP(nil),
		fCodeRefID(0)
	{}
	
	void RegisterInterfaces(PluginLibraryArgTable& ioArgTable, CallBackPtr inCBP);
	
	//CallBackPtr CBP() 	{ return fCBP; }
	long CodeRefID() 	{ return fCodeRefID; }
	
private:


	//CallBackPtr fCBP;
	long		fCodeRefID;
};

//------------------------------------------------------------------
void ProviderData::Allocate(CallBackPtr cbp, long inCodeRefID, long& ioUserData, CallBackPtr inCBP)
//
//	static
{
	if (inCodeRefID != 0 && cbp != nil) {
	
		void* pRawMemory = GS_NewPtr(inCBP, sizeof(ProviderData));
		if (pRawMemory) {
			ProviderData* pConstructedMemory = (ProviderData*) new(pRawMemory) ProviderData();	
			if (pConstructedMemory) {
				//pConstructedMemory->fCBP = cbp;
				pConstructedMemory->fCodeRefID = inCodeRefID;
			}
			ioUserData = (long) pConstructedMemory;
		}
		else 
			ioUserData = 0;
	}
	else 
		ioUserData = 0;
}

//------------------------------------------------------------------
void ProviderData::Deallocate(long& userData, CallBackPtr inCBP)
//
//	static
{
	if (userData) {
		
		GS_DisposePtr(inCBP, (void*)userData);
		userData = nil;
	}
}


void ProviderData::RegisterInterfaces(PluginLibraryArgTable& ioArgTable, CallBackPtr inCBP)
{
	if (fCodeRefID != 0) {
	
		const TXString kRegisterInterfaceRoutinName("VCOMRegisterInterface");
		
		//	Register the kQuotientInterfaceID interface:
		PluginLibraryArgTable at;
		at.args[0].argType = kVIID;
		at.args[0].viid = kQuotientInterfaceID;
		at.args[1].argType = kLongArgType;
		at.args[1].longValue = fCodeRefID;
		
		#if TEST
		if (fCodeRefID != ioArgTable.args[0].longValue)
			DSTOP((kSteve, "We expect that the codeRefID was set in ALLOCATE\n"));
		
		//if (fCBP != CallBackPtr(ioArgTable.args[1].longValue))
		//	DSTOP((kSteve, "We expect that the fCBP was set in ALLOCATE\n"));
		
		#endif
		
		long status = 0;
		ioArgTable.functionResult.argType = kLongArgType;	
		if (GS_CallPluginLibrary(inCBP, kRegisterInterfaceRoutinName, &at,  status))
			ioArgTable.functionResult.longValue = status;
		else
			ioArgTable.functionResult.longValue = S_FALSE;
	}
}

static CallBackPtr gCBP = nil;

//	VCOM PROVIDER

//------------------------------------------------------------------
long PluginLibraryMain(long callSelector, void* argTable, long & ioUserData)
{
	long	callResult = 0;
	switch (callSelector) {

		case kPluginLIBInitGlobals: {
		
			callResult = 0;
			callResult |= kAddCodeReference;		// 	We keep persistent stl DATA that can not easily be persisted
													//	using userData and GS_NEWPtr();
			callResult |= kProvidesInterfacePtrs;
			break;
		}

		case kPluginLIBDisposeGlobals: {
		
			ProviderData::Deallocate(ioUserData, inCBP);
			gCBP = nil;
			break;
		}

		case kVCOMRegisterInterfaces: {
		
			// 	After all Plug-in libraries are intialized the host application calls us with the kPluginLIBRegisterInterfaces selector.
			//	argTablePtr->args[0].longValue contains this PIL's unique code reference ID VWIMPID used to 
			//	Register any of our apis using GS_CallPluginLibrary("VCOMRegisterInterface", uiid, VWIMPID);
			PluginLibraryArgTable* argTablePtr = (PluginLibraryArgTable*) argTable;
			
			if (argTablePtr != nil) {
			
				if (ioUserData == 0) {
				
					 ProviderData::Allocate((CallBackPtr)argTablePtr->args[1].longValue, 
					 						argTablePtr->args[0].longValue, 
					 						ioUserData,
					 						inCBP);
				}
				if (ioUserData != 0) {
				
					ProviderData* pd = (ProviderData*) ioUserData;
					pd->RegisterInterfaces(*argTablePtr, inCBP);
					
					if (gCBP == nil)
						gCBP = (CallBackPtr) argTablePtr->args[1].longValue;
				}
			}
			break;
		}
		
		case kVCOMGetInterface: { 


			//	Whenever a client requests one of the interfaces we registered in kRegisterInterfaceRoutineID, the interface provider
			//	is called here.
			//
			//	We registered as a kQuotientInterfaceID provider so here we instantiate an interface
			//
			PluginLibraryArgTable* giat = (PluginLibraryArgTable*) argTable;
			if (giat) {
				
				//	ArgTable Structure:
				//	IN: 	giat->args[0].argType = kVIID;			//	THe interface ID
				//	IN: 	giat->args[1].argType = kLongArgType;	// 	this is our VWIMPID as provided in our kVCOMRegisterInterfaces call above.
				//	OUT:	giat->args[2].a rgType = kppv;			//	return the interface here.

				giat->functionResult.argType = kLongArgType;	
				HRESULT retVal = ::CreateInstance(giat->args[0].viid, giat->args[2].ppv);
				giat->functionResult.longValue = retVal;
				
			}
			break;
		}
		default:

			break;

	}
	return callResult;
}



//-------------------------------------------------------------------
class QuotientInterfaceImp : public QuotientInterface 
{
public:
    virtual HRESULT	STDMETHODECALLTYPE QueryInterface(const VWUUIDBytes& iid, void **ppv)
    { 
    	return CallVCOMQueryInterface(iid, ppv, gCBP); 
    } 
    virtual Uint32  STDMETHODECALLTYPE AddRef()
    { 
    	return ++fRefCount;
    }
    
    virtual Uint32  STDMETHODECALLTYPE Release()
    { 	
    	if (fRefCount < 2) 
    		STOP(); 
    	return --fRefCount; 
    } 
    virtual double 	STDMETHODECALLTYPE Quotient(const double& numerator, const double& denominator);

private:
 	Sint32 fRefCount; 
};


double STDMETHODECALLTYPE QuotientInterfaceImp::Quotient(const double& numerator, const double& denominator)
{
	return denominator != 0 ? numerator/denominator : 0;
}



static QuotientInterfaceImp* gQuotientInterfaceImp = nil;

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
static HRESULT CreateInstance(const VWUUIDBytes& inVIID, void** ppv)
{
	bool retVal = false;
	
	if (inVIID == kQuotientInterfaceID) 
	{	
		if (gQuotientInterfaceImp)
			(*ppv) = gQuotientInterfaceImp;
		else {
			gQuotientInterfaceImp = new QuotientInterfaceImp; 
			(*ppv) = gQuotientInterfaceImp;
		}
		retVal = true;
	}
	return retVal;
}

#endif //#ifdef GS_VCOM_PROVIDER_TARGET
//-------------------------- END VCOM PROVIDER SOURCE TRANSFER -----
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------

