
/*
  File: include/basic/win32/dllport.h
  Description: Sonique 2 operating-system specific DLL operations
  Part of the Mantis project
  (c) 2002 Lycos Inc.

  This file is distributed for the sole purpose of allowing you to write
  software to interface with Sonique 2 and other Mantis programs. It is
  not to be modified or used for any other purpose without written
  authorization.

  If you redistribute this file, it must be in the original package
  complete with all other files provided, including the usage agreement.

  Do not remove this notice.
*/

#ifndef	_WIN32_DLLPORT_H__
#define _WIN32_DLLPORT_H__

#ifdef DEBUG_INCLUDE_ORDER
#pragma message("Including " __FILE__ "\n" )
#endif

#if _MSC_VER > 1000
#pragma once
#endif


#include "typesport.h"
#include <string.h>
#include "charstring.h"

typedef enum DLLExportType
{
	export_data,
	export_function,
	export_any	
}DLLExportType;

typedef enum DLLLoadType
{
	dll_normal,				// normal
	dll_notify,				// call 'DLLAttach'
	dll_must_notify			// if DLLAttach doesn't exist, fail
}DLLLoadType;

class DLLObject
{
protected:
	HINSTANCE	hDLL;
	DLLLoadType m_dlltype;
	
public:
	DLLObject()
	{
		hDLL = NULL;
	};

	bool operator == (const DLLObject& Other)
	{
		return hDLL == Other.hDLL;
	};

	bool IsLoaded()
	{
		return hDLL != NULL;	
	};

	~DLLObject()
	{
		Unload();
	};
	int32 Load( const char* path, DLLLoadType dlltype = dll_normal, bool bAddDefaultExtension = false )
	{
		ASSERT(path && *path );
		if( path && *path )
		{
			AString szBuffer(path);

            if( bAddDefaultExtension )
				szBuffer.Cat(".dll");

			TRACE1("Attempt to load: %s ...",szBuffer);
			hDLL = LoadLibrary( szBuffer );
			TRACE0( hDLL != NULL ? "successful!\n" : "failed!\n" );

			m_dlltype = dll_normal;
			// do attach?
			if( dlltype == dll_notify || dlltype == dll_must_notify )
			{
				uint32 (*fnDLLAttach)(int32);
				if( GetExport( "DLLAttach", export_function, (void**) &fnDLLAttach ) )
				{
					int result = fnDLLAttach(1); // attaching
					if( !result )
					{
						Unload();
						return -5;
					}
				}
				else
				{
					if( dlltype == dll_must_notify )
					{
						Unload();
						return -4; // DLLAttach not implmented in dll
					}
				}
			}

			// success
			if (hDLL)
			{	
				m_dlltype = dlltype;
				return 0;
			}


			int32 LastError = GetLastError();
			if (LastError == ERROR_FILE_NOT_FOUND)
				return -1;//GetBasicError(ERR_BASIC_FILENOTFOUND);
			else if (LastError == ERROR_NOT_ENOUGH_MEMORY)
				return -2;//GetBasicError(ERR_BASIC_OUTOFMEMORY);
			else
				return -3;//GetBasicError(ERR_BASIC_INVALIDFILETYPE);
		}
		return -1;
	};

	int32 Load( const wchar_t* wcPath, DLLLoadType dlltype = dll_normal, bool bAddDefaultExtension = false )
	{
		AString szBuffer = wcPath;
		return Load( szBuffer, dlltype, bAddDefaultExtension );
	};

	bool Notify()
	{
		if( !IsLoaded() || !(m_dlltype == dll_notify || m_dlltype == dll_must_notify) )
			return false;
		
		uint32 (*fnDLLAttach)(int32);
		if( GetExport( "DLLAttach", export_function, (void**) &fnDLLAttach ) )
		{
			fnDLLAttach(-1); // dettaching
		}
		else if( m_dlltype == dll_must_notify )
		{
			ASSERT(0); // how did this ever get attached?
		}

		m_dlltype = dll_normal;
		return true;
	}
	bool Unload()
	{
		if( hDLL != NULL )
		{	
			// do Dettach?
			Notify();

			if( FreeLibrary( hDLL ) == TRUE )
			{
				hDLL = NULL;
				return true;
			}
		}
		return false;
	};
	bool GetExport( const char* name, DLLExportType /*type*/, void** toHere )
	{
		if( hDLL != NULL )
		{	
			void* temp = (void*)GetProcAddress( hDLL, name );

			if( temp )
			{
				*toHere = temp;
				return true;
			}
		}
		return false;
	};
};

#endif
