
/*
  File: include/mia/mia.h
  Description: Main MIA object system file.
  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 _MIA_H_
#define _MIA_H_

#include "mgeneric.h"
#include "charstring.h"
#include "dllport.h"
#include "filesys.h"
#include "MID.h"
#include "MIAInternals.h"
#include "MIAUtil.h"
#include "Factory.h"
#include "libport.h"
#include "mobject.h"
#include "Interface_ErrorSys.h"

//-----------------------------------------------
// enviornment variables that affect Mia


// where the dll/shared object/analogs are registered
#define MIA_LIBRARY_PATH	"MIA_LIBRARY_PATH"
#define MIA_LIBRARY_PATHW	L"MIA_LIBRARY_PATH"
// where the mia.dll is located.
#define MIA_SHARED_PATH		"MIA_SHARED_PATH"
#define MIA_SHARED_PATHW	L"MIA_SHARED_PATH"


//------------------------------------------------


// {F316B898-717E-451e-8518-79714110B00F}
DEFINE_MID( MID_MIA_CONTROL,		0xf316b898, 0x717e, 0x451e, 0x85, 0x18, 0x79, 0x71, 0x41, 0x10, 0xb0, 0x0f);

// {4CC2B5B9-7B90-4599-ACAB-608F3AC67A99}
DEFINE_MID( MID_IFACTORY,			0x4cc2b5b9, 0x7b90, 0x4599, 0xac, 0xab, 0x60, 0x8f, 0x3a, 0xc6, 0x7a, 0x99);
 
// {45AC0C05-34E2-4f52-90DF-2DB2C28608FA}
DEFINE_MID( MID_IFACTORY_SERVER,	0x45ac0c05, 0x34e2, 0x4f52, 0x90, 0xdf, 0x2d, 0xb2, 0xc2, 0x86, 0x8,  0xfa);

// {50145CDE-CEA8-4041-B07E-337FAF511BFC}
DEFINE_MID( FID_MIA,				0x50145cde, 0xcea8, 0x4041, 0xb0, 0x7e, 0x33, 0x7f, 0xaf, 0x51, 0x1b, 0xfc);

// {69C40EFD-1EBF-477d-8E13-C6BEF8B406F7}
DEFINE_MID( MID_ERROR_SYSTEM,		0x69c40efd, 0x1ebf, 0x477d, 0x8e, 0x13, 0xc6, 0xbe, 0xf8, 0xb4, 0x06, 0xf7);

	

#if defined(_DEBUG) 
//&& defined(_DEBUG_REFCOUNT)
// {A78E064D-2DC7-4e1f-A149-E1220AE27B8E}
DEFINE_MID( MID_REFCOUNT_DEBUG,		0xa78e064d, 0x2dc7, 0x4e1f, 0xa1, 0x49, 0xe1, 0x22, 0x0a, 0xe2, 0x7b, 0x8e);
#endif

enum ERegisterMode
{
	use_current_MIA_state,
	new_from_default,
	new_ignore_environment,
//	new_from_path
};

class MMiaControl : public MGeneric // MID_MIA_CONTROL
{
public:
	virtual MRESULT __stdcall	RegisterDLL( bool bRegister, const wchar_t* wcPath, ERegisterMode How = use_current_MIA_state, const wchar_t* wcMiaLibDir = NULL )														= 0;
	virtual MRESULT	__stdcall	RegisterIFactory( bool bRegister, const MID& mFactoryID, const wchar_t* wcPath, const char* acName )										= 0;
	virtual MRESULT __stdcall	RegisterGenericFactory( bool bRegister, const MID& mGenericFactoryID, const MID& mImplementationFactoryID, const char* acName )				= 0;
	
	virtual void	__stdcall	SetPersistentState(bool bPersistence)																										= 0;
	virtual bool	__stdcall	GetPersistentState()																														= 0;
	virtual int32	__stdcall	AddLoadCount()																																= 0;
	virtual void	__stdcall	Done()																																		= 0;

    // this only can have effect before the first call to AddLoadCount();
    virtual void    __stdcall   SetPrivatePath( const wchar_t* wcPrivatePath )                                                                                        = 0;
};

// This Function is exported

/*
#if	  !defined( _MIA_DLL_ ) && !defined( _USE_MIA_ )

extern MRESULT (*MCreateInstance)(	const MID& mIFactory, 
									const MID& mInterface, 
									void**	   ppToHere,
									MObject*   pOuter );

extern WString MGetSharedPath();
extern WString MGetDLLPath();

#elif defined( _USE_MIA_ )
MRESULT (*MCreateInstance)(	
							const MID& mIFactory,
						    const MID& mInterface, 
							void**	   ppToHere,
							MObject*   pOuter );

*/
#if defined( _USE_MIA_ ) || defined( _MIA_DLL_ )

#if defined(_WIN32)
#define MIA_DLL_NAME L"mia.dll"
#elif defined(_LINUX)
#define MIA_DLL_NAME L"mia.so"
#endif

WString MIAGetDLLPath( const wchar_t* wcPrivate = NULL)
{
	// jmc110802 unicode support
	AString asPath;
	WString wsPath;
	bool bSuccess = false;
		

	// get from Enviornment FIRST -- else try registry
	if( !GetEnvironmentVariable( MIA_SHARED_PATHW, wsPath ) )
	{
        if( wcPrivate )
        {
            WString wsFullPath = filesys::MakePath( (const wchar_t*) wcPrivate, MIA_DLL_NAME );
            if( filesys::FileExists( wsFullPath ) )
                return  wsFullPath;
        }
#if defined(_WIN32)

		//char szBuffer[32768];
		//szBuffer[0]=0;
		HKEY hKey;
		uint32 len;
		if( ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\MIA", 0, KEY_READ, &hKey ) )
		{
			//int32 size = sizeof(szBuffer);
			//RegQueryValueEx( hKey, "sharedpath", NULL, NULL, (unsigned char*)szBuffer, (unsigned long*)&size );
			if (IsUnicodeFileSystem())
			{
				if( RegQueryValueExW(hKey, L"sharedpath", NULL, NULL, (BYTE*)NULL, &len) == ERROR_SUCCESS )
				{
					wsPath.Allocate(len-1);
					RegQueryValueExW( hKey, L"sharedpath", NULL, NULL, (BYTE*)wsPath.GetAt(), &len );
				}
			}
			else
			{
				if( RegQueryValueExA(hKey, "sharedpath", NULL, NULL, (BYTE*)NULL, &len) == ERROR_SUCCESS )
				{
					asPath.Allocate(len-1);
					RegQueryValueExA( hKey, "sharedpath", NULL, NULL, (BYTE*)asPath.GetAt(), &len );
					wsPath = asPath;
				}
			}
			RegCloseKey( hKey );
		}
		else if( ERROR_SUCCESS == RegOpenKeyEx( HKEY_CLASSES_ROOT, "MIA", 0, KEY_READ, &hKey ) )
		{
			//int32 size = sizeof(szBuffer);
			//RegQueryValueEx( hKey, "sharedpath", NULL, NULL, (unsigned char*)szBuffer, (unsigned long*)&size );
			if (IsUnicodeFileSystem())
			{
				if( RegQueryValueExW(hKey, L"sharedpath", NULL, NULL, (BYTE*)NULL, &len) == ERROR_SUCCESS )
				{
					wsPath.Allocate(len-1);
					RegQueryValueExW( hKey, L"sharedpath", NULL, NULL, (BYTE*)wsPath.GetAt(), &len );
				}
			}
			else
			{
				if( RegQueryValueExA(hKey, "sharedpath", NULL, NULL, (BYTE*)NULL, &len) == ERROR_SUCCESS )
				{
					asPath.Allocate(len-1);
					RegQueryValueExA( hKey, "sharedpath", NULL, NULL, (BYTE*)asPath.GetAt(), &len );
					wsPath = asPath;
				}
			}
			RegCloseKey( hKey );
		}
		//asPath = szBuffer;

#elif defined(_LINUX)
		FILE* etcfile;
		char* Buffer = (char*)NULL;
		long BufferSize = 0;
		int result;
		wsPath = L"./mia.so";

		etcfile = fopen("/etc/mia.conf", "rt");
		if( etcfile )
		{
		//	AParser parse;
			Array<WString> strs;
			while(!feof(etcfile))
			{
				if( (result = filesys::GetNextLine(etcfile, Buffer, BufferSize)) )
				{
					WParser::ParseStrings(Buffer, result, WParser::ConfigParse(), strs);
					if( strs.GetCount() == 2 && !strs[0].CompareTo( "sharedpath" ) )
					{
						wsPath =  strs[1];
					}
				}
			}

			if( Buffer )
				free(Buffer);
		}
		
#else
#error("MIAGetDLLPath() not defined for this platform")
#endif
	}
	return wsPath;
}

WString MIAGetSharedPath()
{
	WString DLLPath = MIAGetDLLPath();
	return filesys::GetDirName((const wchar_t*)DLLPath);
}

#endif
#if defined( _USE_MIA_ ) 

DLLObject* g_pdllMia = (DLLObject*)NULL;

#if defined(_DEBUG) && defined(_DEBUG_REFCOUNT)
MRefCountDebug* RefCountDebugPtr = (MRefCountDebug*)NULL;
#endif

bool g_MIAWasLoaded;

typedef enum
{
    miainit_default,
    miainit_private,
    miainit_global
} EMiaInitMode;

// directory where mialib.conf is, if a 'private' version is desired, NULL for default shared 
// directory where mia.dll is, if a 'private' version is desired, NULL for default shared 

bool InitMIA( const wchar_t* wcPrivateConfPath = NULL, const wchar_t* wcMiaPath = NULL )
{
	WString wsMiaPath;

	wsMiaPath = MIAGetDLLPath(wcMiaPath);

	if( !wsMiaPath.IsNonWhiteSpace() )
		wsMiaPath = "mia"; // try current directory if shared path is unknown
/*	else
	{
		// append path
		wsMiaPath.Cat( DIR_DIVIDER_STRING "mia.");
	}*/

	g_pdllMia = new DLLObject;
	if( g_pdllMia->Load( wsMiaPath, dll_notify ) == M_OK )
	{
		if( g_pdllMia->GetExport( "MCreateInstance", export_function, (void**) &MCreateInstance ) )
		{
			wsMiaPath = filesys::GetAbsolutePath<wchar_t>( L".", wsMiaPath );
			if (IsUnicodeFileSystem())
				SetEnvironmentVariableW( MIA_SHARED_PATHW, wsMiaPath );
			else
				SetEnvironmentVariableA( MIA_SHARED_PATH, AString(wsMiaPath) );


#if defined(_DEBUG) && defined(_DEBUG_REFCOUNT)
#ifdef _WIN32
			VERIFY( MCreateInstance(FID_MIA, MID_REFCOUNT_DEBUG, (void**)&RefCountDebugPtr, NULL) == M_OK );
#endif
#endif

			MMiaControl* MIAControl;

			VERIFY( MCreateInstance(FID_MIA, MID_MIA_CONTROL, (void**)&MIAControl, NULL) == M_OK );

            if( wcPrivateConfPath )
                MIAControl->SetPrivatePath( wcPrivateConfPath );

            g_MIAWasLoaded = MIAControl->AddLoadCount() == 1;
			MIAControl->Release();

			return true;
		}

		g_pdllMia->Unload();
		delete g_pdllMia;
		g_pdllMia = (DLLObject*)NULL;
	}

	return false;
}	

bool DeInitMIA()
{
	if( MCreateInstance )
	{
		if( g_MIAWasLoaded )
		{
			MMiaControl* MIAControl;

			VERIFY( MCreateInstance( FID_MIA, MID_MIA_CONTROL, (void**)&MIAControl, NULL) == M_OK );
			MIAControl->Done();
			VERIFY(MIAControl->Release()==1);// if this fails, then Some DLL or something isn't calling DeInitMIA()!!!"
		}

		MCreateInstance = NULL;
		g_pdllMia->Unload();
		delete g_pdllMia;
		g_pdllMia = (DLLObject*)NULL;
		return true;
	}

	return false;
}
#endif


class MControllingApp : public MGeneric 
{
public:
	virtual MRESULT __stdcall AppStart  ( const Array<WString>& wsaCommands, void* pLocation, char* szLocDesc ) = 0; // szLocDesc tells what pLocation is, example "Win32_HWND", or "Scarab_MItem" etc.
	virtual MRESULT __stdcall AppRun	() = 0; // blocks till application is  finished 
	virtual MRESULT __stdcall AppEnd	() = 0;
	
};

class MErrorSystem;
typedef HeapMidPtr< MErrorSystem,		&FID_MIA,	&MID_ERROR_SYSTEM, true	> ErrorSystemPtr;


#endif//_MIA_H_
