// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include "dummypcmrenderer.h"
#include "iltestuids.hrh"
#include "tilstructtypes.h"
#include <ecom/implementationproxy.h>
#include <mmf/server/mmfdatabuffer.h>


#define KDummyPCM16FormatUid 0x10111126 //dummy

const TInt KBufferSize = 16384;			// we need 16k to hold a pcm packet

const TInt KDefaultSampleRate = 8000;
const TInt KDefaultNumberChannels = 1;

const TInt KInputPortIndex = 0;
	

// ------------------------------------------------------------------------------------------
// CDummyPcmRenderer::CInputPort Implementation
	
CDummyPcmRenderer::CInputPort::CInputPort(CDummyPcmRenderer& aParent) 
	: CActive(EPriorityNormal),
	  iParent(aParent),
	  iSampleRate(KDefaultSampleRate),
	  iChannels(KDefaultNumberChannels),
	  iBufferSize(KBufferSize)
	{
	CActiveScheduler::Add(this);	
	}
		
CDummyPcmRenderer::CInputPort::~CInputPort()
	{
	Cancel();
	iBuffers.Close();
	}

CDummyPcmRenderer::CInputPort* CDummyPcmRenderer::CInputPort::NewL(CDummyPcmRenderer& aParent)
	{
	CInputPort* self = new (ELeave) CInputPort(aParent);
	return self;
	}

TInt CDummyPcmRenderer::CInputPort::FillThisBuffer(CMMFBuffer& /*aInputBuffer*/)
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::CInputPort::EmptyThisBuffer(const CMMFBuffer& aInputBuffer)
	{
	TInt err = iBuffers.Append(&aInputBuffer);
	if (err == KErrNone)
		{
		TILComponentState state;
		if (iParent.GetState(state) != KErrNone)
			{
			return EComponentInvalid;		
			}
		if (state == EComponentExecuting && !IsActive())
			{
			SetActive();
			TRequestStatus* status = &iStatus;
			User::RequestComplete(status, KErrNone);
			}
		}
	return err;
	}
	
TInt CDummyPcmRenderer::CInputPort::TunnelRequest(MILComponentPortIf* aPort)
	{
	TILComponentState state;
	if (iParent.GetState(state) != KErrNone)
		{
		return EComponentInvalid;		
		}
	if (( state != EComponentLoaded) && (!iStopped))
		{
		return EComponentInvalid;
		}
	
	if (iPortConnectedTo)
		{
		// the port is already connected, return an error
		return EPortAlreadyTunnelled;
		}
	iPortConnectedTo = const_cast<MILComponentPortIf*>(aPort);
	return KErrNone;
	}
	
TInt CDummyPcmRenderer::CInputPort::DisconnectTunnel(MILComponentPortIf* /*aPort*/)
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::CInputPort::PortIndex() const
	{
	return KInputPortIndex;
	}	
	
TPortDirection CDummyPcmRenderer::CInputPort::PortDirection() const
	{
	return EDirInput;
	}

	
CMMFBuffer* CDummyPcmRenderer::CInputPort::CreateBufferL(TInt /*aBufferSize*/)
	{
	User::Leave(KErrNotSupported);
	return NULL;
	}
	
TInt CDummyPcmRenderer::CInputPort::UseBuffer(CMMFBuffer& /*aBuffer*/)
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::CInputPort::FreeBuffer(CMMFBuffer* /*aBuffer*/)
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::CInputPort::FlushPort()
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::CInputPort::EnablePort()
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::CInputPort::DisablePort()
	{
	return KErrNotSupported;
	}

MILComponentIf* CDummyPcmRenderer::CInputPort::PortComponent() const
	{
	return &iParent;
	}

void CDummyPcmRenderer::CInputPort::SetVolume(TUint aVolume)
	{
	iVolume = aVolume;
	}

TUint CDummyPcmRenderer::CInputPort::Volume()
	{
	return iVolume;
	}

TUint CDummyPcmRenderer::CInputPort::BytesPlayed()
	{
	return iBytesPlayed;
	}

void CDummyPcmRenderer::CInputPort::SetVolumeRamp(const TTimeIntervalMicroSeconds& aRampDuration)
	{
	iRampDuration = aRampDuration;
	}

TTimeIntervalMicroSeconds& CDummyPcmRenderer::CInputPort::VolumeRamp()
	{
	return iRampDuration;
	}

TInt CDummyPcmRenderer::CInputPort::SampleRate()
	{
	return iSampleRate;
	}
	
TInt CDummyPcmRenderer::CInputPort::Channels()
	{
	return iChannels;
	}	
	
TUint32 CDummyPcmRenderer::CInputPort::BufferSize() const
	{
	return iBufferSize;
	}

void CDummyPcmRenderer::CInputPort::Execute()
	{
	if (!IsActive() && iBuffers.Count()>0)
		{
		SetActive();
		TRequestStatus* status = &iStatus;
		User::RequestComplete(status, KErrNone);
		}
	}

void CDummyPcmRenderer::CInputPort::Pause()
	{
	if (iParent.SoundDevice().Handle())
		{
		iParent.SoundDevice().PausePlayBuffer();
		}
	}
	
void CDummyPcmRenderer::CInputPort::Stop()
	{
	Cancel();	
	}

void CDummyPcmRenderer::CInputPort::RunL()
	{

	if (iCurrentBuffer != NULL)
		{
		// If we've been signalled with a buffer, callback that we've completed the writing of the
		// buffer
		if (iParent.Observer())
			{		
			iParent.Observer()->BufferDelivered(this, iCurrentBuffer);
			if (iCurrentBuffer->LastBuffer())
				{
				TILEvent thisEvent;
				thisEvent.iEvent = EEventBufferFlag;
				thisEvent.iData1 = PortIndex();				
				thisEvent.iData2 = static_cast<TUint32>(KErrUnderflow); 
				iParent.Observer()->MsgFromILComponent(&iParent, thisEvent); 
				iParent.SoundDevice().Close();
				}
			}
		iCurrentBuffer = NULL;
		}
		
	// only process the next buffer if there is no error
	// error callbacks were handled in the previous block
	if (iStatus == KErrNone)
		{
		if (iBuffers.Count()>0)
			{
			iCurrentBuffer = iBuffers[0];
			iBuffers.Remove(0);
			
			if (CMMFBuffer::IsSupportedDataBuffer(iCurrentBuffer->Type()))
				{
 				TDes8& aBufferDes = (static_cast<CMMFDataBuffer*>(iCurrentBuffer))->Data();
 				iStatus = KRequestPending;
				iParent.SoundDevice().PlayData(iStatus, aBufferDes);
				SetActive();
				}
			}
		}
	else
		{
		// In the real implementation here should be sending an error event back to the client
		}
	}

void CDummyPcmRenderer::CInputPort::DoCancel()
	{
	if (iParent.SoundDevice().Handle())
		{
		iParent.SoundDevice().CancelPlayData();
		iParent.SoundDevice().FlushPlayBuffer();
		}
	}

// ------------------------------------------------------------------------------------------
// CDummyPcmRenderer Implementation
	
CDummyPcmRenderer::CDummyPcmRenderer()
	{
	}
	
CDummyPcmRenderer::~CDummyPcmRenderer()
	{
	delete iInputPort;
	iSoundDevice.Close();
	}
	
CDummyPcmRenderer* CDummyPcmRenderer::NewL()
	{
	CDummyPcmRenderer* self = new (ELeave) CDummyPcmRenderer;
	return self;
	}
	
void CDummyPcmRenderer::CreateComponentL(const TDesC8& /*aComponentName*/, MILIfObserver& aComponentIfObserver)
	{
	iObserver = &aComponentIfObserver;
	iInputPort = CInputPort::NewL(*this);
	iState = EComponentLoaded;
	}

void CDummyPcmRenderer::CreateComponentL(const TUid& /*aUid*/, MILIfObserver& /*aComponentIfObserver*/)	
	{
	User::LeaveIfError(KErrNotSupported);
	};

void CDummyPcmRenderer::ReleaseComponent()
	{
	delete this;	
	}

TInt CDummyPcmRenderer::GetComponentInputPorts(RPointerArray<MILComponentPortIf>& aComponentPorts) const
	{
	return aComponentPorts.Append(iInputPort);
	}

TInt CDummyPcmRenderer::GetComponentOutputPorts(RPointerArray<MILComponentPortIf>& /*aComponentPorts*/) const
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::SetConfig(const TILStruct& aConfig, const MILComponentPortIf* /*aComponentPort*/)
	{
	if (aConfig.Uid() == TUid::Uid(KUidMMFTTaskConfig))
		{
		const TTaskConfig* config = TILTaskConfig::GetStructure(aConfig);	
		ASSERT(config);
		iInputPort->iSampleRate = config->iRate;
		iInputPort->iChannels = (config->iStereoMode & ETaskMono)? 1 : 2;
		iInputPort->iInterleaved = (config->iStereoMode & ETaskInterleaved)?ETrue : EFalse;
		return KErrNone;
		}
	
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::GetConfig(TILStruct& /*aConfig*/, const MILComponentPortIf* /*aComponentPort*/) const
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::Initialize()
	{
	ASSERT(iObserver);
	
	TILEvent thisEvent;
	thisEvent.iEvent = EEventCmdComplete;
	thisEvent.iData1 = ECommandStateSet;				
	thisEvent.iData2 = EComponentIdle;
	iObserver->MsgFromILComponent(this, thisEvent); 

	iState = EComponentIdle;
	return KErrNone;	
	}
	
TInt CDummyPcmRenderer::UnInitialize()
	{
	ASSERT(iObserver);
	
	TILEvent thisEvent;
	thisEvent.iEvent = EEventCmdComplete;
	thisEvent.iData1 = ECommandStateSet;				
	thisEvent.iData2 = EComponentLoaded;
	iObserver->MsgFromILComponent(this, thisEvent); 

	iState = EComponentLoaded;
	return KErrNone;	
	}

TInt CDummyPcmRenderer::Execute()
	{
	ASSERT(iObserver);
	TInt err = KErrNone;
	if(!iSoundDevice.Handle())
		{
		err = iSoundDevice.Open();
		}
	
	RMdaDevSound::TCurrentSoundFormatBuf buf;
	if (err == KErrNone)
		{
		if(iState == EComponentPaused)
			{
			iSoundDevice.ResumePlaying();
			}
		else
			{
			// Set play format (for input port)
			iSoundDevice.GetPlayFormat(buf);
			buf().iRate = iInputPort->SampleRate();
			buf().iChannels = iInputPort->Channels();
			buf().iBufferSize = KBufferSize;
			buf().iEncoding = RMdaDevSound::EMdaSoundEncoding16BitPCM;
			err = iSoundDevice.SetPlayFormat(buf);
			}
		}
	iState = EComponentExecuting;	

	TILEvent thisEvent;
	thisEvent.iEvent = EEventCmdComplete;
	thisEvent.iData1 = ECommandStateSet;				
	thisEvent.iData2 = EComponentExecuting;
	iObserver->MsgFromILComponent(this, thisEvent); 

	return err;		
	}

TInt CDummyPcmRenderer::Pause()
	{
	iState = EComponentPaused;
	iInputPort->Pause();
	return KErrNone;
	}

TInt CDummyPcmRenderer::Stop()
	{
	if(iState == EComponentExecuting || iState == EComponentPaused)
		{
		// Cancel and flush the device driver		
		iInputPort->Stop();

		TILEvent thisEvent;
		thisEvent.iEvent = EEventCmdComplete;
		thisEvent.iData1 = ECommandStateSet;				
		thisEvent.iData2 = EComponentIdle;
		iObserver->MsgFromILComponent(this, thisEvent); 

		iState = EComponentIdle;
		
		// Close the sound device
		iSoundDevice.Close();
			
		return KErrNone;			
		}	
	else
		{
		// invalid state
		return EComponentInvalid;			
		}	
	}

TInt CDummyPcmRenderer::GetState(TILComponentState& aState) const
	{
	aState = iState;
	return KErrNone;	
	}
			
TInt CDummyPcmRenderer::SendCommand(const TILCommand& /*aCommand*/)
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::FlushAllPorts()
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::ComponentRoleEnum(TPtr8& /*aComponentRole*/, TUint32 /*aIndex*/) const
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::GetComponentVersion(TILComponentVersion& /*aVersion*/) const
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::SetComponentIfRole(const TDesC8& /*aComponentRole*/)
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::GetComponentIfRole(TDes8& /*aComponentRole*/) const
	{
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::SetComponentIfRole(const TUid& /*aFormat*/)
	{
	// This interface is specific writen to support only this PCM renderer role
	return KErrNotSupported;
	}

TInt CDummyPcmRenderer::GetComponentIfRole(TUid& aFormat) const
	{
	aFormat = TUid::Uid(KDummyPCM16FormatUid);
	return KErrNone;	
	}
	
TAny* CDummyPcmRenderer::CustomInterface(TUid aUid)
	{
	if (aUid.iUid == KMmfPlaySettingsCustomInterface)
		{
		return static_cast<MPlayCustomInterface*>(iInputPort);
		}
	return NULL;	
	}	

TInt CDummyPcmRenderer::SetExtConfig(const TDesC8& /*aParameterName*/, const TILStruct& /*aConfig*/, const MILComponentPortIf* /*aComponentPort*/)
	{
	return KErrNotSupported;		
	}

TInt CDummyPcmRenderer::GetExtConfig(const TDesC8& /*aParameterName*/, TILStruct& /*aConfig*/, const MILComponentPortIf* /*aComponentPort*/) const
	{
	return KErrNotSupported;		
	}


MILIfObserver* CDummyPcmRenderer::Observer() const
	{
	return iObserver;	
	}
		
RMdaDevSound& CDummyPcmRenderer::SoundDevice()
	{
	return iSoundDevice;
	}
	

// ------------------------------------------------------------------------------------------	
// ECOM Implementation table entry	
	
const TImplementationProxy ImplementationTable[] = 
	{
	IMPLEMENTATION_PROXY_ENTRY(KUidDummyPcmRendererMILIFPlugin,	CDummyPcmRenderer::NewL),
	};

EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
	{
	aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
	return ImplementationTable;
	}
