/***************************************************************************
                jukInterface.cpp  -  Interface to access JuK
                             -------------------
    begin                : Mon Jan 15 21:09:00 CEST 2001
    copyright            : (C) 2000-2002 by Stefan Gehn
    email                : sgehn@gmx.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "jukInterface.h"
#include "jukInterface.moc"

#include <kapplication.h>
#include <kdebug.h>
#include <qstringlist.h>
#include <qstrlist.h>
#include <kurldrag.h>

#define TIMER_FAST  250

JuKInterface::JuKInterface() : PlayerInterface()
{
	mTimerValue = TIMER_FAST;
	mJuKTimer = new QTimer ( this, "mJukTimer" );

	connect(mJuKTimer, SIGNAL(timeout()), SLOT(updateSlider()) );
	kapp->dcopClient()->setNotifications ( true );

	connect(kapp->dcopClient(), SIGNAL(applicationRegistered(const QCString&)),
		SLOT(appRegistered(const QCString&)) );

	connect(kapp->dcopClient(), SIGNAL(applicationRemoved(const QCString&)),
		SLOT(appRemoved(const QCString&)));

	QTimer::singleShot(0, this, SLOT(myInit()));
}

JuKInterface::~JuKInterface()
{
	kapp->dcopClient()->setNotifications(false);
	delete mJuKTimer;
}

void JuKInterface::myInit()
{
	// Start the timer if juk is already running
	// Needed if user adds applet while running juk
	if ( findRunningJuK() )
	{
		emit playerStarted();
		mJuKTimer->start(mTimerValue);
	}
	else
	{
		emit playerStopped();
		emit newSliderPosition(0,0);
	}
}

void JuKInterface::appRegistered ( const QCString &appId )
{
	if(appId.contains("juk",false) )
	{
		mAppId = appId;
		emit playerStarted();
		mJuKTimer->start(mTimerValue);
	}
}

void JuKInterface::appRemoved ( const QCString &appId )
{
	if ( appId.contains("juk",false) )
	{
	 	// is there still another juk alive?
		if ( findRunningJuK() )
			return;
		mJuKTimer->stop();
		emit playerStopped();
		emit newSliderPosition(0,0);
	}
}

void JuKInterface::updateSlider ()
{
	// length/time in msecs, -1 means "no playobject in juk"
	int len = -1;
	int time = -1;
	QByteArray data, replyData;
	QCString replyType;

	if (kapp->dcopClient()->call(mAppId, "Player", "totalTime()", data,
		replyType, replyData))
	{
		QDataStream reply(replyData, IO_ReadOnly);
		if (replyType == "int")
			reply >> len;
	}

	data = 0;
	replyData = 0;
	replyType = 0;

	if (kapp->dcopClient()->call(mAppId, "Player", "currentTime()", data,
		replyType, replyData))
	{
		QDataStream reply(replyData, IO_ReadOnly);
		if (replyType == "int")
			reply >> time;
	}

	if ( (time < 0) || (len < 0)) // JuK isn't playing and thus returns -1
	{
		len = 0;
		time = 0;
	}
	emit ( newSliderPosition(len,time) );
	emit playingStatusChanged(playingStatus());
}

// Drag-n-Drop stuff =================================================================

void JuKInterface::dragEnterEvent(QDragEnterEvent* event)
{
//	kdDebug(90200) << "JuKInterface::dragEnterEvent()" << endl;
	event->accept( KURLDrag::canDecode(event) );
}

void JuKInterface::dropEvent(QDropEvent* event)
{
//	kdDebug(90200) << "JuKInterface::dropEvent()" << endl;
	KURL::List list;
	if (KURLDrag::decode(event, list))
	{
		QByteArray data, replyData;
		QStringList fileList;
		QCString replyType;
		QDataStream arg(data, IO_WriteOnly);

		// Juk doesn't handle KURL's yet, so we need to form a list
		// that contains the local paths.
		for (KURL::List::ConstIterator it = list.begin(); it != list.end(); ++it)
			fileList += (*it).path();

		arg << fileList << false;

		// Use call instead of send to make sure the files are added
		// before we try to play.
		if (!kapp->dcopClient()->call(mAppId, "Collection", "openFile(QStringList)", data,
			      replyType, replyData, true))
		{
			kdDebug(90200) << "Couldn't send drop to juk" << endl;
		}

		// Apparently we should auto-play?
		QByteArray strData;
		QDataStream strArg(strData, IO_WriteOnly);
		strArg << *fileList.begin();

		if (!kapp->dcopClient()->send(mAppId, "Player", "play(QString)", strData))
			kdDebug(90200) << "Couldn't send play command to juk" << endl;
	}
}

// ====================================================================================

void JuKInterface::sliderStartDrag()
{
	mJuKTimer->stop();
}

void JuKInterface::sliderStopDrag()
{
	mJuKTimer->start(mTimerValue);
}

void JuKInterface::jumpToTime( int sec )
{
	QByteArray data;
	QDataStream arg(data, IO_WriteOnly);
	arg << sec;
	// Used in JuK shipping with KDE < 3.3
	//kapp->dcopClient()->send(mAppId, "Player", "setTime(int)", data);
	kapp->dcopClient()->send(mAppId, "Player", "seek(int)", data);
}

void JuKInterface::playpause()
{
	if (!findRunningJuK())
		startPlayer("juk");
	QByteArray data;
	kapp->dcopClient()->send(mAppId, "Player", "playPause()", data);
}

void JuKInterface::stop()
{
	QByteArray data;
	kapp->dcopClient()->send(mAppId, "Player", "stop()", data);
}

void JuKInterface::next()
{
	QByteArray data;
	kapp->dcopClient()->send(mAppId, "Player", "forward()", data);
}

void JuKInterface::prev()
{
	QByteArray data;
	kapp->dcopClient()->send(mAppId, "Player", "back()", data);
}

void JuKInterface::volumeUp()
{
   QByteArray data;
   kapp->dcopClient()->send(mAppId, "Player", "volumeUp()", data);
}

void JuKInterface::volumeDown()
{
   QByteArray data;
   kapp->dcopClient()->send(mAppId, "Player", "volumeDown()", data);
}

const QString JuKInterface::getTrackTitle() const
{
	QString title;
	QByteArray data, replyData;
	QCString replyType;

	if (kapp->dcopClient()->call(mAppId, "Player", "playingString()",data,
		replyType, replyData))
	{
		QDataStream reply(replyData, IO_ReadOnly);
		if (replyType == "QString")
		{
			reply >> title;
			return title;
		}
	}
	return QString("");
}

// FIXME: what if we have a dcop app named, let's say, 'jukfrontend'?
bool JuKInterface::findRunningJuK()
{
	QCStringList allApps = kapp->dcopClient()->registeredApplications();
	QValueList<QCString>::iterator iterator;

	for (iterator = allApps.begin(); iterator != allApps.end(); iterator++)
	{
		if ((*iterator).contains("juk",false))
		{
			mAppId = *iterator;
			return true;
		}
	}
	return false;
}

int JuKInterface::playingStatus()
{
	QByteArray data, replyData;
	QCString replyType;

	if (kapp->dcopClient()->call(mAppId, "Player", "status()", data, replyType,
		replyData))
	{
		int status = 0;
		QDataStream reply(replyData, IO_ReadOnly);
		if (replyType == "int")
			reply >> status;

		if (status == 2)
			return Playing;
		else if (status == 1)
			return Paused;
	}

	return Stopped;
}
