/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: CHXClientEngineMac.cpp,v 1.3.6.3 2004/07/09 01:49:35 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "CHXClientEngineMac.h"
#include "CHXClientDebug.h"
#include "HXClientCFuncs.h"

#include "enter_hx_headers.h"
#include "hxwintyp.h"
#include "hxcore.h"

#include "hxsmartptr.h"
HX_SMART_POINTER_INLINE( SPIHXClientEngine, IHXClientEngine );
#include "exit_hx_headers.h"

CHXClientEngine*
CreatePlatformClientEngine( void )
{
	return new CHXClientEngineMac;
}

bool
ClientEngineHandleClassicEvent( EventRecord* classicEvent )
{
	SPIHXClientEngine spIClientEngine;
	if ( CHXClientEngine::GetEngine( spIClientEngine.AsInOutParam() ) )
	{
		HXxEvent hxEvent;
		memset( &hxEvent, 0, sizeof( HXxEvent ) );
		hxEvent.event = classicEvent->what;
		hxEvent.param1 = ( void* ) classicEvent;
		
		spIClientEngine->EventOccurred( &hxEvent );

		return hxEvent.handled ? true : false;
	}
	return false;
}

typedef HX_RESULT ( HXEXPORT_PTR FPRMSETDLLACCESSPATH ) ( const char* );

CHXClientEngineMac::~CHXClientEngineMac( void )
{
	UnloadClientCore();
}

CHXClientEngineMac::CHXClientEngineMac( void )
	: m_CoreBundle( nil )
	, m_pICoreEngine( NULL )
{
}

CFURLRef
CHXClientEngineMac::CopyHelixDirectoryURL( void )
{
	CFURLRef helixDirectoryURL = nil;
	
	CFBundleRef hxClientBundleRef = CFBundleGetBundleWithIdentifier( CFSTR( "org.HelixCommunity.HXClientKit" ) );
	if ( hxClientBundleRef )
	{
		CFURLRef hxClientBundleURL = CFBundleCopyBundleURL( hxClientBundleRef );
		if ( hxClientBundleURL )
		{
			helixDirectoryURL = CFURLCreateCopyAppendingPathComponent( kCFAllocatorDefault, hxClientBundleURL, CFSTR( "HelixPlugins" ), true );
			CFRelease( hxClientBundleURL );
		}
	}
	return helixDirectoryURL;
}

HX_RESULT
CHXClientEngineMac::LoadClientCore( void )
{
	if ( !m_CoreBundle )
	{
		CFURLRef helixDirectoryURL = CopyHelixDirectoryURL();
		if ( helixDirectoryURL )
		{
			CFURLRef helixCommonURL = CFURLCreateCopyAppendingPathComponent( kCFAllocatorDefault, helixDirectoryURL, CFSTR( "Common" ), true );
			if ( helixCommonURL )
			{
				CFURLRef coreBundleURL = CFURLCreateCopyAppendingPathComponent( kCFAllocatorDefault, helixCommonURL, CFSTR( "clntcore.bundle" ), true );
				if ( coreBundleURL )
				{
					m_CoreBundle = CFBundleCreate( kCFAllocatorDefault, coreBundleURL );
					if ( m_CoreBundle )
					{
						Boolean isLoaded = CFBundleLoadExecutable( m_CoreBundle );
						if ( !isLoaded )
						{
							CFRelease( m_CoreBundle );
							m_CoreBundle = nil;
						}
					}
					CFRelease( coreBundleURL );
				}
				CFRelease( helixCommonURL );
			}
			CFRelease( helixDirectoryURL );
		}
	}
	return ( nil != m_CoreBundle ) ? HXR_OK : HXR_MISSING_COMPONENTS;
}

HX_RESULT
CHXClientEngineMac::InitDLLAccessPaths( void )
{
	HX_RESULT outResult = LoadClientCore();
	if ( SUCCEEDED( outResult ) )
	{
		bool hasInited = false;
		CFBundleRef mainBundleRef = CFBundleGetMainBundle();
		CFStringRef mainBundleID = CFBundleGetIdentifier( mainBundleRef );
		CFStringRef helixPrefFileID = ( CFStringRef ) CFPreferencesCopyAppValue( CFSTR( "HelixPreferenceFileID" ), mainBundleID );
		if ( !helixPrefFileID )
		{
			helixPrefFileID = mainBundleID;
			CFRetain( helixPrefFileID );
		}
		CFURLRef helixDirectoryURL = CopyHelixDirectoryURL();
		if ( helixDirectoryURL )
		{
			FPRMSETDLLACCESSPATH SetDLLAccessPath = ( FPRMSETDLLACCESSPATH ) CFBundleGetFunctionPointerForName( m_CoreBundle, CFSTR( "SetDLLAccessPath" ) );
			if ( SetDLLAccessPath )
			{
				struct DLLFolder
				{
					CFStringRef folderDLLAccessKey;
					CFStringRef folderPrefKey;
					CFStringRef folderName;
				};
				const CFStringRef kHelixFolderPrefKeyPrefix = CFSTR( "HelixShared" );
				const DLLFolder kDLLAccessFolders[] = 
				{
					{ CFSTR( "DT_Plugins" ), CFSTR( "DT_Plugins" ), CFSTR( "Plugins" ) },
					{ CFSTR( "DT_Codecs" ),  CFSTR( "DT_Codecs" ),  CFSTR( "Codecs" ) },
					{ CFSTR( "DT_Common" ),  CFSTR( "DT_Common" ),  CFSTR( "Common" ) },
					{ nil, nil, nil }
				};
				CFStringRef folderPrefKeyPrefix = nil;
				CFDataRef helixPrefMapRef = ( CFDataRef ) CFPreferencesCopyAppValue( CFSTR( "HelixPrefMap" ), helixPrefFileID );
				if ( helixPrefMapRef )
				{
					CFStringRef errorString;
					CFDictionaryRef helixPrefMap = ( CFDictionaryRef ) CFPropertyListCreateFromXMLData( kCFAllocatorDefault, helixPrefMapRef, kCFPropertyListImmutable, &errorString );
					if ( helixPrefMap )
					{
						folderPrefKeyPrefix = ( CFStringRef ) CFDictionaryGetValue( helixPrefMap, kHelixFolderPrefKeyPrefix );
						CFRetain( folderPrefKeyPrefix );
						CFRelease( helixPrefMap );
					}
					if ( errorString ) CFRelease( errorString );
					CFRelease( helixPrefMapRef );
				}
				if ( !folderPrefKeyPrefix )
				{
					folderPrefKeyPrefix = kHelixFolderPrefKeyPrefix;
					CFRetain( folderPrefKeyPrefix );
				}
				hasInited = true;
				for ( int index = 0; hasInited && ( kDLLAccessFolders[ index ].folderDLLAccessKey != nil ); ++index )
				{
					hasInited = false;
					CFURLRef dllAccessFolderURL = CFURLCreateCopyAppendingPathComponent( kCFAllocatorDefault, helixDirectoryURL, kDLLAccessFolders[ index ].folderName, true );
					if ( dllAccessFolderURL )
					{
						CFStringRef dllAccessFolderPath = CFURLCopyFileSystemPath( dllAccessFolderURL, kCFURLPOSIXPathStyle );
						if ( dllAccessFolderPath )
						{
							CFStringRef dllAccessPath = CFStringCreateWithFormat( kCFAllocatorDefault, nil, CFSTR( "%@=%@" ), kDLLAccessFolders[ index ].folderDLLAccessKey, dllAccessFolderPath );
							if ( dllAccessPath )
							{
								// UTF-8 form of HFS paths must either be canonically decomposed UTF-8, or else obtained by CFURLGetFileSystemRepresentation
								const CFIndex kNoMaxLength = 0;
								CFMutableStringRef normalizedDLLAccessPath = CFStringCreateMutableCopy( kCFAllocatorDefault, kNoMaxLength, dllAccessPath );
								if ( normalizedDLLAccessPath )
								{
									CFStringNormalize( normalizedDLLAccessPath, kCFStringNormalizationFormD );
									
									CFIndex bufferSize = 1 + CFStringGetMaximumSizeForEncoding( CFStringGetLength( normalizedDLLAccessPath ), kCFStringEncodingUTF8 );
									char* pDLLAccessPath = new char[ bufferSize ];
									if ( pDLLAccessPath )
									{
										if ( CFStringGetCString( normalizedDLLAccessPath, pDLLAccessPath, bufferSize, kCFStringEncodingUTF8 ) )
										{
											HX_RESULT result = SetDLLAccessPath( pDLLAccessPath );
											if ( SUCCEEDED( result ) )
											{
												CFStringRef folderPrefKey = CFStringCreateWithFormat( kCFAllocatorDefault, nil, CFSTR( "%@\\%@" ), folderPrefKeyPrefix, kDLLAccessFolders[ index ].folderPrefKey );
												if ( folderPrefKey )
												{
													CFPreferencesSetAppValue( folderPrefKey, dllAccessFolderPath, helixPrefFileID );
													CFRelease( folderPrefKey );
													hasInited = true;
												}
											}
										}
										delete [] pDLLAccessPath;
									}
									CFRelease( normalizedDLLAccessPath );
								}
								CFRelease( dllAccessPath );
							}
							CFRelease( dllAccessFolderPath );
						}
						CFRelease( dllAccessFolderURL );
					}
				}
				CFRelease( folderPrefKeyPrefix );
			}
			CFRelease( helixDirectoryURL );
		}
		CFRelease( helixPrefFileID );
		
		outResult = hasInited ? HXR_OK : HXR_COULDNOTINITCORE;
	}
	return outResult;
}

IHXClientEngine*
CHXClientEngineMac::OnCreateClientEngine( HX_RESULT* pResult )
{
	*pResult = HXR_OK;
	if ( !m_pICoreEngine )
	{
		*pResult = InitDLLAccessPaths();
		if ( SUCCEEDED( *pResult ) )
		{
			FPRMCREATEENGINE CreateEngine = ( FPRMCREATEENGINE ) CFBundleGetFunctionPointerForName( m_CoreBundle, CFSTR( "CreateEngine" ) );
			if ( CreateEngine )
			{
				*pResult = CreateEngine( &m_pICoreEngine );
				CHXASSERT( ( NULL != m_pICoreEngine ) || ( FAILED( *pResult ) ) ); // If m_pICoreEngine was not created, we should receive a reason.
			}
			else
			{
				*pResult = HXR_COULDNOTINITCORE;
			}
		}
	}
	return m_pICoreEngine;
}

IHXClientEngine*
CHXClientEngineMac::GetClientEngine( void )
{
	return m_pICoreEngine;
}

void
CHXClientEngineMac::OnCloseClientEngine( void )
{
	if (  m_pICoreEngine )
	{
		CHXASSERT( m_CoreBundle );
		FPRMCLOSEENGINE CloseEngine = ( FPRMCLOSEENGINE ) CFBundleGetFunctionPointerForName( m_CoreBundle, CFSTR( "CloseEngine" ) );
		if ( CloseEngine )
		{
			CloseEngine( m_pICoreEngine );
		}
		m_pICoreEngine = NULL;
	}
}

void
CHXClientEngineMac::UnloadClientCore( void )
{
	if (  m_CoreBundle )
	{
		OnCloseClientEngine();
		
		CFBundleUnloadExecutable( m_CoreBundle );
		CFRelease( m_CoreBundle );
		m_CoreBundle = nil;
	}
}
