#include <cmodule.h>
#include <pluginloader.h>
#include <common.h>
#include <noatunapp.h>

#include <qpushbutton.h>
#include <qcheckbox.h>
#include <qlistview.h>
#include <qpushbutton.h>
#include <qlayout.h>
#include <klocale.h>
#include <kdebug.h>
#include <klistview.h>
#include <qsplitter.h>
#include <qlabel.h>
#include <qdragobject.h>
#include <kurlrequester.h>
#include <kfiledialog.h>
#include <kdialog.h>

#include <qwhatsthis.h>

MimeListItem::MimeListItem(KListView *list, const QString &mime, const QString &extension,
                           const QString &author, const QString &url)
	: QListViewItem(list, mime, extension)
{
	mMime=mime;
	mExtension=extension;
	mAuthor=author;
	mUrl=url;
}

Types::Types(QWidget *parent, char *name) : CModule(parent, name)
{
	(new QHBoxLayout(this))->setAutoAdd(true);
	list=new KListView(this);
	list->show();
	list->addColumn(i18n("Mime Type"));
	list->addColumn(i18n("Extension"));
	i18n("URL");
	i18n("Author");
	i18n("&Enable");

	Arts::TraderQuery q;
//	if (!q.supports("Interface","Arts::PlayObject"))
//		return;

	std::vector<Arts::TraderOffer> *results = q.query();
	for (std::vector<Arts::TraderOffer>::iterator i=results->begin(); i != results->end(); i++)
	{
		QString author=commaVector(&*i, "Author");
		QString url=commaVector(&*i, "URL");
		QString extension=commaVector(&*i, "Extension");
		QString mime=commaVector(&*i, "MimeType");
		if (author.isNull() || url.isNull() || extension.isNull() || mime.isNull())
			continue;

		new MimeListItem(list, mime, extension, author, url);
	}
	delete results;
}

QString Types::commaVector(Arts::TraderOffer *t, const char *field)
{
	std::vector<std::string> *prop=t->getProperty(field);
	QString str;
	bool comma=false;

	for (std::vector<std::string>::iterator i=prop->begin(); i != prop->end(); i++)
	{
		if (comma)
			str+=", ";
		comma=true;
		str+=(*i).c_str();
	}
	delete prop;
	return str;
}

Types::~Types()
{

}

void Types::save()
{

}


/*****************************************************************
 * General options
 *****************************************************************/

General::General(QWidget *parent, char *name) : CModule(parent, name)
{
	mLoopList=new QCheckBox(i18n("&Return to Start of Playlist on Finish"), this);
	mLoopList->setChecked(napp->loopList());
	QWhatsThis::add(mLoopList, i18n("When the playlist is done playing, return the start, but don't start playing."));

	mAutoPlay=new QCheckBox(i18n("&Automatically Play First File"), this);
	mAutoPlay->setChecked(napp->autoPlay());
	QWhatsThis::add(mAutoPlay, i18n("Start playing the playlist as soon as Noatun is started."));

	mOneInstance=new QCheckBox(i18n("&Allow Only One Instance of Noatun"), this);
	mOneInstance->setChecked(napp->oneInstance());
	QWhatsThis::add(mOneInstance, i18n("Starting noatun a second time will cause it to just append items from the start to the current instance."));

	mClearOnOpen = new QCheckBox(i18n("Clear playlist when opening a file"), this);
	mClearOnOpen->setChecked(napp->clearOnOpen());
	QWhatsThis::add(mClearOnOpen, i18n("Opening a file with the global Open menu item will clear the playlist first"));

	mHackUpPlaylist = new QCheckBox(i18n("Process Playlist Items for Display"), this);
	mHackUpPlaylist->setChecked(napp->hackUpPlaylist());

	QWhatsThis::add(mHackUpPlaylist, i18n("Process filenames, such as removing spaces, and the filename extension, and set it as the playlist item title."));

	QFrame *dlSaverFrame = new QFrame(this);
	QLabel *dlsaver=new QLabel(i18n("&Download Directory"), dlSaverFrame);
	mDlSaver=new KURLRequester(napp->saveDirectory(), dlSaverFrame);
	dlsaver->setBuddy(mDlSaver);
	connect( mDlSaver, SIGNAL( openFileDialog( KURLRequester * )),
		 this, SLOT( slotRequesterClicked( KURLRequester * )));
	QWhatsThis::add(mDlSaver, i18n("When opening a non-local file, download it to the selected directory."));

	QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
	layout->addWidget(mLoopList);
	layout->addWidget(mAutoPlay);
	layout->addWidget(mOneInstance);
	layout->addWidget(mClearOnOpen);
	layout->addWidget(mHackUpPlaylist);
	layout->addWidget(dlSaverFrame);
	layout->addStretch();

	QHBoxLayout *layoutSaver = new QHBoxLayout(dlSaverFrame, KDialog::marginHint(), KDialog::spacingHint());
	layoutSaver->addWidget(dlsaver);
	layoutSaver->addWidget(mDlSaver);
}


void General::save()
{
	napp->setLoopList(mLoopList->isChecked());
	napp->setAutoPlay(mAutoPlay->isChecked());
	napp->setOneInstance(mOneInstance->isChecked());
	napp->setClearOnOpen(mClearOnOpen->isChecked());
	napp->setSaveDirectory(mDlSaver->url());
	napp->setHackUpPlaylist(mHackUpPlaylist->isChecked());
}

void General::slotRequesterClicked( KURLRequester *requester )
{
	mDlSaver->fileDialog()->setMode(
		(KFile::Mode)(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly));
}


/*****************************************************************
 * Plugins
 *****************************************************************/

PluginListView::PluginListView(QWidget *parent) : KListView(parent)
{
}

bool PluginListView::acceptDrag(QDropEvent *event) const
{
	return QCString(event->format())=="application/x-noatun-playlistviewitem";
}

QDragObject *PluginListView::dragObject() const
{
	if (!currentItem()) return 0;
	return new QStoredDrag("application/x-noatun-playlistviewitem", viewport());
}

class PluginListItem : public QListViewItem
{
public:
	PluginListItem(QListView *parent, const NoatunLibraryInfo &i, QListViewItem *after);
	const NoatunLibraryInfo &info() const { return mInfo; }

private:
	NoatunLibraryInfo mInfo;

};

PluginListItem::PluginListItem(QListView *parent, const NoatunLibraryInfo &i, QListViewItem *after)
	: QListViewItem(parent, after, i.name, i.comment), mInfo(i)
{}


Plugins::Plugins(QWidget *parent, char *name) : CModule(parent, name)
{
	(new QVBoxLayout(this))->setAutoAdd(true);
	mSplitter=new QSplitter(Qt::Vertical, this);
	mPlugins=new PluginListView(mSplitter);
	QWhatsThis::add(mPlugins, i18n("This shows a list of activated plugins. Dragging them to the list below will disable them."));

	{ // the arrows
		QWidget *arrows = new QWidget(mSplitter);
		QHBoxLayout *layout = new QHBoxLayout(arrows, KDialog::marginHint(), KDialog::spacingHint());
		layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));

		(mAdd = new QPushButton(arrows))->setFixedSize(22, 22);
		mAdd->setEnabled(false);
		mAdd->setPixmap(BarIcon("up", KIcon::SizeSmall));
		QWhatsThis::add(mAdd, i18n("Enable the selected plugin."));
		layout->addWidget(mAdd);
		connect(mAdd, SIGNAL(clicked()), SLOT(moveUp()));

		(mRemove =new QPushButton(arrows))->setFixedSize(22, 22);
		mRemove->setEnabled(false);
		mRemove->setPixmap(BarIcon("down", KIcon::SizeSmall));
		QWhatsThis::add(mRemove, i18n("Disable the selected plugin."));
		layout->addWidget(mRemove);
		connect(mRemove, SIGNAL(clicked()), SLOT(moveDown()));

		layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));

		arrows->setMaximumHeight(arrows->sizeHint().height());
	}

	mAvailable=new PluginListView(mSplitter);
	QWhatsThis::add(mAvailable, i18n("This is a list of available plugins. To activate one, drag it to the above list."));

	connect(mPlugins, SIGNAL(dropped(QDropEvent*, QListViewItem*)), SLOT(dropEvent(QDropEvent*, QListViewItem*)));
	connect(mAvailable, SIGNAL(dropped(QDropEvent*, QListViewItem*)), SLOT(removeDropEvent(QDropEvent*, QListViewItem*)));

	connect(mPlugins, SIGNAL(selectionChanged(void)), this, SLOT(pluginsChanged(void)));
	connect(mAvailable, SIGNAL(selectionChanged(void)), this, SLOT(availableChanged(void)));

	mPlugins->setAcceptDrops(true);
	mPlugins->setSorting(0);
	mPlugins->setDropVisualizer(false);
	mPlugins->setSelectionMode(QListView::Single);
	mPlugins->addColumn(i18n("Name"));
	mPlugins->addColumn(i18n("Description"));
	mPlugins->setDragEnabled(true);

	mAvailable->setAcceptDrops(true);
	mAvailable->setSorting(0);
	mAvailable->setSelectionMode(QListView::Single);
	mAvailable->addColumn(i18n("Name"));
	mAvailable->addColumn(i18n("Description"));
	mAvailable->setDragEnabled(true);
	mAvailable->setDropVisualizer(false);
}

void Plugins::reopen()
{
	mAvailable->clear();
	mPlugins->clear();
	QValueList<NoatunLibraryInfo> availible=napp->libraryLoader()->availible();
	for (QValueList<NoatunLibraryInfo>::Iterator i=availible.begin(); i!=availible.end(); ++i)
		new PluginListItem(mAvailable, *i, 0);

	// Now move all the appropriate items..
	QValueList<NoatunLibraryInfo> loaded=napp->libraryLoader()->loaded();
	for (QValueList<NoatunLibraryInfo>::Iterator i=loaded.begin(); i!=loaded.end(); ++i)
		addPlugin(findInfo(&*i), mPlugins->lastItem(), true);

}

void Plugins::dropEvent(QDropEvent *drop, QListViewItem *after)
{
	if (drop->source()!=mAvailable->viewport()) return;
	addPlugin(mAvailable->currentItem(), after);
}

void Plugins::removeDropEvent(QDropEvent *drop, QListViewItem *)
{
	if (drop->source()!=mPlugins->viewport()) return;
	removePlugin(mPlugins->currentItem());
}

void Plugins::addPlugin(QListViewItem *i, QListViewItem *after, bool init)
{
	if (!i) return;

	const NoatunLibraryInfo &info=static_cast<PluginListItem*>(i)->info();

	// Make sure it is the only playlist
	if (info.type=="playlist")
	{
		for (QListViewItem *j=mPlugins->firstChild(); j!=0; j=j->itemBelow())
		{
			const NoatunLibraryInfo &jinfo = static_cast<PluginListItem *>(j)->info();
			if(jinfo.type =="playlist")
			{
				removePlugin(j, true);
			}
		}
	}

	for (QStringList::ConstIterator it = info.require.begin(); it != info.require.end(); ++it)
	{
		NoatunLibraryInfo requiredInfo = napp->libraryLoader()->getInfo(*it);
		addPlugin(findInfo(&requiredInfo), after);
	}
	if (mDeleted.contains(info.specfile))
		mDeleted.remove(info.specfile);
	else if (!init && !mAdded.contains(info.specfile))
		mAdded.append(info.specfile);
	new PluginListItem(mPlugins, info, after);
	delete i;
}

void Plugins::removePlugin(QListViewItem *i, bool force)
{
	if(!i) return;
	const NoatunLibraryInfo &info=static_cast<PluginListItem*>(i)->info();

	// Make sure it is not a playlist or Marquis
	if ((info.type=="playlist" || info.type=="sm") && !force)
		return;
	// make sure this is not the only UI
	int uicount=0;
	if ((info.type=="userinterface") && !force)
	{
		for (QListViewItem *i=mPlugins->firstChild(); i!=0; i=i->itemBelow())
			if (static_cast<PluginListItem*>(i)->info().type=="userinterface")
				uicount++;
		if (uicount<=1)
			return;
	}

	for (QListViewItem *dep=mPlugins->firstChild(); dep!=0; dep=dep->itemBelow())
	{
		const NoatunLibraryInfo &requiredInfo=static_cast<PluginListItem*>(dep)->info();
		for (QStringList::ConstIterator it = requiredInfo.require.begin(); it != requiredInfo.require.end(); ++it)
			if (*it == info.specfile)
		{
			removePlugin(findInfo(&requiredInfo));
		}
	}
	if (mAdded.contains(info.specfile))
		mAdded.remove(info.specfile);
	else if (!mDeleted.contains(info.specfile))
		mDeleted.append(info.specfile);
	new PluginListItem(mAvailable, info, 0);
	delete i;
}


QListViewItem *Plugins::findInfo(const NoatunLibraryInfo *i) const
{
	for (QListViewItem *inf=mAvailable->firstChild(); inf!=0; inf=inf->itemBelow())
	    if (static_cast<PluginListItem*>(inf)->info().specfile==i->specfile)
		return inf;
	for (QListViewItem *inf=mPlugins->firstChild(); inf!=0; inf=inf->itemBelow())
	    if (static_cast<PluginListItem*>(inf)->info().specfile==i->specfile)
		return inf;
	return 0;
}

void Plugins::save()
{
	// add the new modules
	for (QStringList::Iterator i=mAdded.begin(); i!=mAdded.end(); ++i)
		napp->libraryLoader()->add(*i);

	QStringList specList(mAdded);


	// remove the deleted ones
	for (QStringList::Iterator i=mDeleted.begin(); i!=mDeleted.end(); ++i)
		napp->libraryLoader()->remove(*i);

	// handle for the ones that are already loaded;
	QValueList<NoatunLibraryInfo> loaded=napp->libraryLoader()->loaded();
	for (QValueList<NoatunLibraryInfo>::Iterator i=loaded.begin(); i!=loaded.end(); ++i)
		if (!specList.contains((*i).specfile))
			if (napp->libraryLoader()->isLoaded((*i).specfile))
				specList+=(*i).specfile;
	napp->libraryLoader()->setModules(specList);


	mDeleted.clear();
	mAdded.clear();
}

void Plugins::moveUp(void)
{
	if(!mAvailable->selectedItem()) return;
	addPlugin(mAvailable->selectedItem(), mPlugins->lastItem());
}

void Plugins::moveDown(void)
{
	if(!mPlugins->selectedItem()) return;
	removePlugin(mPlugins->selectedItem());
}

void Plugins::availableChanged(void)
{
	mAdd->setEnabled(mAvailable->selectedItem() != 0);
}

void Plugins::pluginsChanged(void)
{
	mRemove->setEnabled(mPlugins->selectedItem() != 0);
}

#include "cmodule.moc"
