/***************************************************************************
                          Interface to access Amarok
                             -------------------
    begin                : Tue Dec  2 23:54:53 CET 2003
    copyright            : (c) 2003 by Thomas Capricelli
    adapted from juk* (C) 2001-2002 by Stefan Gehn (metz {AT} gehn {DOT} net)
    email                : orzel@freehackers.org
 ***************************************************************************/


/***************************************************************************
 *                                                                         *
 *   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 "amarokInterface.h"
#include "amarokInterface.moc"

#include <kdebug.h>
#include <tqstringlist.h>
#include <tqstrlist.h>
#include <kurldrag.h>

#define TIMER_FAST  250

AmarokInterface::AmarokInterface() : PlayerInterface()
{
   mTimerValue = TIMER_FAST;
   mAmarokTimer = new TQTimer ( this, "mAmaroKTimer" );

   connect(mAmarokTimer, TQT_SIGNAL(timeout()), TQT_SLOT(updateSlider()) );
   kapp->dcopClient()->setNotifications ( true );

   connect(kapp->dcopClient(), TQT_SIGNAL(applicationRegistered(const TQCString&)),
      TQT_SLOT(appRegistered(const TQCString&)) );

   connect(kapp->dcopClient(), TQT_SIGNAL(applicationRemoved(const TQCString&)),
      TQT_SLOT(appRemoved(const TQCString&)));

   TQTimer::singleShot(0, this, TQT_SLOT(myInit()));
}

AmarokInterface::~AmarokInterface()
{
   kapp->dcopClient()->setNotifications(false);
   delete mAmarokTimer;
}

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

void AmarokInterface::appRegistered ( const TQCString &appId )
{
   if(appId.contains("amarok",false) )
   {
      mAppId = appId;
      emit playerStarted();
      mAmarokTimer->start(mTimerValue);
   }
}

void AmarokInterface::appRemoved ( const TQCString &appId )
{
   if ( appId.contains("amarok",false) )
   {
       // is there still another amarok alive?
      if ( findRunningAmarok() )
         return;
      mAmarokTimer->stop();
      emit playerStopped();
      emit newSliderPosition(0,0);
   }
}

void AmarokInterface::updateSlider ( )
{
   // length/time in msecs, -1 means "no playobject in amarok"
   int len, time;
   TQByteArray data, replyData;
   TQCString replyType;

   if (!kapp->dcopClient()->call(mAppId, "player", "trackTotalTime()",data, replyType, replyData))
   {
      //kdDebug(90200) << "mediacontrol: DCOP communication Error" << endl;
      // -2 is an internal errornumber, might be used later
      len = -2;
   }
   else
   {
      TQDataStream reply(replyData, IO_ReadOnly);
      if (replyType == "int")
      {
         reply >> len;
      }
      else
      {
         //kdDebug(90200) << "mediacontrol: unexpected type of DCOP-reply" << endl;
         // -3 is an internal errornumber, might be used later
         len = -3;
      }
   }

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

   if (!kapp->dcopClient()->call(mAppId, "player", "trackCurrentTime()",data, replyType, replyData))
   {
      //kdDebug(90200) << "mediacontrol: DCOP communication error" << endl;
      time = -2;
   }
   else
   {
      TQDataStream reply(replyData, IO_ReadOnly);
      if (replyType == "int")
      {
         reply >> time;
      }
      else
      {
         //kdDebug(90200) << "mediacontrol: unexpected type of DCOP-reply" << endl;
         time = -3;
      }
   }

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

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

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

void AmarokInterface::dropEvent(TQDropEvent* event)
{
   kdDebug(90200) << "AmarokInterface::dropEvent()" << endl;
   KURL::List list;
   if (KURLDrag::decode(event, list))
   {
      TQByteArray data;
      TQDataStream arg(data, IO_WriteOnly);
      arg << list;
      if (!kapp->dcopClient()->send(mAppId, "player", "addMediaList(KURL::List)",data))
         kdDebug(90200) << "Couldn't send drop to amarok" << endl;
   }
}

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

void AmarokInterface::sliderStartDrag()
{
   mAmarokTimer->stop();
}

void AmarokInterface::sliderStopDrag()
{
   mAmarokTimer->start(mTimerValue);
}

void AmarokInterface::jumpToTime( int sec )
{
   TQByteArray data;
   TQDataStream arg(data, IO_WriteOnly);
   arg << sec;
   kapp->dcopClient()->send(mAppId, "player", "seek(int)", data);
}

void AmarokInterface::playpause()
{
   if (!findRunningAmarok())
       startPlayer("amarok");
   TQByteArray data;
   kapp->dcopClient()->send(mAppId, "player", "playPause()", data);
}

void AmarokInterface::stop()
{
   TQByteArray data;
   kapp->dcopClient()->send(mAppId, "player", "stop()", data);
}

void AmarokInterface::next()
{
   TQByteArray data;
   kapp->dcopClient()->send(mAppId, "player", "next()", data);
}

void AmarokInterface::prev()
{
   TQByteArray data;
   kapp->dcopClient()->send(mAppId, "player", "prev()", data);
}

void AmarokInterface::volumeUp()
{
   TQByteArray data;
   kapp->dcopClient()->send(mAppId, "player", "volumeUp()", data);
}

void AmarokInterface::volumeDown()
{
   TQByteArray data;
   kapp->dcopClient()->send(mAppId, "player", "volumeDown()", data);
}

const TQString AmarokInterface::getTrackTitle() const
{
   TQString title;
   TQByteArray data, replyData;
   TQCString replyType;
   if (!kapp->dcopClient()->call(mAppId, "player", "nowPlaying()",data, replyType, replyData))
   {
      //kdDebug(90200) << "mediacontrol: DCOP communication Error" << endl;
      return TQString("");
   }
   else
   {
      TQDataStream reply(replyData, IO_ReadOnly);
      if (replyType == TQSTRING_OBJECT_NAME_STRING)
      {
         reply >> title;
         return title;
      }
      else
      {
         //kdDebug(90200) << "mediacontrol: unexpected type of DCOP-reply" << endl;
         return TQString("");
      }
   }
}

bool AmarokInterface::findRunningAmarok()
{
   QCStringList allApps = kapp->dcopClient()->registeredApplications();
   TQValueList<TQCString>::const_iterator iterator;
   TQByteArray data, replyData;
   TQCString replyType;

   for (iterator = allApps.constBegin(); iterator != allApps.constEnd(); ++iterator)
   {
      if ((*iterator).contains("amarok",false))
      {
         if (kapp->dcopClient()->call((*iterator), "player", "interfaces()", data, replyType, replyData) )
    {
            if ( replyType == "QCStringList" )
            {
               TQDataStream reply(replyData, IO_ReadOnly);
               QCStringList list;
               reply >> list;

               if ( list.contains("AmarokPlayerInterface") )
               {
                  kdDebug(90200) << "mediacontrol: amarok found" << endl;
                  mAppId = *iterator;
                  return true;
               }
            }
         }
      }
   }
   kdDebug(90200) << "mediacontrol: amarok not found" << endl;
   return false;
}


int AmarokInterface::playingStatus()
{
   TQByteArray data, replyData;
   TQCString replyType;

   if (kapp->dcopClient()->call(mAppId, "player", "status()", data, replyType,
      replyData))
   {
      int status = 0;
      TQDataStream reply(replyData, IO_ReadOnly);
      if (replyType == "int")
         reply >> status;
      if (status == 2)
         return Playing;
      else if (status == 1)
         return Paused;
   }
   return Stopped;
}