/***************************************************************************
 *   Copyright (C) 2007 by Sébastien Laoût                                 *
 *   slaout@linux62.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.                                   *
 *                                                                         *
 *   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 "informationpoller.h"
#include "playerinformation.h"
#include "dcopinterface.h"

#include <kapplication.h>
#include <qevent.h>
#include <qregexp.h>
#include <qdir.h>
#include <kstandarddirs.h>

//
#include <qtimer.h>

#include <iostream>

const int InformationPoller::NEW_INFO_EVENT     = QEvent::User + 1;
const int InformationPoller::NEW_RATING_EVENT   = QEvent::User + 2;
const int InformationPoller::NEW_PROGRESS_EVENT = QEvent::User + 3;

InformationPoller::InformationPoller()
// : QThread()
 : QObject(),
   m_failureCounter(0)
{
	m_refreshTimer = new QTimer();
	connect( m_refreshTimer, SIGNAL(timeout()), this, SLOT(poll()) );
	m_refreshTimer->start( 333, false );
}

InformationPoller::~InformationPoller()
{
}

void InformationPoller::run()
{
	while (true) {
		poll();
//		msleep(333);
	}
}

void InformationPoller::poll()
{
	PlayerInformation *infos = PlayerInformation::instance();
	bool success = true;

	// Get primary information:
	int     status    = DcopInterface::getInt(    "amarok", "player", "status()",    &success );
	QString path      = DcopInterface::getString( "amarok", "player", "path()",      &success );
	int     rating    = DcopInterface::getInt(    "amarok", "player", "rating()",    &success );

	if (!success) {
		failed();
		return;
	}

	// We started or changed track: ask every information again:
	if (status != infos->status() || path != infos->path()) {
		QString artist    = DcopInterface::getString( "amarok", "player", "artist()",           &success );
		QString album     = DcopInterface::getString( "amarok", "player", "album()",            &success );
		QString year      = DcopInterface::getString( "amarok", "player", "year()",             &success );
		QString track     = DcopInterface::getString( "amarok", "player", "track()",            &success );
		QString title     = DcopInterface::getString( "amarok", "player", "title()",            &success );
		int     duration  = DcopInterface::getInt(    "amarok", "player", "trackTotalTime()",   &success );
		int     position  = DcopInterface::getInt(    "amarok", "player", "trackCurrentTime()", &success );

		if (!success) {
			failed();
			return;
		}

		// If the track is of a different album than the previous one, then we need to get the new cover:
		if (artist != infos->artist() || album != infos->album())
			if (status == PlayerInformation::Stopped)
				infos->setCoverPixmap(QPixmap());
			else
				retreiveNewCover(artist, album, &success);
//		else
//			PlayerInformation::instance()->emitAboutToChangeTrack(isPlaying); // An hack because it's not multi-threaded yet

		if (!success) {
			failed();
			return;
		}

		infos->emitAboutToChangeTrack(status, infos->isPrevious(path)); // An hack because it's not multi-threaded yet

		// Change every information at once (the cover has been set at the very end of retreiveNewCover()):
		infos->setStatus((PlayerInformation::Status) status);
		infos->setPath(path);
		infos->setArtist(artist);
		infos->setAlbum(album);
		infos->setYear(year);
		infos->setTrack(track);
		infos->setTitle(title);
		infos->setRating(rating);
		infos->setDuration(duration);
		infos->setPosition(position);

		// Inform listeners that information have changed:
		//kapp->postEvent(infos, new QCustomEvent(NEW_INFO_EVENT));
		infos->customEvent(new QCustomEvent(NEW_INFO_EVENT));
	} else {
		if (rating != infos->rating()) {
			infos->setRating(rating);
			//kapp->postEvent(infos, new QCustomEvent(NEW_RATING_EVENT));
			infos->customEvent(new QCustomEvent(NEW_RATING_EVENT));
		}
		bool success = true;
		int position = DcopInterface::getInt( "amarok", "player", "trackCurrentTime()", &success );
		if (success && position != infos->position()) {
			infos->setPosition(position);
			//kapp->postEvent(infos, new QCustomEvent(NEW_PROGRESS_EVENT));
			infos->customEvent(new QCustomEvent(NEW_PROGRESS_EVENT));
		}
	}

	if (success)
		m_failureCounter = 0;
	else
		failed();
}

void InformationPoller::failed()
{
	m_failureCounter++;

//	std::cout << "DCOP Call Failed -- Canceling this information retreiving." << std::endl;

	PlayerInformation *infos = PlayerInformation::instance();
	// Ask if Amarok is dead after 0.6 seconds (3 pollings per second):
	if (infos->isPlaying() && m_failureCounter > 2 && !DcopInterface::isApplicationRegistered("amarok")) {
		infos->setStatus(PlayerInformation::Stopped);
		infos->setPath("");
		infos->setArtist("");
		infos->setAlbum("");
		infos->setYear("");
		infos->setTrack("");
		infos->setTitle("");
		infos->setRating(0);
		infos->setDuration(0);
		infos->setPosition(0);
		kapp->postEvent(infos, new QCustomEvent(NEW_INFO_EVENT));
	}
}

/**
 * Most of this code comes from the method AmfsXML::getCoverPath() of the application AmarokFS.
 */
void InformationPoller::retreiveNewCover(const QString &artist, const QString &album, bool *success)
{
	QString escapedArtist = QString(artist).replace("'", "\'");
	QString escapedAlbum  = QString(album).replace( "'", "\'");

	// First try to get the image from the Amarok Images table (USER DEFINED cover):
	QString query = QString("SELECT path FROM images WHERE artist='%1' AND album='%2'").arg(escapedArtist, escapedAlbum);
	QString coverPath = DcopInterface::getStringList("amarok", "collection", "query(QString)", query, success).first();

	// In recent versions of Amarok, coverPath CAN be a relative path. Compute absolute path:
	query = QString("SELECT lastmountpoint FROM devices WHERE id=(SELECT deviceid FROM images WHERE artist='%1' AND album='%2' LIMIT 1)").arg(escapedArtist, escapedAlbum);
//	QString deviceMountPointIdQuery = QString("SELECT deviceid FROM images WHERE artist='%1' and album='%2'").arg(escapedArtist, escapedAlbum);
//	QStringList deviceMountPointId  = DcopInterface::getStringList("amarok", "collection", "query(QString)", deviceMountPointIdQuery);
//	QString deviceMountPointFirstId = deviceMountPointId.first();
//	QString query = QString("SELECT lastmountpoint FROM devices WHERE id='%1'").arg(deviceMountPointFirstId);
	QString deviceMountPoint = DcopInterface::getStringList("amarok", "collection", "query(QString)", query, success).first();
	QString coverAbsolutePath = deviceMountPoint + coverPath;//.right(coverPath.length() - 1);

	// Get the cached thumbnail image path (usually a 100x100px image, but user configurable)
	QString cachedCoverPath = DcopInterface::getString("amarok", "player", "coverImage()", success);

	//$HOME/.kde/share/apps/amarok/albumcovers/large/.... - some covers are stored here
	QString cachedCoverPathLarge = cachedCoverPath;
	cachedCoverPathLarge.replace(QRegExp("cache/[0-9]*@"), "large/");

	//$HOME/.kde/share/apps/amarok/albumcovers/tagcover/.... - and some are stored here
	QString cachedCoverPathTagcover = cachedCoverPathLarge;
	cachedCoverPathTagcover.replace("large/", "tagcover/");

	if (cachedCoverPathLarge.endsWith("/nocover.png"))
		cachedCoverPathLarge = bigNoCoverPath();

	if (!*success)
		return;

// 	// Debug
// 	std::cout << "coverPath: " << coverPath + "" << std::endl;
// 	std::cout << "deviceMountPoint: " << deviceMountPoint + "" << std::endl;
// 	std::cout << "coverAbsolutePath: " << coverAbsolutePath + "" << std::endl;
// 	std::cout << "cachedCoverPathLarge: " << cachedCoverPathLarge + "" << std::endl;
// 	std::cout << "cachedCoverPathTagcover: " << cachedCoverPathTagcover + "" << std::endl;
// 	std::cout << "cachedCoverPath: " << cachedCoverPath + "" << std::endl;

	QPixmap coverPixmap(coverPath);                // User-defined cover, big, sometimes absolute paths
	if (coverPixmap.isNull()) {
		coverPixmap.load(coverAbsolutePath);       // User-defined cover, big, absolute paths with latest Amarok versions
	}
	if (coverPixmap.isNull()) {
		coverPixmap.load(cachedCoverPathLarge);    // Cached cover image, sometimes in ~/.kde/share/apps/amarok/albumcovers/large OR...
	}
	if (coverPixmap.isNull()) {
		coverPixmap.load(cachedCoverPathTagcover); // ... Cached cover image, sometimes in ~/.kde/share/apps/amarok/albumcovers/tagcover
	}
	if (coverPixmap.isNull()) {
		coverPixmap.load(cachedCoverPath);         // Cached cover image, thumbnail (100x100px by default, but the size can be changed in the Amarok settings)
	}

//	PlayerInformation::instance()->emitAboutToChangeTrack(); // An hack because it's not multi-threaded yet

	// Set:
	PlayerInformation *infos = PlayerInformation::instance();
	infos->setCoverPixmap(coverPixmap);
}

QString InformationPoller::bigNoCoverPath()
{
	QDir dir;
	QStringList folders = kapp->dirs()->resourceDirs("data");
	for (QStringList::Iterator it = folders.begin(); it != folders.end(); ++it) {
		QString imagePath = *it + "/amarok/images/nocover.png";
		if (dir.exists(imagePath))
			return imagePath;
	}
	return "";
}

void InformationPoller::suspendPolling()
{
	m_refreshTimer->stop();
}

void InformationPoller::resumePolling()
{
	if (!m_refreshTimer->isActive())
		m_refreshTimer->start( 333, false );
}

#include "informationpoller.moc"
