/**
 * Copyright (c) 2000-2001 Charles Samuels <charles@kde.org>
 *               2000-2001 Neil Stevens <neil@qualityassistant.com>
 *                    2001 Klas Kalass <klas.kalass@gmx.de>
 **/

#include <iostream.h>
#include <qlayout.h>
#include <klocale.h>
#include <kdebug.h>
#include <kstdaction.h>
#include <qheader.h>
#include <qdragobject.h>
#include <kaction.h>
#include <kmenubar.h>
#include <kfiledialog.h>
#include <qtextstream.h>
#include <noatunapp.h>
#include <player.h>
#include <kio/netaccess.h>
#include <ksimpleconfig.h>
#include <kstddirs.h>
#include <kurldrag.h>

#include "playlist.h"
#include "view.h"

#define SPL SplitPlayList::SPL()

SafeListViewItem::SafeListViewItem(QListView *parent, QListViewItem *after, const KURL &text)
	: QCheckListItem(parent,0, QCheckListItem::CheckBox), PlayListItem(text)
{
	static_cast<KListView*>(parent)->moveItem(this, 0, after);
	setOn(true);
	setText(0,title());

	SplitPlayList *p=SPL;
	if (!(p->currentItem || p->nextItem || p->previousItem))
		p->setCurrent(this);
	if (p->currentItem==(SafeListViewItem*)itemAbove())
		p->setNext(this);
	if (p->currentItem==(SafeListViewItem*)itemBelow())
		p->setPrevious(this);
}

SafeListViewItem::~SafeListViewItem()
{
	if (SPL->nextItem==this)
		SPL->setNext(static_cast<SafeListViewItem*>(itemBelow()));
	if (SPL->currentItem==this)
		SPL->setCurrent(0), SPL->setNext(static_cast<SafeListViewItem*>(itemBelow()));
	if (SPL->previousItem==this)
		SPL->setPrevious(static_cast<SafeListViewItem*>(itemAbove()));
}

void SafeListViewItem::downloaded(int percent)
{
	setText(1, QString::number(percent)+'%');
}

void SafeListViewItem::modified()
{
	setText(0, title());
	if (isDownloaded())
		setText(1, lengthString());
}

List::List(View *parent)
	: KListView(parent)
{
	addColumn(i18n("File"));
	addColumn(i18n("Time"));
	setAcceptDrops(true);
	setSorting(-1);
	setDropVisualizer(true);
	setDragEnabled(true);
	setItemsMovable(true);
	setSelectionMode(QListView::Extended);
	connect(this, SIGNAL(dropped(QDropEvent*, QListViewItem*)), SLOT(dropEvent(QDropEvent*, QListViewItem*)));
	connect(this, SIGNAL(moved(QList<QListViewItem>&,QList<QListViewItem>&,QList<QListViewItem>&)), SLOT(move(QList<QListViewItem>&,QList<QListViewItem>&,QList<QListViewItem>&)));
}

List::~List()
{
}

void List::move(QList<QListViewItem>& item, QList<QListViewItem>&, QList<QListViewItem>&)
{
	bool bidimerge=static_cast<bool>(item.containsRef(static_cast<SafeListViewItem*>(SPL->currentItem)));
	bool current=SPL->currentItem;
	if (current)
	{
		if (bidimerge || item.containsRef(static_cast<SafeListViewItem*>(SPL->previousItem))
		              || (current && item.containsRef(static_cast<SafeListViewItem*>(SPL->currentItem)->itemAbove())))
			SPL->setPrevious(static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(SPL->currentItem)->itemAbove()));
	
		if (bidimerge || item.containsRef(static_cast<SafeListViewItem*>(SPL->nextItem))
		              || item.containsRef(static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(SPL->currentItem)->itemBelow())))
			SPL->setNext(static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(SPL->currentItem)->itemBelow()));
	}
	
	emit modified();
}

bool List::acceptDrag(QDropEvent *event) const
{
	return QUriDrag::canDecode(event) || KListView::acceptDrag(event);
}

void List::dropEvent(QDropEvent *event, QListViewItem *after)
{
	KURL::List textlist;
	if (!KURLDrag::decode(event, textlist)) return;
	event->acceptAction();
	
	for (KURL::List::Iterator i=textlist.begin(); i != textlist.end(); ++i)
	{
		// TODO: there must be a better way.. this isn't windows
		if ((*i).path().right(4)==".m3u")
			after=openGlobal(*i, after);
		else
			after=new SafeListViewItem(this, after, *i);
	}

	emit modified();
}

void List::viewportPaintEvent(QPaintEvent *e)
{
	KListView::viewportPaintEvent(e);
	
	static bool recurse=false;
	if (!recurse)
	{
		QListViewItem *current=static_cast<SafeListViewItem*>(SPL->current());
		QRect item(itemRect(current).intersect(e->rect()));
		if (item.isEmpty()) return;

		QPixmap pix(e->rect().width(), e->rect().height());
		pix.fill(QColor(255,255,255));
		
		recurse=true;
		viewport()->repaint(item,true);
		recurse=false;
	
		bitBlt(viewport(), item.topLeft(), &pix, QRect(0,0, item.width(), item.height()), XorROP,false);
	}
}

// for m3u files
QListViewItem *List::openGlobal(const KURL &u, QListViewItem *after)
{
	QString local;
	if(KIO::NetAccess::download(u, local))
	{
		QFile saver(local);
		saver.open(IO_ReadWrite);
		QTextStream t(&saver);
		QString file;
		while (!t.eof())
		{
			file=t.readLine();
			if (!file.isNull())
			{
				KURL u1;
				if (file.find('/')) // we have to deal with a relative path
				{
					u1.setPath(u.path(0));
					u1.setFileName(file);
				}
				else
					u1.setPath(file);
				after=addFile(u1, false, after);
			}
		}
		
		KIO::NetAccess::removeTempFile(local);
	}
	return after;
}

QListViewItem *List::addFile(const KURL& url, bool play, QListViewItem *after)
{
	if (!after) after=lastItem();
	QListViewItem *i=new SafeListViewItem(this, after, url);
	if (play)
		SPL->listItemSelected(i);
	emit modified();
	return i;
}

View::View(SplitPlayList *)
	: KMainWindow(0,0)
{
	list=new List(this);
	list->show();
	setCentralWidget(list);
	connect(list, SIGNAL(modified(void)), this, SLOT(setModified(void)) );
	
	tb = new KToolBar( this );
	addToolBar(tb);
	
	(mOpen=new KAction(i18n("Add &Files"), "queue", 0, this, SLOT(addFiles()),this, "open"))->plug(tb);
	(mDelete=new KAction(i18n("Delete"), "editdelete", 0, this, SLOT(deleteSelected()), this, "delete"))->plug(tb);
	(new KAction(i18n("Add &Directories"), "folder", 0, this, SLOT(addDirectory()), this, "add_dir"))->plug(tb);
	
	(new KActionSeparator(this))->plug(tb);
	(mSave=KStdAction::save(this, SLOT(save()), this, "save"))->plug(tb);
	(mSaveAs=KStdAction::saveAs(this, SLOT(saveAs()), this, "saveas"))->plug(tb);
	(mOpenpl=KStdAction::open(this, SLOT(open()), this, "open"))->plug(tb);
	(mOpenNew=KStdAction::openNew(this, SLOT(openNew()), this, "openNew"))->plug(tb);
	(new KAction(i18n("Shuffle"), "misc", 0, SPL, SLOT( randomize() ), this, "clear"))->plug(tb);
	(new KAction(i18n("Clear"), "fileclose", 0, this, SLOT( clearView() ), this, "clear"))->plug(tb);
}

View::~View()
{
	hide();
	saveState();
	delete list;
	delete tb;
	mOpen->unplug(tb);
	mDelete->unplug(tb);
}

void View::init()
{
	KURL internalURL;
	internalURL.setPath(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylistdata");
	list->openGlobal(internalURL);

	KConfig &config = *KGlobal::config();
	config.setGroup("splitplaylist");

	// this has to come after openGlobal, since openGlobal emits modified()
	setModified(config.readBoolEntry("modified", false));
	mPlaylistFile.setPath(config.readEntry("file", QString::null));

	SPL->reset();
	int saved = config.readNumEntry("current", 0);

	PlayListItem *item=SPL->getFirst();
	for(int i = 0 ; i < saved ; i++)
	{
		item=SPL->getAfter(item);
	}
	if (item)
		SPL->setCurrent(item);
}

void View::save()
{
	if (mPlaylistFile.isMalformed()) { saveAs(); return; }
	saveToURL(mPlaylistFile);
	setModified(false);
}

void View::saveAs()
{
	KURL u=KFileDialog::getSaveURL(0, "*.m3u\n*", this, i18n("Save Playlist"));
	if (u.isMalformed()) return;
	mPlaylistFile = u;
	save();
}

void View::open()
{
	KURL u=KFileDialog::getOpenURL(0, "*.m3u\n*", this, i18n("Insert Playlist"));
	if (u.isMalformed()) return;
	list->openGlobal(u);
	setModified(false);
}

void View::openNew()
{
	KURL u=KFileDialog::getOpenURL(0, "*.m3u\n*", this, i18n("Open New Playlist"));
	if (u.isMalformed()) return;
	mPlaylistFile = u;

	listView()->clear();
	list->openGlobal(mPlaylistFile);

	setModified(false);
}

void View::deleteSelected()
{
	QList<QListViewItem> items(list->selectedItems());
	for (QListIterator<QListViewItem> it(items); it.current(); ++it)
		delete *it;

	setModified(true);
}

void View::addFiles()
{
	KURL::List files=KFileDialog::getOpenURLs(0, napp->shellGlob(), this, i18n("Select a File to Play"));

	for(KURL::List::Iterator it=files.begin(); it!=files.end(); ++it)
		addFile(KURL(*it));
	
	setModified(true);
}

void View::addDirectory()
{
	QString file=KFileDialog::getExistingDirectory(0, this, i18n("Select a Directory"));

	if (!file) return;
	addDirectory(KURL(file));
	
	setModified(true);
}

void View::addDirectory(const KURL &dir)
{
	KDirLister *newLister = new KDirLister();
	connect(newLister, SIGNAL(completed()), SLOT(listDone()));
	newLister->openURL(dir, false);
}

void View::listDone()
{
	// WildFox:
	// aarrrrgl *hack* we need to use KIO::listRecursive somewhen
	// as suggested by tronical cause this is *ugly*
	KDirLister *curLister = (KDirLister*)(static_cast<const KDirLister*>(sender()));
	QList<KFileItem> items=curLister->items();
	for (KFileItem *i=items.first(); i!=0; i=items.next())
	{
		if (i->isDir())
			addDirectory(i->url());
		else
			addFile(i->url());
	}
}

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

void View::showEvent(QShowEvent *)
{
	emit shown();
}

void View::hideEvent(QHideEvent *)
{
	emit hidden();
}

void View::setModified(bool b)
{
	modified = b;
	setCaption(i18n("Playlist"), modified);
}

void View::setModified(void)
{
	setModified(true);
}

void View::saveToURL(const KURL &url)
{
	QString local(napp->tempSaveName(url.path()));
	QFile saver(local);
	saver.open(IO_ReadWrite | IO_Append);
	QTextStream t(&saver);
	// navigate the list
	for (SafeListViewItem *i=static_cast<SafeListViewItem*>(listView()->firstChild());
	     i != 0; i=static_cast<SafeListViewItem*>(i->itemBelow()))
		t<< *i << '\n';
	saver.close();

	KIO::NetAccess::upload(local, url);

	saver.remove();
}

void View::saveState(void)
{
	KConfig &config = *KGlobal::config();
	config.setGroup("splitplaylist");

	config.writeEntry("modified", modified);
	config.writeEntry("file", mPlaylistFile.path());
	saveToURL(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylistdata");

	int i;
	PlayListItem *item=SPL->getFirst();
	for(i = 0; item != SPL->current(); )
		item=SPL->getAfter(item), i++;

	config.writeEntry("current", i);

	config.sync();
}

#include "view.moc"

