/*
 * Copyright (C) 2006-2007	Andre Beckedorf
 * 					 		<evilJazz _AT_ katastrophos _DOT_ 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.
 *
 * 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 <qapplication.h>
#include <qtextstream.h>
#include <qlistview.h>
#include <qstringlist.h>
#include <qtextcodec.h>
#include <qwidgetstack.h>
#include <qmessagebox.h>
#include <qregexp.h>
#include <unistd.h>

#include "qtkfiledialog.h"
#include "qinputdialog.h"
#include "qprogressdialog.h"
#include "configuration.h"
#include "playlistmanager.h"
#include "playlist.h"
#include "media.h"
#include "mediadatabase.h"
#include "sqlite_source/sqlite3.h"

#include "debug.h"

QString resolveLocation(const QString &parentDir, const QString &location)
{
	QString result(location);
	
	if (!result.startsWith("/"))
	{
		if (result.startsWith("./"))
			result = parentDir + result.right(result.length() - 1);
		else if (result.startsWith("../"))
		{
			QDir dir(parentDir);
			while (result.startsWith("../"))
			{
				result.remove(0, 3);
				dir.cdUp();
			}
			
			QString absPath = dir.absPath();
			if (absPath == "/")
				result = absPath + result;
			else
				result = absPath + "/" + result;
		}
		else // relative to the directory this playlist is in... (obviously the same as ./)
			result = parentDir + "/" + result;
	}
	
	DHPRINTF("resolvedLocation: %s -> %s", (const char *)location.utf8(), (const char *)result.utf8());
	return result;
}

PlayListManager::PlayListManager(PlayList *targetPlayList, QWidgetStack *container, Configuration &config, QWidget *owner)
	:	playlistFile(""),
		owner_(owner),
		playListContainer_(container),
		config_(config),
		playList_(targetPlayList),
		isDynPlayList_(false),
		lboxDynPlayList_(NULL),
		previouslyVisibleWidget_(NULL),
		activeMode_(PlayListManager::ModeMax),
		playlistSelectedItemIndex_(0),
		otgPlaylistSelectedItemIndex_(0)
{
	dynPlayList_.clear();
}

PlayListManager::~PlayListManager()
{
}

QString PlayListManager::getDisplayPlayListFilename()
{
	if (isOnTheGoPlayListActive())
		return "On-The-Go Play List";
	else if (playlistFile.isNull() || playlistFile.isEmpty())
		return "Untitled." + (isDynamicPlaylist() ? QString("dynplaylist") : QString("playlist"));
	else
		return QFileInfo(playlistFile).fileName();	
}

void PlayListManager::execDialogAddFileToPlayList()
{
	addDialog(false);
}

void PlayListManager::execDialogAddDirToPlayList()
{
	addDialog(true);
}

void PlayListManager::execDialogAddDirWithSubDirsToPlayList()
{
	addDialog(true, true);
}

void PlayListManager::addDialog(const bool dir, const bool subdir)
{
	if (isDynamicPlaylist())
		setActiveMode(PlayListManager::ModeDynPlaylistEditor);

	QTKFileDialog dialog(owner_);
	dialog.setFilter("All files|*.*");
	dialog.setDirectory(config_.lastDirectory);
	dialog.setSystemFile(true);
	dialog.setHiddenFile(true);
	dialog.setDirectorySelectMode(dir);
	dialog.setHomeDirectoryCdUpEnable(true);

	loadWidgetPosition(&dialog, config_.getConfigName(), "FileDialog");	

	if (dialog.exec() == QDialog::Accepted)
	{
		if (dir)
			addDir(dialog.getCurrentDir(), subdir);
		else
			addFile(dialog.getFileName());
		
		// Update last directory
		config_.lastDirectory = dialog.getCurrentDir();
	}
	
	saveWidgetPosition(&dialog, config_.getConfigName(), "FileDialog");
}

void PlayListManager::openASX(const QString &asx)
{
	QFile file(asx);
	if (file.open(IO_ReadOnly))
	{
		// Parse ASX
		QTextStream stream(&file);
		const QStringList list = QStringList::split('\n', stream.read());
		for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
		{
			QString line(*it);
			QString uri;
			
			if (line.contains("ref href", false))
			{
				// String Indexes
				int uriBegin, uriEnd;
				
				// Find URI Index
				uriBegin = line.find("href", 0, false);
				uriBegin = line.find('\"', uriBegin);
				++uriBegin;
				
				uriEnd = line.find('\"', uriBegin);
				
				// Get URI
				uri = line.mid(uriBegin, uriEnd - uriBegin);
				
				// Add to PlayList
				openURL(uri);
			}
		}
		
		file.close();
	}
}

void PlayListManager::openURL(const QString &uri)
{
	bool ok;
	QString url;
	
	if (uri == NULL)
		url = QInputDialog::getText(tr("URL for open..."), tr("URL"), QLineEdit::Normal, QString::null, &ok, owner_);
	else
		url = uri;
	
	if (ok && !url.isEmpty())
	{
		playList_->insertItem(url);
	}
	
	playList_->updateFilter();
}

void PlayListManager::addFile(const QString &path)
{
	if (isDynamicPlaylist())
	{
		if (isDynamicPlaylistEditorActive())
			lboxDynPlayList_->insertItem(path);
		else
			dynPlayList_.append(path);
	}
	else
		playList_->insertItem(QFileInfo(path).absFilePath());
}

void PlayListManager::fillFileList(QStringList &filelist, const QString &path, const bool subdir)
{
	QDir dir(path);
	if (!dir.exists()) return;
	
	dir.setFilter(QDir::Files | (subdir ? QDir::Dirs : 0));
	dir.setSorting(QDir::Unsorted);

	const QFileInfoList *list = dir.entryInfoList();
	QString fileName;
	QFileInfoListIterator it(*list);
	QFileInfo *info;

	for (; (info = it.current()); ++it)
	{
		if (info->isFile())
		{
			// only add the file, if its extension is known...
			if (config_.isValidExtension(info->extension(false)))
				filelist.append(info->absFilePath());
		}
		else 
		{
			fileName = info->fileName();
			if (fileName != "." && fileName != "..")
				fillFileList(filelist, info->absFilePath(), subdir);
		}
	}
}

void PlayListManager::addDir(const QString &path, const bool subdir)
{
	DENTERMETHOD("PlayListManager::addDir(\"%s\", %s)", (const char*)path.utf8(), subdir ? "true" : "false");
	
	if (isDynamicPlaylist())
	{
		QString location = path; 
		
		if (location.right(1) != "/")
			location += "/";
		
		if (subdir)
			location += "**";
		
		if (isDynamicPlaylistEditorActive())
			lboxDynPlayList_->insertItem(location);
		else
			dynPlayList_.append(location);
	}
	else
	{
		QProgressDialog progress(tr("Please wait..."), tr("Cancel"), 0, owner_, "progress", true);
		
		progress.setCaption(tr("Scanning for files..."));
		
		progress.setMinimumDuration(0);
		progress.setFixedWidth(QMIN(qApp->desktop()->width() * 0.9, 640));
		progress.setAutoClose(false);
		progress.setAutoReset(false);
		progress.show();
		qApp->processEvents();
			
		QStringList filelist;
		DTIMERINIT(timer);
		fillFileList(filelist, path, subdir);
		filelist.sort();
		DTIMERPRINT(timer, "recursive directory scanning");

		const int numFiles = filelist.count();

		playList_->beginChangingUpdate();
		
		// start reading the playlist entries...
		progress.setTotalSteps(numFiles);	
		progress.setCaption(tr("Adding files..."));
		
		qApp->processEvents();		
		
		QTime time;
		time.start();
			
		int i = 0;
		
		QListViewItemIterator it(playList_);
		QListViewItemIterator last;
		while (it.current()) {
			last = it;
			++ it;
		}
		PlayListItem *lastItem = static_cast<PlayListItem *>(last.current());
		
		for (QStringList::Iterator it = filelist.begin(); it != filelist.end(); ++it, ++i)
		{
			QString &file = *it;

			if (time.elapsed() > 1500)
			{
				progress.setProgress(i);
				
				if (file.length() > 55)
					progress.setLabelText(file.left(20) + "..." + file.right(40));
				else
					progress.setLabelText(file);
					
				qApp->processEvents();
				time.restart();
			};
			
			if (progress.wasCancelled())
				break;

			lastItem = playList_->insertItem(file, lastItem);
		}
		
		progress.setProgress(numFiles);
		progress.setCaption(tr("Updating playlist..."));
		progress.setLabelText(tr("Please wait..."));
		qApp->processEvents();
		
		playList_->endChangingUpdate();
		playList_->updateFilter();
		playList_->updateView();
		
		qApp->processEvents();
		
		progress.hide();
		qApp->processEvents();
	}
	
	DEXITMETHOD("PlayListManager::addDir(\"%s\", %s)", (const char*)path.utf8(), subdir ? "true" : "false");
}

void PlayListManager::newPlayList()
{
	setActiveMode(PlayListManager::ModeOpenedPlaylist);

	clearPlayList();
	playlistFile = "";
	isDynPlayList_ = false;

	emit playlistFileNameChanged();
}

void PlayListManager::newDynamicPlayList()
{
	setActiveMode(PlayListManager::ModeOpenedPlaylist);
	
	clearPlayList();
	playlistFile = "";
	isDynPlayList_ = true;

	checkInputMode();
	
	emit playlistFileNameChanged();

	//setActiveMode(PlayListManager::ModeDynPlaylistEditor);
}

void PlayListManager::clearPlayList()
{
	if (activeMode_ == ModeOpenedPlaylist)
	{
		dynPlayList_.clear();
	}
	else if (activeMode_ == ModeDynPlaylistEditor)
	{
		lboxDynPlayList_->clear();
		dynPlayList_.clear();
	}		
	
	playList_->clear();
	emit playlistCleared();
	
	//switchToDynamicPlayListEditor();
	
	DMEMSTAT();	
}

void PlayListManager::savePlayList()
{
	if (!playlistFile || playlistFile.isEmpty() || activeMode_ == ModeOnTheGoPlaylist) {
		execDialogSavePlayListAs();
		return;
	}
	
	writePlayList(playlistFile);
}

void PlayListManager::execDialogSavePlayListAs()
{
	QTKFileDialog dialog(owner_);

	if (isDynPlayList_ && activeMode_ != ModeOnTheGoPlaylist)
		dialog.setFilter("Dynamic playlist|*.dynplaylist");
	else
		dialog.setFilter("Playlist files|*.playlist;*.m3u");
	
	dialog.setMkDirButtonDisabled(false);
	dialog.setSaveMode(true);
	dialog.setSaveMode_ReWriteMessageDisabled(false);
	dialog.setDirectory(config_.lastPlaylistDirectory);
	dialog.setHomeDirectoryCdUpEnable(true);
	
	loadWidgetPosition(&dialog, config_.getConfigName(), "FileDialog");

	if (dialog.exec() == QDialog::Accepted)
		writePlayList(dialog.getFileName());
	
	saveWidgetPosition(&dialog, config_.getConfigName(), "FileDialog");
}

void PlayListManager::execDialogOpenPlayList()
{
	QTKFileDialog dialog(owner_);

	dialog.setFilter("All supported playlist formats|*.dynplaylist;*.playlist;*.m3u;*.asx|"\
		"Quasar dynamic playlists|*.dynplaylist|"\
		"Quasar playlists|*.playlist|"\
		"M3U playlists|*.m3u|"\
		"Windows Media Meta Files|*.asx|"\
		"All files|*.*");
	
	dialog.setDirectory(config_.lastPlaylistDirectory);
	dialog.setHomeDirectoryCdUpEnable(true);
			
	loadWidgetPosition(&dialog, config_.getConfigName(), "FileDialog");
	
	if (dialog.exec() == QDialog::Accepted)
	{
		if (activeMode_ == ModeOnTheGoPlaylist)
			clearPlayList();
		else
			newPlayList();
		
		readPlayList(dialog.getFileName());
		playList_->setActiveItem(0);
		playList_->setFocus();
	}
	
	saveWidgetPosition(&dialog, config_.getConfigName(), "FileDialog");
}


void PlayListManager::refreshDynamicPlayList()
{
	playList_->clear();
	readPlayList(playlistFile, dynPlayList_, tr("Refreshing playlist..."));
}

void PlayListManager::writePlayList(const QString &filename)
{
	QString fullFilename = filename;
	QString extension = QFileInfo(fullFilename).extension(false);
	
	if (isDynamicPlaylist())
	{
		if (extension != "dynplaylist")
			fullFilename += ".dynplaylist";
	}
	else if (extension != "playlist")
		fullFilename += ".playlist";
	
	QFile file(fullFilename);
	if (file.open(IO_WriteOnly))
	{
		QTextStream s(&file);
		// s.setEncoding(Qt::utf8);

		if (isDynamicPlaylist())
		{
			if (isDynamicPlaylistEditorActive())
				for (int i = 0; i < lboxDynPlayList_->count(); i++)
					s << lboxDynPlayList_->text(i) << "\n";
			else
				for (QStringList::Iterator it = dynPlayList_.begin(); it != dynPlayList_.end(); ++it )
					s << *it << "\n";
		}
		else
			for (QListViewItemIterator it(playList_); it.current(); ++it)
				s << static_cast<PlayListItem *>(it.current())->media()->location().toString() << "\n";
		
		// Update last directory/file if this is the real playlist, ie. it's not the On-The-Go playlist...
		if (activeMode_ != ModeOnTheGoPlaylist)
		{
			playlistFile = fullFilename;
			config_.lastPlaylistFile = playlistFile;
		}

		config_.lastPlaylistDirectory = QFileInfo(fullFilename).dirPath(true);
		
		// Close File
		file.close();

		emit playlistFileNameChanged();
	}
}

void PlayListManager::readPlayList(const QString &filename, const QStringList &filelist, const QString &dialogTitle, bool assumeInDB)
{
	DTIMERINIT(timer);
	
	QStringList list(filelist);
	
	QString playlistDirectory = QFileInfo(filename).dirPath(true);
	
	// initialize dialog and shot it...
	QProgressDialog progress(tr("Please wait..."), tr("Cancel"), 0, owner_, "progress", true);
	
	progress.setCaption(tr("Scanning for files..."));
	
	progress.setMinimumDuration(0);
	progress.setFixedWidth(QMIN(qApp->desktop()->width() * 0.9, 640));
	progress.setAutoClose(false);
	progress.setAutoReset(false);
	progress.show();
	qApp->processEvents();
	
	// do we have a dynamic playlist here?
	// if so, we need to expand various entries in that list...
	if (filename.lower().right(12) == ".dynplaylist" || 
		(isDynamicPlaylist() && (filename.isNull() || filename.isEmpty())))
	{
		// copy current file content, so we can change it later...
		if (activeMode_ != ModeOnTheGoPlaylist)
			dynPlayList_ = list;
		
		if (!assumeInDB)
		{
			// expand dynamic playlist to real content...
			QStringList expandedList;
			for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
			{
				QString location(*it);
				
				if (location.startsWith("#") || location.startsWith(";"))
					continue;
	
				// WHY OH WHY is there startsWith but no endsWith in QString in Qt 2.3.x ?!?!
				if (location.right(3) == "/**")
				{
					// recursive scanning of location...
					location = resolveLocation(playlistDirectory, location.left(location.length() - 3));					
					QStringList fileList;
					fillFileList(fileList, location, true);
					fileList.sort();
					expandedList += fileList;
				}
				else if (location.right(2) == "/*" || location.right(1) == "/")
				{
					// all files of the location...
					location = resolveLocation(playlistDirectory, location.left(location.findRev('/')));
					QStringList fileList;
					fillFileList(fileList, location, false);
					fileList.sort();
					expandedList += fileList;
				}
				else if (location.startsWith("http://") || location.startsWith("mms://"))
					expandedList.append(location);
				else
				{
					location = resolveLocation(playlistDirectory, location);
					QFileInfo fi(location);
					
					if (fi.exists())
					{
						if (fi.isDir())
						{
							QStringList fileList;
							fillFileList(fileList, location, false);
							fileList.sort();
							expandedList += fileList;
						}
						else if (config_.isValidExtension(fi.extension()))
							// just add the (file) location to the expanded list...
							expandedList.append(location);
					}
				}
			}
			
			list = expandedList;
		}
					
		if (activeMode_ != ModeOnTheGoPlaylist)
			isDynPlayList_ = true;
	}
	else if (activeMode_ != ModeOnTheGoPlaylist)
	{
		isDynPlayList_ = false;
	}
	
	
	if (!assumeInDB)
	{
		// start reading the playlist entries...
		int numFiles = list.count();
		progress.setTotalSteps(numFiles);	
		
		if (!dialogTitle)
			progress.setCaption(tr("Adding files..."));
		else
			progress.setCaption(dialogTitle);
		
		qApp->processEvents();		
		
		int i = 0;
	
		playList_->beginChangingUpdate();	
	
		QTime time;
		time.start();

		PlayListItem *lastItem = NULL;
		
		for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it, ++i)
		{
			QString location(*it);
			DHPRINTF("Relative Path: %s", (const char*)location.utf8());
			
			if (time.elapsed() > 1000)
			{
				progress.setProgress(i);
				
				if (location.length() > 55)
					progress.setLabelText(location.left(20) + "..." + location.right(40));
				else
					progress.setLabelText(location);
					
				qApp->processEvents();
				
				time.restart();
			}
			
			if (!(location.startsWith("/") || location.startsWith("http://") || location.startsWith("mms://")))
			{
				// skip comments
				if (location.startsWith("#") || location.startsWith(";"))	
					continue;			
	
				location = resolveLocation(playlistDirectory, location);
			}
			
			lastItem = playList_->insertItem(location, lastItem);
			
			if (progress.wasCancelled())
				break;
		}
	
		progress.setProgress(numFiles);
		
		progress.setCaption(tr("Updating playlist..."));
		progress.setLabelText(tr("Please wait..."));
		qApp->processEvents();
		
		DTIMERPRINT(timer, "playlist added to DB in");
		playList_->endChangingUpdate();
	}
	
	playList_->reloadView();
	
	progress.hide();
	qApp->processEvents();
	
	checkInputMode();
	
	emit playlistLoaded();
	
	DMEMSTAT();	
}

void PlayListManager::readPlayList(const QString &filename, bool assumeInDB)
{
	DENTERMETHOD("PlayListManager::readPlayList()");
	DTIMERINIT(timer);

	// Load ASX
	QRegExp asxExp("^[^\\.]+.asx$", false);
	if (asxExp.match(filename) != -1)
	{
		openASX(filename);
		return;
	}
	
	// Load PlayList
	QFile file(filename);
	if (file.open(IO_ReadOnly))
	{
		QString playlistDirectory = QFileInfo(filename).dirPath(true); 
		
		// Update last directory/file if this is the real playlist, ie. it's not the On-The-Go playlist...
		if (activeMode_ != ModeOnTheGoPlaylist)
		{
			playlistFile = filename;
			config_.lastPlaylistFile = playlistFile;
		}
		
		config_.lastPlaylistDirectory = playlistDirectory;
		
		// Load PlayList
		QTextStream stream(&file);
		QStringList list = QStringList::split('\n', stream.read());
		readPlayList(filename, list, NULL, assumeInDB);

		file.close();

		emit playlistFileNameChanged();
	}
	
	DTIMERPRINT(timer, "playlist read in");
	DEXITMETHOD("PlayListManager::readPlayList()");
}

void PlayListManager::moveSelectedUp()
{
	if (isDynamicPlaylist())
	{
		if (isDynamicPlaylistEditorActive())
		{
			int index = lboxDynPlayList_->currentItem();
			if (index > -1)
			{
				QString location = lboxDynPlayList_->currentText();
				lboxDynPlayList_->removeItem(index);
				lboxDynPlayList_->insertItem(location, index - 1);
				lboxDynPlayList_->setCurrentItem(index - 1);
			}
		}
		else
		{
			int result = QMessageBox::information(owner_, tr("Dynamic playlist"),
					tr("This playlist is dynamic, thus you can't change\nthe order of items in this playlist\ndirectly.\nDo you want to switch to the editor\nfor dynamic playlists to change\nthe order there?"),
					 QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape);
			
			if (result == QMessageBox::Yes)
				setActiveMode(PlayListManager::ModeDynPlaylistEditor);
		}
	}
	else
		playList_->swapPreviousItem();
}

void PlayListManager::moveSelectedDown()
{
	if (isDynamicPlaylist())
	{
		if (isDynamicPlaylistEditorActive())
		{
			int index = lboxDynPlayList_->currentItem();
			if (index > -1)
			{
				QString location = lboxDynPlayList_->currentText();
				lboxDynPlayList_->removeItem(index);
				lboxDynPlayList_->insertItem(location, index + 1);
				lboxDynPlayList_->setCurrentItem(index + 1);
			}
		}
		else
		{
			int result = QMessageBox::information(owner_, tr("Dynamic playlist"),
					 tr("This playlist is dynamic, thus you can't change\nthe order of items in this playlist\ndirectly.\nDo you want to switch to the editor\nfor dynamic playlists to change\nthe order there?"),
					 QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape);
			
			if (result == QMessageBox::Yes)
				setActiveMode(PlayListManager::ModeDynPlaylistEditor);
		}
	}
	else
		playList_->swapPreviousItem();	
}

void PlayListManager::removeFileFromPlayList()
{
	if (isDynamicPlaylist())
	{
		if (isDynamicPlaylistEditorActive())
		{
			for (int i = lboxDynPlayList_->count() - 1; i > -1; --i)
				if (lboxDynPlayList_->isSelected(i))
					lboxDynPlayList_->removeItem(i);
		}
		else
		{
			int result = QMessageBox::information(owner_, tr("Dynamic playlist"),
					 tr("This playlist is dynamic, thus you can't delete\nan item from this playlist directly.\nDo you want to switch to the editor\nfor dynamic playlists and delete\nthe item there?"),
					 QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape);
			
			if (result == QMessageBox::Yes)
				setActiveMode(PlayListManager::ModeDynPlaylistEditor);
		}
	}
	else
	{
		playList_->removeSelectedItems();
		playList_->updateFilter();
	}
}

void PlayListManager::cutSelectionFromPlayList()
{
	mediaIDClipboard_ = playList_->getSelectedMediaIDs();
	removeFileFromPlayList();
	emit clipboardChanged();
}

void PlayListManager::copySelectionFromPlayList()
{
	mediaIDClipboard_ = playList_->getSelectedMediaIDs();
	emit clipboardChanged();
}

void PlayListManager::pasteIntoPlayList()
{
	if (!isDynamicPlaylist() && mediaIDClipboard_.count() > 0) 
	{
		// if the playlist is not sorted or filtered, then we can
		// directly insert the new items at the current position...
		if (!playList_->isSorted() && 
			!playList_->isFiltered())
		{
			PlayListItem *lastItem = NULL;
			bool moveFirstItem = false;
			
			// is anything selected?
			if (playList_->selectedItems().count() > 0)
			{
				// if yes, take the first item as place to insert...
				lastItem = playList_->selectedItems().at(0);
				lastItem->media()->dumpInfo();
	
				// since we want to place the new items at the position/index of
				// the previously selected insertion point, we need to get the item
				// above it, because we can only tell QListView to insert after
				// a given item...
				if (lastItem->itemAbove())
					lastItem = static_cast<PlayListItem *>(lastItem->itemAbove());
				else if (lastItem == playList_->firstItem())
					// if there is no item above, this means we have selected the first
					// child in the list. Do a sanity check and then mark the item separately
					// so we can move it to the end of the new stuff after our items have 
					// been inserted...
					moveFirstItem = true;
			}
			else
				// insert at end of list if nothing is selected or the playlist is empty...
				lastItem = playList_->lastItem();
	
			playList_->selectAll(false);
			
			for (QValueList<unsigned long>::Iterator listit = mediaIDClipboard_.begin(); listit != mediaIDClipboard_.end(); ++listit)
			{
				lastItem = playList_->insertItem((*listit), lastItem);
				playList_->setSelected(lastItem, true);
			}
	
			// if we have a marked an item that was (and still is) the first child in the list
			// move it to the end of our inserted items...
			if (moveFirstItem)
				playList_->firstItem()->moveItem(lastItem);
		}
		// the playlist is sorted or filtered, so insert at DB level...
		else			
		{
			playList_->beginChangingUpdate();
			directlyAppendToPlaylistTable(mediaIDClipboard_, playList_->playListSource());
			playList_->endChangingUpdate();
			playList_->reloadView();
		}
	}
}

void PlayListManager::directlyAppendToPlaylistTable(const QValueList<unsigned long> &itemIDs, const QString &tableName)
{
	int newPlaylistID = 0;
	sqlite3_stmt *vm;

	// query playlist index for first new item...
	QString query = "SELECT MAX(idx)+1 FROM " + tableName + ";";
	sqlite3_prepare(playList_->mediaDatabase()->db(), (const char*)query.utf8(), -1, &vm, 0);
	if (sqlite3_step(vm) == SQLITE_ROW)
		newPlaylistID = sqlite3_column_int(vm, 0);
	else
	{
		// Nothing found? Funny we got here...
		// Something is wrong with our DB! clean up - back out.
		sqlite3_finalize(vm);
		return;
	}	
	sqlite3_finalize(vm);

	// append new items to the OTG playlist... 
	query = "INSERT INTO " + tableName + " VALUES(?1, NULL, ?2);";
	sqlite3_prepare(playList_->mediaDatabase()->db(), (const char*)query.utf8(), -1, &vm, 0);
	for (QValueList<unsigned long>::ConstIterator it = itemIDs.begin(); it != itemIDs.end(); ++it )
	{
		sqlite3_bind_int(vm, 1, newPlaylistID);
		sqlite3_bind_int(vm, 2, (*it));
		sqlite3_step(vm);
		sqlite3_reset(vm);
		++newPlaylistID;
	}

	sqlite3_finalize(vm);
}

void PlayListManager::selectAll()
{
	if (isDynamicPlaylistEditorActive())
	{	
		lboxDynPlayList_->selectAll(true);
		if (lboxDynPlayList_->currentItem() == -1 && lboxDynPlayList_->count() > 0)
			lboxDynPlayList_->setCurrentItem(0);
	}
	else
		playList_->selectAll(true);
}

void PlayListManager::deselectAll()
{
	if (isDynamicPlaylistEditorActive())
	{
		lboxDynPlayList_->selectAll(false);
		lboxDynPlayList_->setCurrentItem(-1);
	}
	else
		playList_->selectAll(false);
}

void PlayListManager::copyToOnTheGoPlayList()
{
	if (playList_->selectedItems().count() > 0 && activeMode_ != PlayListManager::ModeOnTheGoPlaylist)
	{
		directlyAppendToPlaylistTable(playList_->getSelectedMediaIDs(), "onthego_playlist");

		if (playList_->selectedItems().count() > 1)
			emit showMessage(tr("Items copied to On-The-Go Playlist."), 1500);
		else
			emit showMessage(tr("Item copied to On-The-Go Playlist."), 1500);
	}
}

bool PlayListManager::setActiveMode(Mode mode)
{
	bool result = false;
	
	if (mode == activeMode_)
		result = true;
	
	// if we are already in a playlist view and the last active playlist was requested,
	// do nothing and return positive.
	if (mode == PlayListManager::ModeLastActivePlaylist && 
		(activeMode_ == PlayListManager::ModeOnTheGoPlaylist || activeMode_ == PlayListManager::ModeOpenedPlaylist))
		result = true;

	emit switchingModeTo(mode);	
	
	if (!result)
	{
		if (mode == PlayListManager::ModeOpenedPlaylist || 
			mode == PlayListManager::ModeOnTheGoPlaylist ||
			mode == PlayListManager::ModeLastActivePlaylist)
		{
			if (isDynamicPlaylist() && activeMode_ == PlayListManager::ModeDynPlaylistEditor)
			{
				QStringList newDynPlayList;
				
				for (int i = 0; i < lboxDynPlayList_->count(); i++)
					newDynPlayList.append(lboxDynPlayList_->text(i));
				
				// did anything change?
				if (!(newDynPlayList == dynPlayList_))
				{
					// if so, copy over
					dynPlayList_ = newDynPlayList;
					// and refresh the real playlist...
					refreshDynamicPlayList();			
				}
				
				// make previous widget visible...
				playListContainer_->raiseWidget(previouslyVisibleWidget_);
				previouslyVisibleWidget_ = NULL;
				
				delete lboxDynPlayList_;
				lboxDynPlayList_ = NULL;
			}
	
			// resolve last active play list and set mode accordingly...
			if (mode == PlayListManager::ModeLastActivePlaylist)
				mode = (playList_->playListSource() == "playlist" ? PlayListManager::ModeOpenedPlaylist : PlayListManager::ModeOnTheGoPlaylist);
	
			// save current playlist params...
			if (playList_->playListSource() == "playlist")
			{
				playlistParams_ = playList_->currentParams();
				
				if (playList_->currentItem())
					playlistSelectedItemIndex_ = playList_->itemIndex(playList_->currentItem());
				else
					playlistSelectedItemIndex_ = -1;
				
				playlistPos_ = QPoint(playList_->contentsX(), playList_->contentsY());
			}
			else if (playList_->playListSource() == "onthego_playlist")
			{
				otgPlaylistParams_ = playList_->currentParams();
				
				if (playList_->currentItem())
					otgPlaylistSelectedItemIndex_ = playList_->itemIndex(playList_->currentItem());
				else
					otgPlaylistSelectedItemIndex_ = -1;
				
				otgPlaylistPos_ = QPoint(playList_->contentsX(), playList_->contentsY());
			}
			
			if (mode == PlayListManager::ModeOpenedPlaylist)
			{
				playList_->setParams(playlistParams_);
				emit playlistFilterChanged(playlistParams_.filterText);
				playList_->setPlayListSource("playlist");
				playList_->setCurrentItem(playList_->findItemByIndex(playlistSelectedItemIndex_));
				playList_->setContentsPos(playlistPos_.x(), playlistPos_.y());
				checkInputMode();
			}
			else if (mode == PlayListManager::ModeOnTheGoPlaylist)
			{
				playList_->setParams(otgPlaylistParams_);
				emit playlistFilterChanged(otgPlaylistParams_.filterText);
				playList_->setPlayListSource("onthego_playlist");
				playList_->setCurrentItem(playList_->findItemByIndex(otgPlaylistSelectedItemIndex_));
				playList_->setContentsPos(otgPlaylistPos_.x(), otgPlaylistPos_.y());
			}
				
			result = true;
			activeMode_ = mode;
		}
		else if (mode == PlayListManager::ModeDynPlaylistEditor)
		{
			if (isDynamicPlaylist())
			{
				if (!lboxDynPlayList_)
				{
					lboxDynPlayList_ = new QListBox(playListContainer_);
					connect(lboxDynPlayList_, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
					
					lboxDynPlayList_->insertStringList(dynPlayList_);
				
					// remember previously visible widget, so we can reselect it afterwards...
					previouslyVisibleWidget_ = playListContainer_->visibleWidget();
				}
	
				// show our editor...
				playListContainer_->raiseWidget(lboxDynPlayList_);
				
				result = true;
				activeMode_ = PlayListManager::ModeDynPlaylistEditor;
			}
			else
				result = false;
		}
	}
	
	if (result)
		emit switchedModeTo(mode);
	
	checkInputMode();
	
	return result;
}

void PlayListManager::checkInputMode()
{
	if (isDynamicPlaylist() && playList_->inputMode() == PlayList::Move)
		playList_->setInputMode(PlayList::Select);
	
	if (isDynamicPlaylistEditorActive())
	{
		switch (inputMode())
		{
		case PlayList::Select:
			lboxDynPlayList_->selectAll(false);
			lboxDynPlayList_->setSelectionMode(QListBox::Single);
			if (lboxDynPlayList_->currentItem())
				lboxDynPlayList_->setSelected(lboxDynPlayList_->currentItem(), true);
			break;
		case PlayList::MultiSelect:
		case PlayList::Move:
			lboxDynPlayList_->setSelectionMode(QListBox::Extended);
			break;
		}
	}	
}

PlayList::InputMode PlayListManager::inputMode()
{
	return playList_->inputMode();
}

void PlayListManager::setInputMode(PlayList::InputMode inputMode)
{
	playList_->setInputMode(inputMode);
	checkInputMode();
}