/**
 * audio_player.c: The audio player
 * 
 * Copyright: See COPYING file that comes with this distribution
 *
 * Author: Matthias Feistel <hitman_47@users.sourceforge.net>, (C) 2007
 */
/*
 * audio_player.c: The audio player and control interface
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: audio_player.c,v 1.5 2005/12/23 09:24:48 cm Exp $
 */

#include <limits.h>
#include <vdr/status.h>
#include "audio_player.h"
#include "audio_encap.h"
#include "config.h"
#include "settings.h"
#include "audio_playlist.h" // GetNextsongurl
#include <sstream> // cWebServiceConnectorScrobbler::Submission
#include "web_serviceconnector_scrobbler.h" // cWebServiceConnectorScrobbler::Submission
#include <time.h>

cAudioPlayer::cAudioPlayer( void ) :
	cPlayer(pmAudioOnly)
{
	SetDescription( PLUGIN_NAME"-AudioPlayer" );
	mpo_ringBufferFrame = new cRingBufferFrame( PLAYER_BUFSIZE );
	cDevice::PrimaryDevice()->SetCurrentAudioTrack( ttAudio );
	m_playMode = pmNone;
	
	mpo_playFrame = 0;
	httpmp3decoder = 0;
	
	cSettings::GetInstance().Mp3decoderstate = 1;
	
	Start( );
}

cAudioPlayer::~cAudioPlayer( void )
{
	Cancel( MAXIMUM_THREADKILLTIME );
	Stop( );
	//////	Detach( );

	if( httpmp3decoder )
		DELETENULL( httpmp3decoder );
	
	if( mpo_ringBufferFrame )
		DELETENULL( mpo_ringBufferFrame );
	
	cSettings::GetInstance().Mp3decoderstate = 0;
}

void cAudioPlayer::Action( void )
{
	uint8_t *b = NULL, *data = new uint8_t[ MAX_FRAME_PAYLOAD ];
	int bc = 0;
	cEncapsulator oEncapsulator;
	cPoller oPoller;
	mpo_readFrame = 0;
	
	oEncapsulator.SetPesParameters( 0xBD, 0x8700 );
	
	while( Running( ) && (m_playMode != pmStopped || !DeviceFlush( POLLTIMEOUT
			|| mpo_ringBufferFrame->Available( ) ) ) )
	{
		if( !httpmp3decoder )
			Next( );


		if( DevicePoll( oPoller, POLLTIMEOUT ) && httpmp3decoder )
		{
			h.Select( 0, 3 );

			
			
			// Frame to be read next time from source will be requested:
			if( !mpo_readFrame )
			{
				int len = 0;
				
				
				// Get DATA from source:
				len = httpmp3decoder->GetMp3Data( data, MAX_FRAME_PAYLOAD );
				
				if( httpmp3decoder->IsConnected( ) &&
						!httpmp3decoder->IsMp3DataAvailable( ) )
				{
					time_t time_lastconnloss = time( 0 );
					int secondstobuffer = PLAYER_FAILURETIMEOUTINTERVAL;
					
					while( h.Valid( httpmp3decoder ) &&
							( time_lastconnloss + secondstobuffer)>= time( 0 ) )
					{
						h.Select( 0, 3 );
						cCondWait::SleepMs( 3 ); // avoid high cpuload
					}
					
				}
				
				if( !httpmp3decoder->IsConnected( )
						&&!httpmp3decoder->IsMp3DataAvailable( ) )
				{
					Next( );
				}
				else if( len )
				{
					oEncapsulator.SetPcmParameters( 2, 44100 );
					bc = oEncapsulator.Data( (uint8_t *)data, len );
					bc = oEncapsulator.ToPcm( (uint8_t **)&b );
					bc = oEncapsulator.ToPes( (uint8_t **)&b );
					
					mpo_readFrame = new cFrame(b, bc, ftUnknown);
					b = NULL;
					bc = 0;
				}
				
			}
			
			// If a new source frame has been found, it will be moved to the RingBuffer:
			if( mpo_readFrame )
			{
				if( mpo_ringBufferFrame->Put( mpo_readFrame ) )
					mpo_readFrame = NULL;
			}
			
			
			// playFrame is the Frame to played with DVBoutput:
			if( !mpo_playFrame )
			{
				mpo_playFrame = mpo_ringBufferFrame->Get( );
				b = NULL;
				bc = 0;
			}
			
			
			// when we have got a new playFrame, we will move it to the PESPLAYER (DVBouptut):
			if( mpo_playFrame )
			{
				if( !b )
				{
					b = mpo_playFrame->Data( );
					bc = mpo_playFrame->Count( );
				}
				if( b )
				{
					int i = 0;
					i = PlayPes( b, bc, false );
					if( 0 < i )
					{
						b += i;
						bc -= i;
					}
				}
				if( !bc )
				{
					mpo_ringBufferFrame->Drop( mpo_playFrame );
					mpo_playFrame = NULL;
					b = NULL;
				}
			}
			
		} // if (DevicePoll ...

		cCondWait::SleepMs( 3 ); // avoid high cpuload

	} // while (Running())

}

void cAudioPlayer::WaitPlayMode( ePlayMode test, bool negate, ePlayMode wait )
{
	mo_playerMutex.Lock( );
	// negate == false: test == m_playMode
	// negate == true : test != m_playMode

	if( (test == m_playMode) ^ negate )
	{
		while( m_playMode != wait && m_playMode != pmStopped )
		{
			mo_playerCond.Wait( mo_playerMutex );
		}
	}
	
	mo_playerMutex.Unlock( );
}

void cAudioPlayer::SetPlayMode( ePlayMode mode )
{
	mo_playerMutex.Lock( );
	m_playMode = mode;
	mo_playerCond.Broadcast( );
	mo_playerMutex.Unlock( );
}

void cAudioPlayer::Clear( void )
{
	LOCK_THREAD;
	SetPlayMode( pmNone );
	mpo_ringBufferFrame->Clear( );
	mpo_readFrame = NULL;
	mpo_playFrame = NULL;
	DeviceClear( );
}

void cAudioPlayer::Reset( void )
{
	LOCK_THREAD;
	Clear( );
}

void cAudioPlayer::Play( void )
{
	SetPlayMode( pmPlay );
	DevicePlay( );
}

void cAudioPlayer::Pause( void )
{
	if( pmPaused == m_playMode )
	{
		Play( );
	}
	else
	{
		SetPlayMode( pmPaused );
		DeviceFreeze( );
	}
}

void cAudioPlayer::Stop( void )
{
	// SetPlayMode() must be called after Reset()
	Reset( );
	SetPlayMode( pmStopped );
}

void cAudioPlayer::Next( void )
{
	LOCK_THREAD;

	std::string host("");
	port_t port = 0;
	
	
	// chech, whether we shall scrobble:
	std::stringstream sstream_tmp;
	bool scrobble;
	
	sstream_tmp.clear( );
	sstream_tmp.str( "" );
	sstream_tmp << cSettings::GetInstance().ParameterMap[ "Scrobblemode" ];
	sstream_tmp >> scrobble;
	
	
	////////////////
	// before skipping song, we try to scrobble it:
	std::string data_npurl, data_protourl, data_sessionid;
	
	data_npurl = cSettings::GetInstance().HandshakedataMap[ "Now-Playing URL" ];
	data_protourl = cSettings::GetInstance().HandshakedataMap[ "Submission URL" ];
	data_sessionid = cSettings::GetInstance().HandshakedataMap[ "Session ID" ];
	
	if( scrobble )
	{
		
		
		// if not handshaked with protocol v1.2, yet:
		if( data_sessionid.compare( "" ) == 0||data_sessionid.empty( ) )
		{
			
			cWebServiceConnectorScrobbler::HandshakeV12(
					cSettings::GetInstance().ParameterMap[ "Username" ],
					cSettings::GetInstance().ParameterMap[ "Password_md5" ], data_npurl,
					data_protourl, data_sessionid );
			
			
			// assign Session ID (version 1.2) globally, so it does not recalled next time:
			cSettings::GetInstance().HandshakedataMap[ "Session ID" ] = data_sessionid;
			cSettings::GetInstance().HandshakedataMap[ "Submission URL" ] = data_protourl;
			cSettings::GetInstance().HandshakedataMap[ "Now-Playing URL" ] = data_npurl;
		}
		
		if( data_sessionid.compare( "" ) != 0 )
		{
			int position, total;
			
			GetIndex( position, total );
			
			std::stringstream track_lastfm_trackauth;
			std::string track_lastfm_trackauth_str;
			
			if( !cAudioPlaylist::GetInstance().trackList.empty( ) &&!cAudioPlaylist::GetInstance().trackList.front().empty( ) &&cAudioPlaylist::GetInstance().trackList.front().begin()->first&&cAudioPlaylist::GetInstance().trackList.front().begin( ) !=cAudioPlaylist::GetInstance().trackList.front().end( ) )
			{
				track_lastfm_trackauth_str = cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_lastfm_trackauth" ];
				
				if( track_lastfm_trackauth_str.compare( "" ) != 0 )
				{
					track_lastfm_trackauth << "L"<< track_lastfm_trackauth_str;
					
					
					// time:
					std::string track_duration_str;
					std::stringstream sstream_tmp(cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_duration" ]);
					sstream_tmp >> total;
					sstream_tmp >> track_duration_str;
					total = total / 1000; //lastfm delivers tracklength in ms
					

					cWebServiceConnectorScrobbler::Submission( data_protourl,
							data_sessionid, cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_creator" ],
							cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_title" ],
							cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_duration" ],
							track_lastfm_trackauth.str( ), position, total,
							cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_album" ] );
				}
				
			}
			
		}
		
	} // if scrobble
	///////////////////////
	

	if( httpmp3decoder )
		DELETENULL ( httpmp3decoder );
	
	
	
	// Get new songurl:
	std::string url(cAudioPlaylist::GetInstance().GetNextsongurl( ) );

	if(url.compare("") == 0 )
		return;

	httpmp3decoder = new cHttpMp3Decoder( h );
	
	
	
	sstream_tmp.clear( );
	sstream_tmp.str( "" );
	sstream_tmp << cSettings::GetInstance().ParameterMap[ "Scrobblemode" ];
	sstream_tmp >> scrobble;
	
	if( scrobble )
	{
		/////////////////////// Now-Playing:
		if( data_sessionid.compare( "" ) != 0 )
		{
			int position, total;
			
			GetIndex( position, total );
			
			std::stringstream track_lastfm_trackauth;
			std::string track_lastfm_trackauth_str;
			
			if( !cAudioPlaylist::GetInstance().trackList.empty( ) &&!cAudioPlaylist::GetInstance().trackList.front().empty( ) &&cAudioPlaylist::GetInstance().trackList.front().begin()->first&&cAudioPlaylist::GetInstance().trackList.front().begin( ) !=cAudioPlaylist::GetInstance().trackList.front().end( ) )
			{
				track_lastfm_trackauth_str = cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_lastfm_trackauth" ];
				
				if( track_lastfm_trackauth_str.compare( "" ) != 0 )
				{
					// time:
					std::string track_duration_str;
					std::stringstream sstream_tmp(cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_duration" ]);
					sstream_tmp >> total;
					
					sstream_tmp.clear( );
					sstream_tmp.str( "" );
					sstream_tmp << total / 1000; // lastfm delivers tracklength in ms
					sstream_tmp >> track_duration_str;
					
					cWebServiceConnectorScrobbler::NowPlaying( data_npurl,
							data_sessionid, cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_creator" ],
							cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_title" ],
							cAudioPlaylist::GetInstance().trackList.front().begin()->second[ "track_album" ],
							track_duration_str );
				}
				
			}
			
		}
		
	} // if scrobble
	//////////////////////////////////////
	
	// we cAudioPlayer handles construction and destruction of httpmp3decoder
	httpmp3decoder->SetDeleteByHandler(false);

	httpmp3decoder->SetUrl( url );
	
	httpmp3decoder->Url( url, host, port );
	
	httpmp3decoder->Open( host, port );
	
	h.Add( httpmp3decoder );
	
	h.Select( ); // wait until first event happened
}

bool cAudioPlayer::GetIndex( int &Current, int &Total, bool SnapToIFrame )
{
	if( httpmp3decoder )
		return httpmp3decoder->GetIndex( Current, Total );
	
	return false;
}

