/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: CHXClientDataStream.cpp,v 1.6.2.3 2004/07/09 01:49:47 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 "CHXClientDataStream.h"

#include "CHXClientBuffer.h"
#include "CHXClientRequest.h"
#include "CHXClientPlayer.h"
#include "CHXClientEngine.h"
#include "CHXClientDebug.h"

#include "hlxclib/stdlib.h"

#include "enter_hx_headers.h"
#include "ihxpckts.h"
#include "hxplugn.h"
#include "hxcore.h"   // IHXValidator
#include "mfsiface.h"

#include "hxsmartptr.h"
HX_SMART_POINTER_INLINE( SPIHXBuffer, IHXBuffer );
HX_SMART_POINTER_INLINE( SPIHXValues, IHXValues );
HX_SMART_POINTER_INLINE( SPIHXClientEngine, IHXClientEngine );
HX_SMART_POINTER_INLINE( SPIHXPlugin, IHXPlugin );
HX_SMART_POINTER_INLINE( SPIHXPlugin2Handler, IHXPlugin2Handler );
HX_SMART_POINTER_INLINE( SPIHXMemoryFileSystem2, IHXMemoryFileSystem2 );
HX_SMART_POINTER_INLINE( SPIHXValidator, IHXValidator );
#include "exit_hx_headers.h"

static const char* const kProtocolMemFSName = "mem";
static const char* const kProtocolPropertyName  = "FileProtocol";
static UINT32 const kMetaFileMaximumSize = 8192; // 8K
static const char* const kMimeTypePropertyName = "Content-Type"; // XXXSEH: Put in common header. Duplicated in CHXMimeTypeHeader.

// Created this class instead of inheriting CHXClientDataStream from IHXMemoryFileContext to avoid circular references.
class CHXClientMemoryFileContext : public IHXMemoryFileContext
{
    DECLARE_UNKNOWN_NOCREATE( CHXClientMemoryFileContext )
private:
	LONG32 m_lCount;
public:
    virtual ~CHXClientMemoryFileContext( void ) {}
    CHXClientMemoryFileContext( void )
		: m_lCount( 0 ) {}
	
	// IHXMemoryFileContext
	STDMETHOD ( RequestOpen ) ( THIS_ const char* pURL ) { return HXR_NOTIMPL; }
	STDMETHOD ( RequestRead ) ( THIS_ void* pID, ULONG32 ulBytes ) { return HXR_NOTIMPL; }
};
DEFINE_SINGLE_INTERFACE_COMPONENT( CHXClientMemoryFileContext, IHXMemoryFileContext, m_lCount )


CHXClientDataStream::CHXClientDataStream( CHXClientPlayer* pClientPlayer, const char* pURL, const char* pMimeType, UINT32 dataLength, bool autoPlay )
	: m_pClientPlayer( pClientPlayer )
	, m_pBuffer( NULL )
	, m_pIRequest( NULL )
	, m_DataLength( dataLength )
	, m_pIMemoryFileSystem( NULL )
	, m_AutoPlay( autoPlay )
{
	CHXASSERT( pURL && *pURL );

	char* pMemFSURL = ( char* ) malloc( strlen( kProtocolMemFSName ) + 3 + strlen( pURL ) + 1 );
	CHXASSERT( pMemFSURL );
	if ( pMemFSURL )
	{
		sprintf( pMemFSURL, "%s://%s", kProtocolMemFSName, pURL );
		m_pIRequest = new CHXClientRequest( pMemFSURL, pMimeType );
		m_pIRequest->AddRef();
		free( pMemFSURL );
	}
}

CHXClientDataStream::~CHXClientDataStream( void )
{
	if ( m_pIMemoryFileSystem )
	{
		m_pIMemoryFileSystem->Remove( this );
		m_pIMemoryFileSystem->Release();
		m_pIMemoryFileSystem = NULL;
	}
	HX_RELEASE( m_pBuffer );
	HX_RELEASE( m_pIRequest );
}

HX_RESULT
CHXClientDataStream::GetMemoryFileSystem( IHXMemoryFileSystem** ppMemoryFileSystem )
{
	CHXASSERT( ppMemoryFileSystem && !*ppMemoryFileSystem );
	
	SPIHXClientEngine spClientEngine;
	if ( !SUCCEEDED( m_pClientPlayer->GetHXClientEngine( spClientEngine.AsInOutParam() ) ) ) return HXR_FAIL;

	HX_RESULT result;
	SPIHXPlugin2Handler spPlugin2Handler;
	result = spPlugin2Handler.Query( spClientEngine.Ptr() );
	if ( FAILED( result ) ) return result;
	
	SPIUnknown spUnkPlugin;
	result = spPlugin2Handler->FindPluginUsingStrings( ( char* ) kProtocolPropertyName, ( char* ) kProtocolMemFSName,
													   NULL, NULL, NULL, NULL, *spUnkPlugin.AsInOutParam() );
	if ( FAILED( result ) ) return result;
	SPIHXPlugin spPlugin = spUnkPlugin.Ptr();
	CHXASSERT( spPlugin.IsValid() );

	result = spPlugin->InitPlugin( spClientEngine.Ptr() );
	if ( FAILED( result ) ) return result;

	result = spPlugin.Ptr()->QueryInterface( IID_IHXMemoryFileSystem, ( void** ) ppMemoryFileSystem );
	if ( FAILED( result ) ) return result;

	// if the recursion depth has been overriden, change it
	/* SPIHXValues spValues = *ppMemoryFileSystem;
	if ( spValues.IsValid() )
	{
		spValues->SetPropertyULONG32( MEMFS_RECURSION_DEPTH, ... );
	} */
	return HXR_OK;
}

HX_RESULT
CHXClientDataStream::InitMemoryFileSystem( void )
{
	if ( NULL != m_pIMemoryFileSystem ) return HXR_OK;
	if ( !m_pIRequest ) return HXR_FAIL;
	
	const char* pMemFSURL = NULL;
	m_pIRequest->GetURL( pMemFSURL );
	if ( !pMemFSURL || !*pMemFSURL ) return HXR_FAIL;
	
	HX_RESULT result = GetMemoryFileSystem( &m_pIMemoryFileSystem );
	if ( FAILED( result ) ) return result;
	
	IHXMemoryFileContext* pIMemoryFileContext = new CHXClientMemoryFileContext;
	pIMemoryFileContext->AddRef();
	
	const char* pMimeType = NULL;
	SPIHXValues spHeader;
	SPIHXBuffer spMimeTypeBuffer;
	m_pIRequest->GetResponseHeaders( *spHeader.AsInOutParam() );
	if ( spHeader.IsValid() )
	{
		spHeader->GetPropertyCString( kMimeTypePropertyName, *spMimeTypeBuffer.AsInOutParam() );
		if ( spMimeTypeBuffer.IsValid() )
		{
			pMimeType = ( const char* ) spMimeTypeBuffer->GetBuffer();                    
		}
	}
	SPIHXMemoryFileSystem2 spMemoryFileSystem2 = m_pIMemoryFileSystem;
	result = spMemoryFileSystem2.IsValid() ?
			 spMemoryFileSystem2->AddWithSize( pMemFSURL, pIMemoryFileContext, this, pMimeType, m_DataLength ) :
			 m_pIMemoryFileSystem->Add( pMemFSURL, pIMemoryFileContext, this, pMimeType );
	
	pIMemoryFileContext->Release();
	pIMemoryFileContext = NULL;
	
	if ( FAILED( result ) )
	{
		m_pIMemoryFileSystem->Release();
		m_pIMemoryFileSystem = NULL;
	}
	return result;
}

bool
CHXClientDataStream::ValidateBufferData( void )
{
	if ( !m_pIRequest ) return false;
	
	SPIHXClientEngine spClientEngine;
	if ( !SUCCEEDED( m_pClientPlayer->GetHXClientEngine( spClientEngine.AsInOutParam() ) ) ) return false;

	SPIHXValidator spValidator = spClientEngine.Ptr();
	if ( !spValidator.IsValid() ) return false;
	
	HX_RESULT result = spValidator->ValidateMetaFile( m_pIRequest, m_pBuffer );
	return result != HXR_INVALID_METAFILE;
}

HX_RESULT
CHXClientDataStream::WriteData( UINT32 bufferLength, unsigned char* pBuffer )
{
	HX_RESULT outResult = HXR_FAIL;
	if ( m_pIMemoryFileSystem )
	{
		outResult = m_pIMemoryFileSystem->Append( this, pBuffer, bufferLength );
	}
	else
	{
		UINT32 newBufferSize = bufferLength + ( m_pBuffer ? m_pBuffer->GetSize() : 0 );
		if ( newBufferSize > kMetaFileMaximumSize )
		{
			outResult = InitMemoryFileSystem();
			if ( SUCCEEDED( outResult ) )
			{
				if ( m_pBuffer )
				{
					outResult = m_pIMemoryFileSystem->Append( this, m_pBuffer->GetBuffer(), m_pBuffer->GetSize() );
				}
				if ( SUCCEEDED( outResult ) )
				{
					outResult = m_pIMemoryFileSystem->Append( this, pBuffer, bufferLength );
					if ( SUCCEEDED( outResult ) )
					{
						outResult = m_pClientPlayer->OpenRequest( m_pIRequest );
						if ( SUCCEEDED( outResult ) && m_AutoPlay )
						{
							m_pClientPlayer->Play();
						}
					}
				}
				if ( FAILED( outResult ) )
				{
					m_pIMemoryFileSystem->Remove( this );
					m_pIMemoryFileSystem->Release();
					m_pIMemoryFileSystem = NULL;
				}
			}
			HX_RELEASE( m_pBuffer );
		}
		else
		{
			outResult = HXR_OUTOFMEMORY;
			if ( !m_pBuffer )
			{
				m_pBuffer = new CHXClientBuffer;
				if ( m_pBuffer )
				{
					m_pBuffer->AddRef();
				}
			}
			if (  m_pBuffer )
			{
				outResult = m_pBuffer->Add( pBuffer, bufferLength );
			}
		}
	}
	return outResult;
}

HX_RESULT
CHXClientDataStream::CloseData( void )
{
	HX_RESULT outResult;
	if ( m_pIMemoryFileSystem )
	{
		( void ) m_pIMemoryFileSystem->Finish( this );
		outResult = HXR_OK;
	}
	else
	{
		outResult = HXR_FAIL;
		if ( m_pBuffer )
		{
			if ( ValidateBufferData() )
			{
				outResult = InitMemoryFileSystem();
				if ( SUCCEEDED( outResult ) )
				{
					outResult = m_pIMemoryFileSystem->Append( this, m_pBuffer->GetBuffer(), m_pBuffer->GetSize() );
					if ( SUCCEEDED( outResult ) )
					{
						( void ) m_pIMemoryFileSystem->Finish( this ); // XXXSEH: Don't know if we care about the return value?
						outResult = m_pClientPlayer->OpenRequest( m_pIRequest );
						if ( SUCCEEDED( outResult ) && m_AutoPlay )
						{
							m_pClientPlayer->Play();
						}
					}
					if ( FAILED( outResult ) )
					{
						m_pIMemoryFileSystem->Remove( this );
						m_pIMemoryFileSystem->Release();
						m_pIMemoryFileSystem = NULL;
					}
				}
			}
			HX_RELEASE( m_pBuffer );
		}
	}
	return outResult;
}
