/* NMM - Network-Integrated Multimedia Middleware
 *
 * Copyright (C) 2006
 *               Motama GmbH, Saarbruecken, Germany
 *
 * Maintainer: Bernhard Fuchshumer <fub@motama.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License only.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 */

#include "bytestream.h"
#include "backend.h"

#include <nmm/interfaces/general/IByteStream.hpp>
#include <nmm/base/memorymgr/TransferBuffer2.hpp>
#include <nmm/base/ProxyObject.hpp>

#include <iostream>
#include <kdebug.h>

namespace Phonon
{
namespace nmm
{
ByteStream::ByteStream( Backend* backend, QObject* parent )
	: AbstractMediaProducer( backend, parent )
	, m_bufferSize( 0 )
	, m_streamPosition( 0 )
	, m_backend( backend )
    , m_iByteStream(0)
	, m_sinkInitDone(false)
{
	kDebug(1234) << k_funcinfo << endl;
	///tick a virtual signal emitted in AbstractMediaProducer
	connect( this, SIGNAL( tick( qint64 ) ), SLOT( consumeStream() ) );
	setState( Phonon::LoadingState );

	deinitGraphHandler();
	initGraphHandler();

	try
	{
		m_iByteStream=m_graphHandler->createByteStreamNode();
		m_iByteStream->setMaxSize(1024*1024);
		NMM::INode* inode(m_iByteStream->getParentObject()->getCheckedInterface<NMM::INode>());
		inode->init();
		//when stage1 is reached audio and video paths can be added to the
		//CompositeNode this is done on ...
	}
	catch(...)
	{
		//TODO add better error msg
		kDebug(1234) << "NMM's GraphHandler failed creating ByteStreamNode" << endl;
		setState( Phonon::ErrorState );
	}

}

ByteStream::~ByteStream()
{
	kDebug(1234) << k_funcinfo << endl;
	if (m_iByteStream) delete m_iByteStream;
}

qint64 ByteStream::currentTime() const
{
	return 0;
}

qint64 ByteStream::totalTime() const
{
	return AbstractMediaProducer::totalTime(); 
}

qint32 ByteStream::aboutToFinishTime() const
{
	kDebug(1234) << k_funcinfo << endl;
	return 0;
}

qint64 ByteStream::streamSize() const
{
	kDebug(1234) << k_funcinfo << endl;
	return 0;
}

bool ByteStream::streamSeekable() const
{
	kDebug(1234) << k_funcinfo << endl;
	return m_streamSeekable;
}

void ByteStream::setStreamSeekable( bool s )
{
	kDebug(1234) << k_funcinfo << endl;
	m_streamSeekable = s;
}

void ByteStream::reachStage1()
{
	
		if ( !m_sinkInitDone )
		{
			NMM::INode* inode(m_iByteStream->getParentObject()->getCheckedInterface<NMM::INode>());
			try
			{
				inode->initOutput();
				m_sinkInitDone=true;
			}
			catch(...){	}
		}

		if (m_sinkInitDone && ! m_stage1Reached ) //setup the GH here
		{
			try
			{
				m_graphHandler->stage1_bs();
				//when stage1 is reached audio and video paths can be added to the
				//CompositeNode this is done on ...
				stage1Reached();
			}
			catch(...)
			{
				//TODO add better error msg
				kDebug(1234) << "NMM's GraphHandler failed" << endl;
				setState( Phonon::ErrorState );
			}
		}
}

void ByteStream::writeData( const QByteArray& data )
{
	kDebug(1234) << k_funcinfo << endl;
	Q_ASSERT( ! m_eod );
	Q_ASSERT( m_iByteStream );

	if (!data.size()) 
		return;

	double percentFill=1;
	if ( m_iByteStream->getMaxSize() > m_iByteStream->getSize() )
		percentFill = 1-(m_iByteStream->getMaxSize()-m_iByteStream->getSize())/static_cast<double>(m_iByteStream->getMaxSize());

	TransferBuffer2 tb;
	kDebug(1234) << "Size="<<data.size() << endl;
	tb.setData(data.data(),data.size());

	m_iByteStream->sendBuffer2(tb);
	kDebug(1234)<<"     ByteStreamNode full at "<<percentFill*100<<"%"<<endl;
	kDebug(1234)<<"     ByteStreamNode has "<<m_iByteStream->getSize()<<" bytes. Max is "<<m_iByteStream->getMaxSize()<<endl;

#ifdef __GNUC__
#warning You probably do not need to fill the buffer to the max before starting playback, you need to find the optimum between fast buffering and as few going into BufferingState as possible
#endif
	if (percentFill==1)
	{
		reachStage1();
		
		if (m_stage1Reached && m_sinkInitDone) //useful check: can change above
		{
			if( state() == Phonon::BufferingState )
				AbstractMediaProducer::play();
			if( state() == Phonon::LoadingState )
				setState( Phonon::StoppedState );
		}

		kDebug(1234)<<"     buffer full: emit enoughData()"<<endl;
		emit enoughData();
	}

}

void ByteStream::setStreamSize( qint64 s )
{
	kDebug(1234) << k_funcinfo << endl;
	m_streamSize = s;
	emit length( totalTime() );
}

void ByteStream::endOfData()
{
	kDebug(1234) << "############## "<<k_funcinfo << endl;
	m_eod=true;
	reachStage1();
	if( state() == Phonon::BufferingState )
		play();

	///Sending an empty tb to ByteStreamNode makes it send end of track evenk
	TransferBuffer2 tb;
	m_iByteStream->sendBuffer2(tb);
}

void ByteStream::setAboutToFinishTime( qint32 t )
{
	kDebug(1234) << k_funcinfo << endl;
	m_aboutToFinishBytes = t;
}

void ByteStream::play()
{
	kDebug(1234) << k_funcinfo << endl;
	if (!m_stage1Reached || ! m_sinkInitDone)
	{
		kDebug(1234)<<"Not playing because ByteStramNode is not initialized"<<endl;
		setState(Phonon::BufferingState);
		return;
	}
	AbstractMediaProducer::play();
}

void ByteStream::pause()
{
	kDebug(1234) << k_funcinfo << endl;
	if( state() == Phonon::LoadingState || state() == Phonon::StoppedState)
	{
		kDebug(1234)<<"Not pausing:"<<state()<<endl;
		return;
	}

	AbstractMediaProducer::pause();
}

void ByteStream::stop()
{
	kDebug(1234) << k_funcinfo << endl;
	m_eod=false;
	if( state() == Phonon::LoadingState || ! m_stage1Reached || ! m_sinkInitDone)
		return;

	AbstractMediaProducer::stop();
	m_sinkInitDone=false;
}

bool ByteStream::isSeekable() const
{
	return AbstractMediaProducer::isSeekable();
}

void ByteStream::seek( qint64 time )
{
	kDebug(1234) << k_funcinfo <<" Not working jet!"<< endl;
	return;
	
}

void ByteStream::consumeStream()
{
	kDebug(1234) << k_funcinfo <<endl;
	double percentFill=1;
	if (m_iByteStream->getMaxSize()>m_iByteStream->getSize())
		percentFill = 1-(m_iByteStream->getMaxSize()-m_iByteStream->getSize())/static_cast<double>(m_iByteStream->getMaxSize());
	kDebug(1234)<<m_iByteStream->getSize()<<"<<<<<<<< buffer at "<<percentFill*100<<"% >>>>>>>>"<<m_iByteStream->getMaxSize() <<endl;
	switch( state() )
	{
		case Phonon::LoadingState:
		case Phonon::ErrorState:
		case Phonon::StoppedState:
		case Phonon::PausedState:
			return;
		case Phonon::BufferingState:
			if( percentFill > 0.10 || m_eod) //resume from buffering
				AbstractMediaProducer::play();
			break;
		case Phonon::PlayingState:
			break;
	}
	
	if( m_eod )
	{
		return;
	}
	if(percentFill < 0.50) // don't let use it too much
	{
		kDebug(1234)<<"     emit needData()"<<endl;
		emit needData();
	}
}

void ByteStream::setTrackDuration(qint64 duration ) 
{ 
    m_trackDuration=duration*0; 
}

}} //namespace Phonon::nmm

#include "bytestream.moc"
// vim: sw=4 ts=4 tw=80 noet
