// mcp.cpp
//
// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// 
// Except as contained in this notice, the name(s) of the author(s) shall not be
// used in advertising or otherwise to promote the sale, use or other dealings
// in this Software without prior written authorization from the author(s).

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <kdebug.h>
#include <kio/global.h>
#include <kio/job.h>
#include <kio/jobclasses.h>
#include <kio/netaccess.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <krandomsequence.h>
#include <ktempfile.h>
#include <qfile.h>
#include <qtextstream.h>

#include "noatunapp.h"
#include "mcp.h"
#include "player.h"

/**
 * class MCPItem is closely coupled with class MCP.  It makes sure that,
 * at all times, every PlayListItem maintains a one-to-one relationship
 * with a QDomElement from MCP.
 *
 * Additionally, it makes sure that the QDomElement is kept updated with
 * the PlayListItem information.
 */
class MCPItem : public PlayListItem
{
public:
	/**
	 * Create a new item, and create an element for it
	 */
	MCPItem(QDomDocument, const KURL & = 0, bool = true);

	/**
	 * Create an item for an existing element, as from a playlist file
	 */
	MCPItem(QDomElement);

	virtual ~MCPItem();

	inline QDomElement element(void) const {return node;};
	static inline KURL getURL(const QString &path);

protected:
	virtual void modified(void);

private:
	void setPointer(void);

	QDomElement node;
};

MCPItem::MCPItem(QDomElement n)
	: PlayListItem(getURL(n.attribute("path")), n.attribute("download") == "true")
{
	node = n;
	mTitle = node.attribute("title");
	mLength = node.attribute("length").toInt();
	setPointer();
}

MCPItem::MCPItem(QDomDocument d, const KURL &url, bool download)
	: PlayListItem(url, download)
{
	node = d.createElement("item");
	setPointer();

	node.setAttribute("title", mTitle);
	node.setAttribute("path", mUrl.path());
	node.setAttribute("download", mDownloaded ? "true" : "false");
	node.setAttribute("length", QString::number(mLength));
}

MCPItem::~MCPItem()
{
	node.removeAttribute("MCPItem");
}

void MCPItem::modified(void)
{
	node.setAttribute("title", mTitle);
	node.setAttribute("path", mUrl.path());
	node.setAttribute("download", mDownloaded ? "true" : "false");
	node.setAttribute("length", QString::number(mLength));

	static_cast<MCP *>(napp->playlist())->itemModified(this);
}

void MCPItem::setPointer(void)
{
	node.setAttribute("MCPItem", QString::number((long)this));
}

KURL MCPItem::getURL(const QString &path)
{
	KURL result(path);
	if (result.isMalformed() || result.protocol() == "file")
	{
		result.setProtocol("file");
		result.setPath(path);
	}
	return result;
}

inline MCPItem *getItemForElement(QDomElement element)
{
	return (MCPItem *)element.attribute("MCPItem").toLong();
}

/////////
// MCP //
/////////

MCP::MCP(QObject *parent, const char *name)
	: PlayList(parent, name)
	, shuffle(false)
{
	clear();
	shuffleUnused.setAutoDelete(false);
}

MCP::~MCP()
{
	// Only You can help prevent memory leaks
	clearInternal();
}

void MCP::reset(void)
{
	if(shuffle)
	{
		setShuffle(true);
	}
	else
	{
		cur = doc.documentElement().namedItem("item").toElement();
		emit current(getItemForElement(cur));
	}
}

void MCP::clear(void)
{
	clearInternal();
	emit cleared();
}

void MCP::clearInternal(void)
{
	QDomElement element = doc.documentElement().namedItem("item").toElement();

	while(!element.isNull())
	{
		MCPItem *item = getItemForElement(element);
		delete item;
		element = element.nextSibling().toElement();
	}

	doc.clear();
	doc.setContent(QString("<!DOCTYPE NoatunPlaylist><playlist/>"));

	napp->player()->stop();

	if(shuffle)
	{
		shuffleUnused.clear();
	}
}

const MCPItem *MCP::AtBottom = (MCPItem *)-1;

MCPItem *MCP::addFileG(const KURL &_url, bool _play, MCPItem *_afterThis)
{
	MCPItem *item = new MCPItem(doc, _url);

	if(_afterThis == AtBottom)
		doc.documentElement().appendChild(item->element());
	else
		moveAfter(item, _afterThis);

	if(_play) play(item);

	if(shuffle)
	{
		shuffleUnused.append(item);
		KRandomSequence rand;
		rand.randomize(&shuffleUnused);
	}

	return item;
}

void MCP::addFile(const KURL &_url, bool _play)
{
	(void)addFileG(_url, _play);
}

PlayListItem *MCP::addFile(const KURL &_url, PlayListItem *_afterThis)
{
	return addFileG(_url, false, static_cast<MCPItem *>(_afterThis));
}

PlayListItem *MCP::next(void)
{
	if(shuffle)
	{
		PlayListItem *item = shuffleUnused.take();
		if(!item)
		{
			setShuffle(true);
			item = shuffleUnused.take();
		}

		if(item)
			cur = static_cast<MCPItem *>(item)->element();
		else
			cur = QDomElement();

		return item;
	}
	else
	{
		QDomElement element = cur.nextSibling().toElement();
		if(element.isNull())
		{
			return 0;
		}
		else
		{
			cur = element;
			emit current(getItemForElement(cur));
			return getItemForElement(cur);
		}
	}
}

PlayListItem *MCP::current(void)
{
	return getItemForElement(cur);
}

PlayListItem *MCP::previous(void)
{
	if(shuffle)
		return next();

	QDomElement element = cur.previousSibling().toElement();
	if(element.isNull())
	{
		return 0;
	}
	else
	{
		cur = element;
		emit current(getItemForElement(cur));
		return getItemForElement(cur);
	}
}

PlayListItem *MCP::getFirst(void) const
{
	return getItemForElement(doc.documentElement().namedItem("item").toElement());
}

PlayListItem *MCP::getAfter(const PlayListItem *_item) const
{
	const MCPItem *item = static_cast<const MCPItem *>(_item);
	QDomElement element = item->element();
	element = element.nextSibling().toElement();
	return getItemForElement(element);
}

PlayListItem *MCP::getBefore(const PlayListItem *_item) const
{
	const MCPItem *item = static_cast<const MCPItem *>(_item);
	QDomElement element = item->element();
	element = element.previousSibling().toElement();
	return getItemForElement(element);
}

bool MCP::listVisible(void) const
{
	return !static_cast<QWidget *>(parent())->isHidden();
}

void MCP::setShuffle(bool b)
{
	shuffle = b;
	if(shuffle)
	{
		shuffleUnused.clear();

		QDomElement element = doc.documentElement().namedItem("item").toElement();
		while(!element.isNull())
		{
			shuffleUnused.append(getItemForElement(element));
			element = element.nextSibling().toElement();
		}
		KRandomSequence rand;
		rand.randomize(&shuffleUnused);
	}
}

void MCP::moveAfter(PlayListItem *_item, PlayListItem *_moveAfter)
{
	MCPItem *item = static_cast<MCPItem *>(_item);
	QDomElement element = item->element();

	MCPItem *moveAfter = static_cast<MCPItem *>(_moveAfter);
	if(moveAfter)
	{
		QDomElement after = moveAfter->element();
		after.parentNode().insertAfter(element, after);
		emit moved(_item, _moveAfter);
	}
	else
	{
		QDomNode parent = doc.documentElement();
		parent.insertBefore(element, parent.firstChild());
		emit moved(_item, 0);
	}
}

void MCP::play(PlayListItem *_item)
{
	MCPItem *item = static_cast<MCPItem *>(_item);
	QDomElement element = item->element();

	cur = element;
	emit current(item);
	emit playCurrent();
}

void MCP::itemModified(PlayListItem *item)
{
	emit modified(item);
}

void MCP::showList(void)
{
	if(!listVisible()) static_cast<QWidget *>(parent())->show();
	emit listShown();
}

void MCP::hideList(void)
{
	if(listVisible()) static_cast<QWidget *>(parent())->hide();
	emit listHidden();
}

void MCP::toggleList(void)
{
	QWidget *widget = static_cast<QWidget *>(parent());
	if(widget->isHidden())
		widget->show();
	else
		widget->hide();
}

void MCP::remove(PlayListItem *_item)
{
	MCPItem *item = static_cast<MCPItem *>(_item);
	QDomElement element = item->element();

	if(shuffle)
	{
		shuffleUnused.removeRef(item);
	}

	if(element == cur)
	{
		if(cur.nextSibling().isNull())
			napp->player()->stop();
		else
			next();
	}

	delete item;
	element.parentNode().removeChild(element);
	element.clear();

	emit removed(item);
}

static inline void MCPError(QObject *obj, QString err)
{
	KMessageBox::error(static_cast<QWidget *>(obj), err);
}

void MCP::load(const KURL &url, bool errorFree)
{
	QString dest;
	if(!KIO::NetAccess::download(url, dest))
	{
		if(!errorFree)
			MCPError(this, i18n("Failed to load %1.").arg(url.prettyURL()));
	}
	else
	{
		QFile file(dest);
		file.open(IO_ReadOnly);
		QTextStream stream(&file);
		QString data = stream.read();

		QDomDocument newDoc;
		if(!newDoc.setContent(data))
		{
			MCPError(this, i18n("%1 is not a valid Noatun playlist.").arg(url.prettyURL()));
		}
		else
		{
			clearInternal();
			doc.setContent(data);
			QDomElement element = doc.documentElement().namedItem("item").toElement();
			while(!element.isNull())
			{
				(void)new MCPItem(element);
				element = element.nextSibling().toElement();
			}
			emit loaded();
			setShuffle(shuffle);
		}
		KIO::NetAccess::removeTempFile(dest);
	}
}

void MCP::save(const KURL &url)
{
	KTempFile file;
	*(file.textStream()) << doc.toString();
	file.close();

	if(!KIO::NetAccess::upload(file.name(), url))
	{
		MCPError(this, i18n("Failed to save to %1.").arg(url.prettyURL()));
	}
	else
	{
		emit saved();
		kdDebug() << "End Of Line" << endl;
	}
}

PlayListItem *MCP::importWindowsPlayList(const KURL &url, PlayListItem *_item)
{
	MCPItem *item = static_cast<MCPItem *>(_item);

	QString dest;
	if(!KIO::NetAccess::download(url, dest))
	{
		MCPError(this, i18n("Failed to load %1.").arg(url.prettyURL()));
	}
	else
	{
		QFile file(dest);
		file.open(IO_ReadOnly);
		QTextStream stream(&file);
		QString data = stream.readLine();
		while(!data.isNull())
		{
			item = addFileG( MCPItem::getURL(data), false, item);
			data = stream.readLine();
		}
		KIO::NetAccess::removeTempFile(dest);
	}

	return item;
}

PlayListItem *MCP::importPlayList(const KURL &url, PlayListItem *_item)
{
	MCPItem *item = static_cast<MCPItem *>(_item);

	QString dest;
	if(!KIO::NetAccess::download(url, dest))
	{
		MCPError(this, i18n("Failed to load %1.").arg(url.prettyURL()));
	}
	else
	{
		QFile file(dest);
		file.open(IO_ReadOnly);
		QTextStream stream(&file);
		QString data = stream.read();

		// create a QDomDocument for the playlist

		// Get the documentElement

		// Loop through the children of the documentElement

			// clone the element, and put it in doc

			// create a new MCPItem for that new element

			// insert the newly created item's element after item's element

			// item = newly created item
			
		KIO::NetAccess::removeTempFile(dest);
	}

	return item;
}

#include "mcp.moc"
