// tron.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 <kaction.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kdialog.h>
#include <kfiledialog.h>
#include <kglobal.h>
#include <klocale.h>
#include <kstdaction.h>
#include <kstddirs.h>
#include <kurldrag.h>
#include <qframe.h>
#include <qlayout.h>
#include <qurl.h>

#include "mcp.h"
#include "tron.h"

//////////////////////
// TronListViewItem //
//////////////////////

TronListViewItem::TronListViewItem(QListView *parent, QListViewItem *after, QString t1, QString t2)
	: QListViewItem(parent, after, t1, t2)
	, special(false)
{
}

TronListViewItem::~TronListViewItem()
{
}

void TronListViewItem::setSpecial(bool b)
{
	special = b;
	repaint();
}

bool TronListViewItem::isSpecial(void)
{
	return special;
}

void TronListViewItem::paintCell(QPainter *painter, const QColorGroup &_group, int column, int width, int alignment)
{
	QColorGroup group = _group;
	if(special)
	{
		// TODO: make the color configurable, or based on a standard
		group.setColor(QColorGroup::Base, qRgb(0xFF, 0x00, 0x00));
		group.setColor(QColorGroup::Text, qRgb(0xFF, 0xFF, 0xFF));
		group.setColor(QColorGroup::Highlight, qRgb(0xFF, 0x70, 0x70));
		group.setColor(QColorGroup::HighlightedText, qRgb(0xFF, 0xFF, 0xFF));
	}
	QListViewItem::paintCell(painter, group, column, width, alignment);
}

//////////////////
// TronListView //
//////////////////

TronListView::TronListView(QWidget *parent, const char *name)
	: KListView(parent, name)
	, special(0)
{
	connect( this, SIGNAL(dropped(QDropEvent *, QListViewItem *)), this, SLOT(myDropEvent(QDropEvent *, QListViewItem *)) );
}

TronListView::~TronListView()
{
}

void TronListView::setSpecialItem(TronListViewItem *item)
{
	TronListViewItem *oldItem = dynamic_cast<TronListViewItem *>(special);

	if(oldItem && oldItem->listView() == this)
	{
		special->setSpecial(false);
	}

	if(!item || item->listView() != this)
	{
		special = 0;
	}
	else
	{
		special = item;
		special->setSpecial(true);
	}
}

TronListViewItem *TronListView::specialItem(void)
{
	return special;
}

void TronListView::clear(void)
{
	special = 0;
	KListView::clear();
}

void TronListView::takeItem(QListViewItem *item)
{
	if(item == special) special = 0;
	KListView::takeItem(item);
}

bool TronListView::acceptDrag(QDropEvent *e) const
{
	KURL::List urls;
	bool retval = QUriDrag::canDecode(e) || KListView::acceptDrag(e);
	return retval;
}

void TronListView::myDropEvent(QDropEvent *e, QListViewItem *after)
{
	KURL::List urls;
	if(KURLDrag::decode(e, urls))
	{
		emit dropped(urls, after);
	}
}

//////////
// Tron //
//////////

Tron::Tron()
	: KMainWindow()
	, Plugin()
{
	NOATUNPLUGINC(Tron);
	(void)KStdAction::openNew(this, SLOT(file_opennew()), actionCollection());
	(void)KStdAction::open(this, SLOT(file_open()), actionCollection());
	(void)KStdAction::save(this, SLOT(file_save()), actionCollection());
	(void)KStdAction::saveAs(this, SLOT(file_saveas()), actionCollection());
	(void)KStdAction::close(this, SLOT(hide()), actionCollection());
	(void)new KAction(i18n("&Add..."), 0, this, SLOT(edit_add()), actionCollection(), "edit_add");
	edit_removeAction = new KAction(i18n("&Remove"), 0, this, SLOT(edit_remove()), actionCollection(), "edit_remove");
	options_shuffleAction = new KToggleAction(i18n("&Shuffle"), 0, this, SLOT(options_shuffle()), actionCollection(), "options_shuffle");
	(void)KStdAction::preferences(napp, SLOT(preferences()), actionCollection());

	edit_removeAction->setEnabled(false);

	createGUI("tronui.rc");

	list = new MCP(this);
}

Tron::~Tron()
{
	KConfig &config = *KGlobal::config();
	config.setGroup("tron");
	config.writeEntry("modified", currentPlayListModified);
	config.writeEntry("currentPlayList", currentPlayListURL.path());
	config.writeEntry("width", width());
	config.writeEntry("height", height());
	config.writeEntry("shuffle", options_shuffleAction->isChecked());

	if(listView->specialItem())
	{
		int specialNum = 0;
		for(QListViewItem *cur = listView->firstChild(); cur != listView->specialItem(); cur = cur->itemBelow())
			specialNum++;
		config.writeEntry("current", specialNum);
	}
	else
	{
		config.writeEntry("current", -1);
	}

	KURL url(QUrl(napp->dirs()->saveLocation("data", "noatun/") + "trondata"));
	list->save(url);

	delete list;
	list = 0;
}

void Tron::init(void)
{
	connect( list, SIGNAL(cleared()), this, SLOT(cleared()) );
	connect( list, SIGNAL(loaded()), this, SLOT(loaded()) );
	connect( list, SIGNAL(saved()), this, SLOT(saved()) );
	connect( list, SIGNAL(added(PlayListItem *)), this, SLOT(tronadded(PlayListItem *)) );
	connect( list, SIGNAL(removed(PlayListItem *)), this, SLOT(removed(PlayListItem *)) );
	connect( list, SIGNAL(modified(PlayListItem *)), this, SLOT(modified(PlayListItem *)) );
	connect( list, SIGNAL(current(PlayListItem *)), this, SLOT(current(PlayListItem *)) );
	connect( list, SIGNAL(moved(PlayListItem *, PlayListItem *)), this, SLOT(moved(PlayListItem *, PlayListItem *)) );

	QFrame *box = new QFrame(this);
	setCentralWidget(box);

	listView = new TronListView(box);
	listView->setAcceptDrops(true);
	listView->setDragEnabled(true);
	listView->setSorting(-1);
	listView->setMultiSelection(true);
	listView->setSelectionModeExt(KListView::Extended);
	listView->setAllColumnsShowFocus(true);
	listView->addColumn(i18n("Title"));
	listView->addColumn(i18n("Length"));
	connect( listView, SIGNAL(moved(QListViewItem *, QListViewItem *, QListViewItem *)), this, SLOT(itemMoved(QListViewItem *, QListViewItem *, QListViewItem *)) );
	connect( listView, SIGNAL(executed(QListViewItem *)), this, SLOT(itemExecuted(QListViewItem *)) );
	connect( listView, SIGNAL(dropped(KURL::List, QListViewItem *)), this, SLOT(itemDropped(KURL::List, QListViewItem *)) );
	connect( listView, SIGNAL(selectionChanged()), this, SLOT(itemSelectionChanged(void)) );

	QVBoxLayout *layout = new QVBoxLayout(box, KDialog::marginHint(), KDialog::spacingHint());
	layout->addWidget(listView);

	hide();

	KURL url(QUrl(napp->dirs()->saveLocation("data", "noatun/") + "trondata"));
	list->load(url, true);

	// this has to come after the load, because of the modified flag
	KConfig &config = *KGlobal::config();
	config.setGroup("tron");
	currentPlayListURL = KURL(QUrl(config.readEntry("currentPlayList", QString::null)));
	setModified(config.readBoolEntry("modified", false));
	
	int savedWidth = config.readLongNumEntry("width", 0);
	int savedHeight = config.readLongNumEntry("height", 0);
	if(savedWidth && savedHeight) resize(savedWidth, savedHeight);

	list->reset();
	int savedCurrent = config.readLongNumEntry("current", -1);
	for(int i = 0; i < savedCurrent; i++)
		list->next();

	options_shuffleAction->setChecked(config.readBoolEntry("shuffle", false));
	options_shuffle();
}

PlayList *Tron::playlist(void) const
{
	return list;
}

void Tron::closeEvent(QCloseEvent *)
{
	hide();
}

void Tron::hideEvent(QHideEvent *e)
{
	if(list) list->hideList();
	KMainWindow::hideEvent(e);
}

void Tron::showEvent(QShowEvent *e)
{
	if(list) list->showList();
	KMainWindow::showEvent(e);
}

void Tron::itemMoved(QListViewItem *item, QListViewItem *, QListViewItem *newSibling)
{
	list->moveAfter(viewMap[(TronListViewItem *)item], viewMap[(TronListViewItem *)newSibling]);
}

void Tron::itemExecuted(QListViewItem *item)
{
	list->play(viewMap[(TronListViewItem *)item]);
}

void Tron::itemDropped(KURL::List urlList, QListViewItem *viewItem)
{
	PlayListItem *item = viewMap[static_cast<TronListViewItem *>(viewItem)];

	for (KURL::List::Iterator i = urlList.begin(); i != urlList.end(); ++i)
	{
		if((*i).fileName().right(4) == ".m3u")
			item = list->importWindowsPlayList((*i), item);
		// I hope you're happy, Charles.. I *could* have opened the file and looked
		// for <!DOCTYPE NoatunPlaylist>, but I gave it a filename extension instead
		else if((*i).fileName().right(13) == ".tronplaylist")
			item = list->importPlayList((*i), item);
		else
			item = list->addFile((*i), item);
		// TODO: handle when the user drops a directory
	}
}

void Tron::itemSelectionChanged(void)
{
	QList<QListViewItem> selected = listView->selectedItems();
	edit_removeAction->setEnabled( selected.count() > 0 );
}

void Tron::fillView(void)
{
	listView->clear();
	viewMap.clear();
	playMap.clear();
	for(PlayListItem *cur = list->getFirst(); cur; cur = list->getAfter(cur))
	{
		addItem(cur);
	}
}

void Tron::setModified(bool b)
{
	currentPlayListModified = b;

	QString caption;
	if(currentPlayListURL.isEmpty())
		caption = i18n("Playlist");
	else
		caption = currentPlayListURL.path();
	setCaption(caption, currentPlayListModified);
}

void Tron::addItem(PlayListItem *item)
{
	TronListViewItem *viewItem = new TronListViewItem(listView, listView->lastItem());
	viewMap.insert(viewItem, item);
	playMap.insert(item, viewItem);
	initializeItem(item);
}

void Tron::initializeItem(PlayListItem *item)
{
	TronListViewItem *viewItem = playMap[item];
	viewItem->setText(0, item->title());
	if(item->lengthString() == "00:-1")
		viewItem->setText(1, QString::null);
	else
		viewItem->setText(1, item->lengthString());
}

// List Actions
//
// These call setModified
void Tron::cleared(void)
{
	listView->clear();
	viewMap.clear();
	playMap.clear();
	currentPlayListURL = KURL();
	setModified(true);
}

void Tron::loaded(void)
{
	fillView();
	setModified(false);
}

void Tron::saved(void)
{
	setModified(false);
}

void Tron::tronadded(PlayListItem *item)
{
	addItem(item);
	setModified(true);
}

void Tron::removed(PlayListItem *item)
{
	TronListViewItem *viewItem = playMap[item];
	listView->takeItem(viewItem);
	delete viewItem;

	viewMap.remove(viewItem);
	playMap.remove(item);
	setModified(true);
}

void Tron::modified(PlayListItem *item)
{
	initializeItem(item);
	setModified(true);
}

void Tron::current(PlayListItem *item)
{
	TronListViewItem *viewItem = playMap[item];
	listView->setSpecialItem(viewItem);
	listView->ensureItemVisible(viewItem);
}

void Tron::moved(PlayListItem *item, PlayListItem *itemAfter)
{
	TronListViewItem *viewItem = playMap[item];

	if(itemAfter)
	{
		TronListViewItem *viewItemAfter = playMap[itemAfter];
		listView->moveItem(viewItem, 0, viewItemAfter);
	}
	else
	{
		listView->moveItem(viewItem, 0, 0);
	}
	setModified(true);
}

// GUI Actions
//
// These do not call setModified, they tell the list what to do
void Tron::file_opennew(void)
{
	currentPlayListURL = KURL();
	list->clear();
}

void Tron::file_open(void)
{
	KURL url = KFileDialog::getOpenURL(QString::null, "*.tronplaylist\n*");
	if(!url.isEmpty())
	{
		currentPlayListURL = url;
		list->load(currentPlayListURL);
	}
}

void Tron::file_save(void)
{
	if(currentPlayListURL.isEmpty())
		file_saveas();
	else
		list->save(currentPlayListURL);
}

void Tron::file_saveas(void)
{
	KURL url = KFileDialog::getSaveURL(currentPlayListURL.path(), "*.tronplaylist\n*");
	if(!url.isEmpty())
	{
		currentPlayListURL = url;
		file_save();
	}
}

void Tron::edit_add(void)
{
	KURL::List list = KFileDialog::getOpenURLs(QString::null, "*");
	itemDropped(list, listView->lastItem());
}

void Tron::edit_remove(void)
{
	QList<QListViewItem> selected = listView->selectedItems();
	for (QListIterator<QListViewItem> i(selected); i.current(); ++i)
		list->remove(viewMap[ static_cast<TronListViewItem *>(*i) ]);
}

void Tron::options_shuffle(void)
{
	list->setShuffle(options_shuffleAction->isChecked());
}

#include "tron.moc"
