/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: HXClientPlayer.mm,v 1.16.2.4 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 ***** */

#import "HXClientEngine.h"
#import "HXClientPlayer.h"
#import "CHXClientPlayer.h"
#import "HXClientConstants.h"
#import "HXStringUtils.h"
#import "CHXClientDebug.h"

#if defined(HELIX_FEATURE_REGISTRY) && defined(HELIX_FEATURE_STATS)
#import "CHXStatisticObserverBridge.h"
#endif

#ifdef HELIX_FEATURE_VIDEO
#import "HXClientView.h"
#endif

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

extern HXClientCallbacks gPlayerToObserverCallbacks;

@implementation HXClientPlayer

- (id)init
{
	[self doesNotRecognizeSelector:@selector(init)];
	return nil;
}

- (IHXClientPlayerPtr)createClientForCorePlayer:(void *)pIHXPlayer
									 withEngine:(void *)pIHXClientEngine
									   inWindow:(SHXClientWindow *)pWindow
								   withObserver:(id)observer
								 usingCallbacks:(HXClientCallbacks*)pClientCallbacks
{
	IHXClientPlayerPtr pIClientPlayer = CHXClientPlayer::Create( ( IHXClientEngine* ) pIHXClientEngine, ( IHXPlayer* ) pIHXPlayer, ( HXxWindow* ) pWindow, observer, pClientCallbacks );
	CHXASSERT( pIClientPlayer );
	return pIClientPlayer;
}

- (id)initInWindow:(SHXClientWindow *)pWindow withObserver:(id)observer usingCallbacks:(HXClientCallbacks*)pClientCallbacks
{
	self = [super init];
	if ( self )
	{
		HX_RESULT result = HXR_OUTOFMEMORY;
		clientEngine = [[HXClientEngine sharedClientEngine] retain];
		IHXClientEngine* pICoreEngine = [clientEngine coreEngine];
		if ( pICoreEngine )
		{
			IHXPlayer* pICorePlayer = NULL;
			result = pICoreEngine->CreatePlayer( pICorePlayer );
			if ( SUCCEEDED( result ) )
			{
				m_pIClientPlayer = [self createClientForCorePlayer:pICorePlayer
														withEngine:pICoreEngine
														  inWindow:pWindow
													  withObserver:observer
													usingCallbacks:pClientCallbacks];
				pICorePlayer->Release();
				return self;
			}
		}
		[clientEngine release];
		clientEngine = nil;
		if ( pClientCallbacks->OnErrorOccurred )
		{
			pClientCallbacks->OnErrorOccurred( observer, result, 0, NULL, NULL, NULL ); // XXXSEH: Should we fill in the last 4 arguments?
		}
		[self autorelease];
		self = nil;
	}
	return self;
}

- (id)initInWindow:(SHXClientWindow *)pWindow withObserver:(id)observer
{
	self = [self initInWindow:pWindow withObserver:observer usingCallbacks:&gPlayerToObserverCallbacks];
	if ( self )
	{
	}
	return self;
}

#ifdef HELIX_FEATURE_VIDEO
- (id)initInView:(HXClientView *)view withObserver:(id)observer
{
	CHXASSERT( view ); // @"initInView requires a valid HXClientView*"

	self = [self initInWindow:[view siteWindow] withObserver:observer];
	if ( self )
	{
		[view setClientPlayer:self];
	}
	return self;
}
#endif

- (void)dealloc
{
#if defined(HELIX_FEATURE_REGISTRY) && defined(HELIX_FEATURE_STATS)
	if ( m_pStatisticObserver )
	{
		CHXStatisticObserverBridge* pStatisticObserverBridge = ( CHXStatisticObserverBridge* ) m_pStatisticObserver;
		delete pStatisticObserverBridge;
		m_pStatisticObserver = NULL;
	}
#endif
	HX_RELEASE( m_pIClientPlayer );
	[clientEngine release];
	clientEngine = nil;

	[super dealloc];
}

- (void)openWithURL:(NSURL *)url mimeType:(NSString *)mimeType
{
	if ( m_pIClientPlayer )
	{
		const char* pURL = [[url absoluteString] UTF8String];
		const char* pMimeType = mimeType ? [mimeType UTF8String] : NULL;
		m_pIClientPlayer->OpenURL( pURL, pMimeType );
	}
}

- (BOOL)canViewSource
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->CanViewSource() ? YES : NO ) : NO;
}

- (void)viewSource
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->ViewSource();
	}
}

- (BOOL)canViewRights
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->CanViewRights() ? YES : NO ) : NO;
}

- (void)viewRights
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->ViewRights();
	}
}

- (BOOL)authenticateForUser:(NSString *)username withPassword:(NSString *)password
{
	if ( m_pIClientPlayer )
	{
		if ( !username ) return ( 0 != SUCCEEDED( m_pIClientPlayer->Authenticate( false, NULL, NULL ) ) ) ? YES : NO;

		const char* pUsername = NULL;
		char* pUsernameBuffer = NULL;
		CFStringRef usernameText = ( CFStringRef ) username;
		pUsername = CFStringGetCStringPtr( usernameText, kCFStringEncodingUTF8 );
		if ( !pUsername )
		{
			CFIndex bufferSize = 1 + CFStringGetMaximumSizeForEncoding( CFStringGetLength( usernameText ), kCFStringEncodingUTF8 );
			pUsernameBuffer = new char[ bufferSize ];
			if ( pUsernameBuffer &&
					CFStringGetCString( usernameText, pUsernameBuffer, bufferSize, kCFStringEncodingUTF8 ) )
			{
				pUsername = pUsernameBuffer;
			}
		}
		const char* pPassword = NULL;
		char* pPasswordBuffer = NULL;
		if ( password )
		{
			CFStringRef passwordText = ( CFStringRef ) password;
			pPassword = CFStringGetCStringPtr( passwordText, kCFStringEncodingUTF8 );
			if ( !pPassword )
			{
				CFIndex bufferSize = 1 + CFStringGetMaximumSizeForEncoding( CFStringGetLength( passwordText ), kCFStringEncodingUTF8 );
				pPasswordBuffer = new char[ bufferSize ];
				if ( pPasswordBuffer &&
					 CFStringGetCString( passwordText, pPasswordBuffer, bufferSize, kCFStringEncodingUTF8 ) )
				{
					pPassword = pPasswordBuffer;
				}
			}
		}
		BOOL outHasAuthenticated = ( 0 != SUCCEEDED( m_pIClientPlayer->Authenticate( true, pUsername, pPassword ) ) ) ? YES : NO;

		delete [] pPasswordBuffer;
		delete [] pUsernameBuffer;
		
		return outHasAuthenticated;
	}
	return NO;
}

- (int)contentState
{
	return m_pIClientPlayer ? m_pIClientPlayer->GetContentState() : kContentStateNotLoaded;
}

- (BOOL)setStatus:(NSString *)status
{
	if ( m_pIClientPlayer )
	{
		if ( !status || ( [status length] <= 0 ) )
		{
			return m_pIClientPlayer->SetStatus( NULL ) ? YES : NO;
		}
		CFStringRef statusText = ( CFStringRef ) status;
		const char* pStatusText = CFStringGetCStringPtr( statusText, kCFStringEncodingUTF8 );
		if ( pStatusText )
		{
			return m_pIClientPlayer->SetStatus( pStatusText ) ? YES : NO;
		}
		CFIndex bufferSize = 1 + CFStringGetMaximumSizeForEncoding( CFStringGetLength( statusText ), kCFStringEncodingUTF8 );
		char* pConvertedStatusText = new char[ bufferSize ];
		if ( !pConvertedStatusText ) return NO;
		
		BOOL outDidSetStatus = NO;
		if ( CFStringGetCString( statusText, pConvertedStatusText, bufferSize, kCFStringEncodingUTF8 ) )
		{
			outDidSetStatus = m_pIClientPlayer->SetStatus( pConvertedStatusText ) ? YES : NO;
		}
		delete [] pConvertedStatusText;
		return outDidSetStatus;
	}
	return NO;
}

- (void)play
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->Play();
	}
}

- (void)pause
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->Pause();
	}
}

- (void)stop
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->Stop();
	}
}

- (BOOL)startSeeking
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->StartSeeking() ? YES : NO ) : NO;
}

- (void)stopSeeking
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->StopSeeking();
	}
}

- (BOOL)setPosition:(UInt32)position
{
	return m_pIClientPlayer ? ( ( 0 != SUCCEEDED( m_pIClientPlayer->SetPosition( position ) ) ) ? YES : NO ) : NO;
}

- (UInt32)position
{
	return m_pIClientPlayer ? m_pIClientPlayer->GetPosition() : 0;
}

- (UInt32)length
{
	return m_pIClientPlayer ? m_pIClientPlayer->GetLength() : 0;
}

- (BOOL)isLive
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->IsLive() ? YES : NO ) : NO;
}

- (NSString *)title
{
	if ( m_pIClientPlayer )
	{
		const char* pTitle = m_pIClientPlayer->GetTitle();
		if ( pTitle && *pTitle )
		{
			CFStringRef title = CFStringCreateWithCString( kCFAllocatorDefault, pTitle, GuessEncodingForCString( ( const unsigned char* ) pTitle ) );
			return( [(NSString*)title autorelease] );
		}
	}
	return [NSString string];
}

- (NSURL *)contextURL
{
	if ( m_pIClientPlayer )
	{
		const char* pContextURL = m_pIClientPlayer->GetContextURL();
		if ( pContextURL && *pContextURL )
		{
			NSString* urlString = [NSString stringWithCString:pContextURL];
			NSString* escapedURLString = ( NSString* ) CFURLCreateStringByAddingPercentEscapes( NULL, ( CFStringRef ) urlString, NULL, NULL, kCFStringEncodingUTF8 );
			NSURL* contextURL = [NSURL URLWithString:escapedURLString]; // returns nil if pURL cannot be parsed.
			[escapedURLString release];
			
			return contextURL;
		}
	}
	return nil;
}

- (BOOL)hasVisualContent
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->HasVisualContent() ? YES : NO ) : NO;
}

- (NSSize)idealSize
{
	NSSize idealSize = NSZeroSize;
	if ( m_pIClientPlayer )
	{
		INT32 siteIdealWidth, siteIdealHeight;
		m_pIClientPlayer->GetIdealSize( &siteIdealWidth, &siteIdealHeight );
		idealSize.width  = siteIdealWidth;
		idealSize.height = siteIdealHeight;
	}
	return idealSize;
}

- (SInt32)clipBandwidth
{
	return m_pIClientPlayer ? m_pIClientPlayer->GetClipBandwidth() : 0;
}

- (void)setSize:(NSSize)siteSize
{
	if ( m_pIClientPlayer )
	{
		INT32 siteWidth  = ( INT32 ) siteSize.width;
		INT32 siteHeight = ( INT32 ) siteSize.height;
		m_pIClientPlayer->SetSize( siteWidth, siteHeight );
	}
}

- (void)drawSite:(SHXClientRect)siteRect
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->DrawSite( ( const HXxRect* ) &siteRect );
	}
}

- (UInt16)groupCount
{
	return m_pIClientPlayer ? m_pIClientPlayer->GetGroupCount() : 0;
}

- (UInt16)currentGroup
{
	return m_pIClientPlayer ? m_pIClientPlayer->GetCurrentGroup() : 0;
}

- (BOOL)setCurrentGroup:(UInt16)groupIndex
{
	return m_pIClientPlayer ? ( ( 0 != SUCCEEDED( m_pIClientPlayer->SetCurrentGroup( groupIndex ) ) ) ? YES : NO ) : NO;
}

- (NSURL*)groupURLByIndex:(UInt16)groupIndex
{
	if ( m_pIClientPlayer )
	{
		static const UInt32 kBufferSize = 4096; // XXXSEH: Is this big enough? Could call GetGroupTitle() twice; the first time will a nil buffer to obtain the necessary size.
		char urlBuffer[ kBufferSize ];
		UInt32 usedBufferLength;
		if ( m_pIClientPlayer->GetGroupURL( groupIndex, urlBuffer, kBufferSize - 1, &usedBufferLength ) )
		{
			NSString* urlString = [NSString stringWithCString:urlBuffer];
			NSString* escapedURLString = (NSString*) CFURLCreateStringByAddingPercentEscapes( NULL, (CFStringRef) urlString, NULL, NULL, kCFStringEncodingUTF8 );
			NSURL* url = [NSURL URLWithString:escapedURLString]; // returns nil if pURL cannot be parsed
			[escapedURLString release];

			return url;
		}
	}
	return nil;
}

- (NSString*)groupTitleByIndex:(UInt16)groupIndex
{
	if ( m_pIClientPlayer )
	{
		static const UInt32 kBufferSize = 4096; // XXXSEH: Is this big enough? Could call GetGroupTitle() twice; the first time will a nil buffer to obtain the necessary size.
		char titleBuffer[ kBufferSize ];
		UInt32 usedBufferLength;
		if ( m_pIClientPlayer->GetGroupTitle( groupIndex, titleBuffer, kBufferSize - 1, &usedBufferLength ) )
		{
			CFStringRef title = CFStringCreateWithCString( kCFAllocatorDefault, titleBuffer, GuessEncodingForCString( ( const unsigned char* ) titleBuffer ) );
			return [(NSString*)title autorelease];
		}
	}
	return [NSString string];
}

- (NSArray*)groupsArray
{
	if ( m_pIClientPlayer )
	{
		UInt16 groupCount = [self groupCount];
		if ( groupCount > 0 )
		{
			NSMutableArray* groupsArray = [NSMutableArray arrayWithCapacity:groupCount];
			if ( groupsArray )
			{
				for ( UInt16 groupIndex = 0; groupIndex < groupCount; ++groupIndex )
				{
					NSString* groupTitle = [self groupTitleByIndex:groupIndex];
					[groupsArray addObject:groupTitle];
				}
				// NSLog([groupsArray description]);  useful for seeing when the array of names changes
				return groupsArray;
			}
		}
	}
	return nil;
}

- (UInt16)volume
{
	return m_pIClientPlayer ? m_pIClientPlayer->GetVolume() : 0;
}

- (void)setVolume:(UInt16)volume
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->SetVolume( volume );
	}
}

- (BOOL)isMuted
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->IsMuted() ? YES : NO ) : NO;
}

- (void)mute:(BOOL)shouldMute
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->Mute( shouldMute ? true : false );
	}
}

- (void)enableEQ:(BOOL)enable
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->EnableEQ( enable ? true : false );
	}
}

- (BOOL)isEQEnabled
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->IsEQEnabled() ? YES : NO ) : NO;
}

- (void)setEQGain:(SInt32)gain forBand:(int)band
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->SetEQGain( band, gain );
	}
}

- (SInt32)eqGainForBand:(int)band
{
	return m_pIClientPlayer ? m_pIClientPlayer->GetEQGain( band ) : 0;
}

- (void)setEQPreGain:(SInt32)preGain
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->SetEQPreGain( preGain );
	}
}

- (SInt32)eqPreGain
{
	return m_pIClientPlayer ? m_pIClientPlayer->GetEQPreGain() : 0;
}

- (void)enableEQAutoPreGain:(BOOL)enable
{
	if ( m_pIClientPlayer )
	{
		m_pIClientPlayer->EnableEQAutoPreGain( enable ? true : false );
	}
}

- (BOOL)isEQAutoPreGainEnabled
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->IsEQAutoPreGainEnabled() ? YES : NO ) : NO;
}

- (void)setEQReverb:(SInt32)reverb
{
	if ( m_pIClientPlayer )
	{
		static const SInt32 kIgnoreRoomSize = -1;
		m_pIClientPlayer->SetEQReverb( kIgnoreRoomSize, reverb );
	}
}

- (SInt32)eqReverb
{
	SInt32 reverb = 0;
	if ( m_pIClientPlayer )
	{
		SInt32 roomSize = 0;
		m_pIClientPlayer->GetEQReverb( &roomSize, &reverb );
	}
	return reverb;
}

- (void)setEQRoomSize:(SInt32)roomSize
{
	if ( m_pIClientPlayer )
	{
		static const SInt32 kIgnoreReverb = -1;
		m_pIClientPlayer->SetEQReverb( roomSize, kIgnoreReverb );
	}
}

- (SInt32)eqRoomSize
{
	SInt32 roomSize = 0;
	if ( m_pIClientPlayer )
	{
		SInt32 reverb = 0;
		m_pIClientPlayer->GetEQReverb( &roomSize, &reverb );
	}
	return roomSize;
}

- (BOOL)hasVideoAttribute:(float *)curValue forKey:(int)attributeKey
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->GetVideoAttribute( attributeKey, curValue ) ? YES : NO ) : NO;
}

- (BOOL)setVideoAttribute:(float)value forKey:(int)attributeKey
{
	return m_pIClientPlayer ? ( m_pIClientPlayer->SetVideoAttribute( attributeKey, value ) ? YES : NO ) : NO;
}

- (CFTypeRef)copyStatisticsFor:(NSString *)key
{
	CFTypeRef statisticsValue = nil;
	if ( m_pIClientPlayer && key )
	{
		int valueType;
		UInt32 bufferLength = 0;
		m_pIClientPlayer->GetStatistic( [key cString], NULL, bufferLength, &valueType, &bufferLength );
		if ( bufferLength > 0 )
		{
			switch ( valueType )
			{
				case kValueType32BitSignedInt:
				{
					SInt32 intValue;
					if ( m_pIClientPlayer->GetStatistic( [key cString], ( unsigned char* ) &intValue, bufferLength, &valueType, &bufferLength ) )
					{
						statisticsValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type, &intValue );
					}
				}
				break;
				
				case kValueTypeString:
				{
					unsigned char* pValueBuffer = new unsigned char[ bufferLength ];
					if ( pValueBuffer )
					{
						if ( m_pIClientPlayer->GetStatistic( [key cString], pValueBuffer, bufferLength, &valueType, &bufferLength ) )
						{
							statisticsValue = CFStringCreateWithCString( kCFAllocatorDefault, ( const char* ) pValueBuffer, GuessEncodingForCString( pValueBuffer ) );
						}
						delete [] pValueBuffer;
						pValueBuffer = NULL;
					}
				}
				break;
			}
		}
	}
	return statisticsValue;
}

- (BOOL)addObserver:(id)observer forStatistic:(NSString *)key userInfo:(UInt32)aUserInfo
{
#if defined(HELIX_FEATURE_REGISTRY) && defined(HELIX_FEATURE_STATS)
	if ( m_pIClientPlayer )
	{
		if ( !m_pStatisticObserver )
		{
			m_pStatisticObserver = new CHXStatisticObserverBridge( m_pIClientPlayer );
		}
		if (  m_pStatisticObserver )
		{
			CHXStatisticObserverBridge* pStatisticObserverBridge = ( CHXStatisticObserverBridge* ) m_pStatisticObserver;
			return( pStatisticObserverBridge->AddObserver( observer, key, aUserInfo ) ? YES : NO );
		}
	}
#endif
	return NO;
}

- (void)removeObserver:(id)observer forStatistic:(NSString *)key userInfo:(UInt32)aUserInfo
{
#if defined(HELIX_FEATURE_REGISTRY) && defined(HELIX_FEATURE_STATS)
	CHXStatisticObserverBridge* pStatisticObserverBridge = ( CHXStatisticObserverBridge* ) m_pStatisticObserver;
	if ( pStatisticObserverBridge )
	{
		pStatisticObserverBridge->RemoveObserver( observer, key, aUserInfo );
	}
#endif
}

@end
