// Copyright (c) 2003 Charles Samuels <charles@kde.org>
// See the file COPYING for redistribution terms.

#include "tree.h"
#include "file.h"
#include "query.h"
#include "menu.h"
#include "oblique.h"

#include <qpainter.h>
#include <iostream>

#include <klocale.h>


TreeItem::TreeItem(Tree *parent, QueryGroup *group, const File &file, const QString &p)
	: KListViewItem(parent, p), mGroup(group), mUserOpened(false), mHidden(false)
{
	if (group->option(QueryGroup::Playable))
		mFile = file;
}

TreeItem::TreeItem(TreeItem *parent, QueryGroup *group, const File &file, const QString &p)
	: KListViewItem(parent, p), mGroup(group), mUserOpened(false), mHidden(false)
{
	if (group->option(QueryGroup::Playable))
		mFile = file;
}

TreeItem::~TreeItem()
{
	// I have to remove my children, because they need their parent
	// in tact for the below code
	while (TreeItem *c = firstChild())
		delete c;
	tree()->deleted(this);
}

void Tree::deleted(TreeItem *item)
{
	mAutoExpanded.removeRef(item);
	if (current() == item)
	{
		oblique()->next();
	}
}

static void pad(QString &str)
{
	int len=str.length();
	int at = 0;
	int blocklen=0;

	static const int paddingsize=12;

	// not static for reason
	const QChar chars[paddingsize] =
	{
		QChar('0'), QChar('0'), QChar('0'), QChar('0'),
		QChar('0'), QChar('0'), QChar('0'), QChar('0'),
		QChar('0'), QChar('0'), QChar('0'), QChar('0')
	};

	for (int i=0; i < len; i++)
	{
		if (str[i].isNumber())
		{
			if (!blocklen)
				at = i;
			blocklen++;
		}
		else if (blocklen)
		{
			int pads=paddingsize;
			pads -= blocklen;
			str.insert(at, chars, pads);
			i += pads;
			blocklen = 0;
		}
	}
	if (blocklen)
	{
		int pads=paddingsize;
		pads -= blocklen;
		str.insert(at, chars, pads);
	}
}

int TreeItem::compare(QListViewItem * i, int col, bool) const
{
	QString text1 = text(col);
	QString text2 = i->text(col);

	pad(text1);
	pad(text2);
	return text1.compare(text2);
}


Tree *TreeItem::tree()
{
	return static_cast<Tree*>(KListViewItem::listView());
}

TreeItem *TreeItem::find(File item)
{
	TreeItem *i = firstChild();
	while (i)
	{
		if (i->file() == item) return i;

		TreeItem *found = i->find(item);
		if (found and found->playable()) return found;
		i = i->nextSibling();
	}
	return 0;
}

bool TreeItem::playable() const
{
	return mFile && mGroup->option(QueryGroup::Playable);
}

TreeItem *TreeItem::nextPlayable()
{
	TreeItem *next=this;
	do
	{
		next = next->next();
	} while (next && !next->playable());
	return next;
}

void TreeItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align)
{
	QFont font = p->font();
	if (tree()->current() == this)
	{
		font.setUnderline(true);
		p->setFont(font);
	}
	
	QColorGroup newcg(cg);
	if (parent() && parent()->isOpen() && !parent()->mUserOpened)
	{
		// slow, but not often used
		QColor text = newcg.text();
		QColor bg = newcg.background();
		
		int r = text.red() + bg.red();
		int g = text.green() + bg.green();
		int b = text.blue() + bg.blue();
		text.setRgb(r/2,g/2,b/2);
		newcg.setColor(QColorGroup::Text, text);
	}
	KListViewItem::paintCell(p, newcg, column, width, align);

	font.setUnderline(false);
	p->setFont(font);
}

void TreeItem::setOpen(bool o)
{
	if (!tree()->autoExpanding())
	{
		mUserOpened = o;
		tree()->removeAutoExpanded(this);
	}
	KListViewItem::setOpen(o);
}

void TreeItem::autoExpand()
{
	tree()->setAutoExpanding(true);
	if (tree()->current() == this)
	{
		tree()->resetAutoExpanded();
		forceAutoExpand();
	}
	tree()->setAutoExpanding(false);
}

void TreeItem::forceAutoExpand()
{
	if (parent())
		parent()->forceAutoExpand();

	if (!mUserOpened)
		tree()->addAutoExpanded(this);
	setOpen(true);
}

bool TreeItem::hideIfNoMatch(const QString &match)
{
	if (!firstChild())
	{
		if (match.length())
		{
			if (!text(0).contains(match, false))
			{
				setHidden(true);
				return false;
			}
		}
		setHidden(false);
		return true;
	}
	else
	{
		bool visible=false;
		for (TreeItem *ch = firstChild(); ch; ch = ch->nextSibling())
		{
			bool here = ch->hideIfNoMatch(match);
			visible = visible || here;
		}

		if (visible)
		{
			setHidden(false);
		}
		else
		{
			if (match.length())
			{
				if (!text(0).contains(match, false))
				{
					setHidden(true);
					return false;
				}
			}
		}
		return true;
	}
}

void TreeItem::setup()
{
	QListViewItem::setup();
	if (mHidden)
		setHeight(0);
}

void TreeItem::setHidden(bool h)
{
	mHidden = h;
	setup();
}

TreeItem *TreeItem::next()
{
	if (firstChild())
	{
		return firstChild();
	}
	else
	{ // go up the tree
		TreeItem *upYours = this;
		do
		{
			if (upYours->nextSibling())
				return upYours->nextSibling();
			upYours = upYours->parent();
		} while (upYours);
	}
	return 0;
}




Tree::Tree(Oblique *oblique, Query *query, QWidget *parent)
	: KListView(parent), mQuery(query), mOblique(oblique), mAutoExpanding(0)
{
	mCurrent = 0;
	addColumn("");
	setCaption(i18n("Oblique"));
	setRootIsDecorated(true);
	((QWidget*)header())->hide();

	connect(
			this, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
			SLOT(contextMenu(KListView*, QListViewItem*, const QPoint&))
		);
	connect(
			this, SIGNAL(executed(QListViewItem*)),
			SLOT(play(QListViewItem*))
		);
}

Tree::~Tree()
{
	// have to clear here to prevent sigsegv on exit
	clear();
}

TreeItem *Tree::firstChild()
	{ return static_cast<TreeItem*>(KListView::firstChild()); }

TreeItem *Tree::find(File item)
{
	TreeItem *i = firstChild();

	while (i)
	{
		if (i->file() == item) return i;

		TreeItem *found = i->find(item);
		if (found) return found;

		i = i->nextSibling();
	}
	return i;
}

void Tree::reset(Query *q)
{
	clear();
	if (q) mQuery = q;
}

void Tree::insert(TreeItem *replace, File file)
{
	TreeItem *created = collate(file);
	if (mCurrent == replace)
	{
		setCurrent(created);
	}
	delete replace;
}

void Tree::insert(File file)
{
	collate(file);
}

void Tree::remove(File file)
{
	remove(firstChild(), file);
}

void Tree::update(File file)
{
	if (TreeItem *item = find(file))
	{
		insert(item, file);
	}
}

void Tree::remove(TreeItem *ti, const File &file)
{
	while (ti)
	{
		if (ti->file() == file)
		{
			TreeItem *t = ti->nextSibling();
			delete ti;
			ti = t;
		}
		else
		{
			remove(ti->firstChild(), file);
			ti = ti->nextSibling();
		}
	}
}

void Tree::setCurrent(TreeItem *cur)
{
	if (cur == mCurrent) return;
	// undo the old one
	TreeItem *old = mCurrent;
	mCurrent = cur;
	QPtrList<TreeItem> oldAutoExpanded = mAutoExpanded;
	mAutoExpanded.clear();
	repaintItem(old);
	repaintItem(cur);
	if (cur) cur->autoExpand();

	// do an anti-intersection on oldAutoUpdated and the new mAutoExpanded
	for (QPtrListIterator<TreeItem> i(mAutoExpanded); *i; ++i)
	{
		oldAutoExpanded.removeRef(*i);
	}


	bool user=false;
	for (QPtrListIterator<TreeItem> i(oldAutoExpanded); *i; ++i)
	{
		if ((*i)->userOpened())
		{
			user = true;
			break;
		}
	}
	if (!user)
	{
		for (QPtrListIterator<TreeItem> i(oldAutoExpanded); *i; ++i)
		{
			(*i)->setOpen(false);
		}
	}

	ensureItemVisible(cur);
}

void Tree::setLimit(const QString &text)
{
	for (TreeItem *ch = firstChild(); ch; ch = ch->nextSibling())
	{
		ch->hideIfNoMatch(text);
	}
}

void Tree::contextMenu(KListView*, QListViewItem* i, const QPoint& p)
{
	if (!i) return;
	FileMenu *menu = new FileMenu(this, static_cast<TreeItem*>(i)->file());
	menu->popup(p);
}

void Tree::play(QListViewItem *_item)
{
	if (!_item) return;
	TreeItem *item = static_cast<TreeItem*>(_item);
	if (item->playable())
		emit selected(item);
	else
		play(item->nextPlayable());
}


TreeItem *Tree::collate(QueryGroup *group, const File &file, TreeItem *childOf)
{
	do
	{
		if (group->matches(file))
		{
			TreeItem *item = node(group, file, childOf);
			TreeItem *ti=0;
			if (group->firstChild())
			{
				ti = collate(group->firstChild(), file, item);
			}
			if (ti && ti->playable())
				return ti;
			else if(item && item->playable())
				return item;
			else
				return 0;
		}

	} while (( group = group->nextSibling()));
	return 0;
}

TreeItem *Tree::node(QueryGroup *group, const File &file, TreeItem *childOf)
{
	// search childOf's immediate children
	TreeItem *children;
	if (childOf)
		children = childOf->firstChild();
	else
		children = firstChild();

	QString presentation = group->presentation(file);
	while (children)
	{
		// merging would be done here
		bool matches=false;
		if (group->fuzzyness(QueryGroup::Case))
		{
			matches = (children->text(0).lower() == presentation.lower());
		}
		else
		{
			matches = (children->text(0) == presentation);
		}

		matches = matches && !children->group()->option(QueryGroup::Playable);

		if (matches)
		{
			children->setFile(File());
			return children;
		}

		children = children->nextSibling();
	}

	TreeItem *item;
	if (group->option(QueryGroup::ChildrenVisible))
	{
		item = childOf;
	}
	else if (childOf)
	{
		item = new TreeItem(childOf, group, file, presentation);
	}
	else
	{
		item = new TreeItem(this, group, file, presentation);
	}

	item->setOpen(group->option(QueryGroup::AutoOpen));

	return item;
}

#include "tree.moc"

