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

#include "cmodule.h"
#include "oblique.h"
#include "file.h"

#include <kregexpeditorinterface.h>
#include <tdeparts/componentfactory.h>
#include <tdelocale.h>
#include <tdelistview.h>
#include <klineedit.h>
#include <kiconloader.h>
#include <kinputdialog.h>
#include <tdeconfig.h>

#include <tqgroupbox.h>
#include <tqcheckbox.h>
#include <tqlabel.h>
#include <tqvbox.h>
#include <tqhbox.h>
#include <tqfileinfo.h>
#include <tqtooltip.h>
#include <tqpushbutton.h>
#include <tqlayout.h>
#include <tqcombobox.h>
#include <tqwhatsthis.h>
#include <tqtabwidget.h>
#include <tqheader.h>

SchemaConfig::SchemaConfig(TQWidget *parent, Oblique *oblique)
	: TQWidget(parent)
{
	mOblique = oblique;
	mIgnore = true;
	mRegexpEditor=0;

	{
		TQVBoxLayout *layout = new TQVBoxLayout(this, 11, 7);
		layout->setAutoAdd(true);
		layout->setSpacing(7);
	}


	{
		TQHBox *box = new TQHBox(this);
		box->setSpacing(7);
		mSchemaList = new TQComboBox(box);
		connect(
				mSchemaList, TQ_SIGNAL(activated(const TQString&)),
				TQ_SLOT(selectSchema(const TQString&))
			);

		mAdd = new TQPushButton(BarIconSet("document-new"), 0, box);
		mAdd->setFixedWidth(mAdd->height());
		TQToolTip::add(mAdd, i18n("Create new schema"));
		connect(mAdd, TQ_SIGNAL(clicked()), TQ_SLOT(newSchema()));

		mRemove = new TQPushButton(BarIconSet("edit-delete"), 0, box);
		mRemove->setFixedWidth(mRemove->height());
		TQToolTip::add(mRemove, i18n("Remove this schema"));
		connect(mRemove, TQ_SIGNAL(clicked()), TQ_SLOT(removeSchema()));

		mCopy = new TQPushButton(BarIconSet("edit-copy"), 0, box);
		mCopy->setFixedWidth(mCopy->height());
		TQToolTip::add(mCopy, i18n("Copy this schema"));
		connect(mCopy, TQ_SIGNAL(clicked()), TQ_SLOT(copySchema()));
	}


	{
		TQHBox *middle = new TQHBox(this);
		middle->setSpacing(7);

		mSchemaTree = new TDEListView(middle);
		connect(
				mSchemaTree, TQ_SIGNAL(currentChanged(TQListViewItem*)),
				TQ_SLOT(setCurrent(TQListViewItem*))
			);
		connect(
				mSchemaTree, TQ_SIGNAL(moved(TQListViewItem *, TQListViewItem *, TQListViewItem *)),
				TQ_SLOT(move(TQListViewItem *, TQListViewItem *, TQListViewItem *))
			);

		mSchemaTree->setAcceptDrops(true);
		mSchemaTree->setSorting(-1);
		mSchemaTree->setDropVisualizer(true);
		mSchemaTree->setDragEnabled(true);
		mSchemaTree->setItemsMovable(true);

		mSchemaTree->addColumn(i18n("Property"));
		mSchemaTree->addColumn(i18n("Value"));
		mSchemaTree->addColumn(i18n("Presentation"));

		TQVBox *buttons = new TQVBox(middle);

		mAddSibling = new TQPushButton(BarIconSet("1rightarrow", TDEIcon::SizeSmall), "",buttons);
		mAddSibling->setFixedWidth(mAddSibling->height());
		connect(mAddSibling, TQ_SIGNAL(clicked()), TQ_SLOT(addSibling()));
		TQToolTip::add(mAddSibling, i18n("Create a new item after the selected one"));

		mAddChild = new TQPushButton(BarIconSet("2rightarrow", TDEIcon::SizeSmall), "", buttons);
		mAddChild->setFixedWidth(mAddChild->height());
		connect(mAddChild, TQ_SIGNAL(clicked()), TQ_SLOT(addChild()));
		TQToolTip::add(mAddChild, i18n("Create a new child item under the selected one"));

		mRemoveSelf = new TQPushButton(BarIconSet("document-new", TDEIcon::SizeSmall), "", buttons);
		mRemoveSelf->setFixedWidth(mRemoveSelf->height());
		connect(mRemoveSelf, TQ_SIGNAL(clicked()), TQ_SLOT(removeSelf()));
		TQToolTip::add(mRemoveSelf, i18n("Remove the selected item"));

		new TQWidget(buttons);
	}


	{
		TQVBox *side = new TQVBox(this);
		side->setSpacing(7);
		// the controllers
		{
			TQWidget *topSide = new TQWidget(side);
			TQGridLayout *grid = new TQGridLayout(topSide);

			TQLabel *label;

			label = new TQLabel(i18n("&Property:"), topSide);
			mPropertyEdit = new KLineEdit(topSide);
			label->setBuddy(mPropertyEdit);
			grid->addWidget(label, 0, 0);
			grid->addMultiCellWidget(mPropertyEdit, 0, 0, 1, 2);
			connect(mPropertyEdit, TQ_SIGNAL(textChanged(const TQString&)), TQ_SLOT(updateCurrent()));

			label = new TQLabel(i18n("&Value:"), topSide);
			mValueEdit = new KLineEdit(topSide);
			label->setBuddy(mPropertyEdit);
			grid->addWidget(label, 1, 0);
			grid->addMultiCellWidget(mValueEdit, 1, 1, 1, 1);
			connect(mValueEdit, TQ_SIGNAL(textChanged(const TQString&)), TQ_SLOT(updateCurrent()));

			TQPushButton *editRe = new TQPushButton(i18n("&Edit..."), topSide);
			grid->addWidget(editRe, 1, 2);
			connect(editRe, TQ_SIGNAL(clicked()), TQ_SLOT(editValueRegexp()));

			label = new TQLabel(i18n("Pre&sentation:"), topSide);
			mPresentationEdit = new KLineEdit(topSide);
			label->setBuddy(mPropertyEdit);
			grid->addWidget(label, 2, 0);
			grid->addMultiCellWidget(mPresentationEdit, 2, 2, 1, 2);
			connect(mPresentationEdit, TQ_SIGNAL(textChanged(const TQString&)), TQ_SLOT(updateCurrent()));
		}

		{
			TQGroupBox *groupbox = new TQGroupBox(
					3, TQt::Horizontal, i18n("Options"), side
				);

			mOptionPlayable = new TQCheckBox(i18n("Play&able"), groupbox);
			TQWhatsThis::add(mOptionPlayable, i18n("This branch represents an individual file.  If two items' presentation match, two items are created."));
			connect(mOptionPlayable, TQ_SIGNAL(toggled(bool)), TQ_SLOT(updateCurrent()));

			mOptionChildrenVisible = new TQCheckBox(i18n("&Children visible"), groupbox);
			TQWhatsThis::add(mOptionChildrenVisible, i18n("Don't create this node, this nodes children become direct children of this node's parent"));
			connect(mOptionChildrenVisible, TQ_SIGNAL(toggled(bool)), TQ_SLOT(updateCurrent()));

			mOptionAutoOpen = new TQCheckBox(i18n("Auto &open"), groupbox);
			TQWhatsThis::add(mOptionAutoOpen, i18n("This branch is marked as open immediately."));
			connect(mOptionAutoOpen, TQ_SIGNAL(toggled(bool)), TQ_SLOT(updateCurrent()));
		}

	}

}

class QueryGroupItem : public TDEListViewItem
{
	QueryGroup *mItem;

public:
	QueryGroupItem(QueryGroupItem *parent, QueryGroup *group, QueryGroupItem *after=0)
		: TDEListViewItem(parent, after)
	{
		init(group);
	}

	QueryGroupItem(TDEListView *parent, QueryGroup *group, QueryGroupItem *after=0)
		: TDEListViewItem(parent, after)
	{
		init(group);
	}

	QueryGroup *item() { return mItem; }
	const QueryGroup *item() const { return mItem; }

	void redisplay()
	{
		setText(0, item()->propertyName());
		setText(1, item()->value().pattern());
		setText(2, item()->presentation());
	}

	QueryGroupItem *parent()
		{ return static_cast<QueryGroupItem*>(TDEListViewItem::parent()); }

	TDEListView *listView()
		{ return static_cast<TDEListView*>(TDEListViewItem::listView()); }

private:
	void init(QueryGroup *group)
	{
		mItem = group;
		redisplay();

		if (group->firstChild())
			new QueryGroupItem(this, group->firstChild(), this);

		// do siblings now iff I don't already have them
		if (!nextSibling())
		{
			if (QueryGroup *after = group->nextSibling())
			{
				if (parent())
					new QueryGroupItem(parent(), after, this);
				else
					new QueryGroupItem(listView(), after, this);
			}
		}

		setOpen(true);
	}
};


void SchemaConfig::reopen()
{
	mSchemaList->clear();
	mQueries.clear();
	mSchemaTree->clear();

	TQStringList names = oblique()->schemaNames();
	TQString firstTitle;

	for (TQStringList::Iterator i(names.begin()); i != names.end(); ++i)
	{
		QueryItem qi;
		qi.title = oblique()->loadSchema(qi.query, *i);
		qi.changed = false;
		mQueries.insert(*i, qi);

		if (!mSchemaList->count())
			firstTitle = qi.title;
		mSchemaList->insertItem(qi.title);
	}
	selectSchema(firstTitle);

}

void SchemaConfig::save()
{
	for (TQMap<TQString,QueryItem>::Iterator i(mQueries.begin()); i != mQueries.end(); ++i)
	{
		TQString name = i.key();
		name = TQFileInfo(name).fileName();
		if (i.data().changed)
		{
			oblique()->saveSchema(i.data().query, name, i.data().title);
			// TODO update the trees.
		}
	}
}


TQString SchemaConfig::nameToFilename(const TQString &_name)
{
	TQString name = _name;
	name = name.replace(TQRegExp("[^a-zA-Z0-9]"), "_");
	return name;
}

void SchemaConfig::newSchema()
{
	bool ok;
	TQString str=KInputDialog::getText(
			i18n("Name of Schema"),
			i18n("Please enter the name of the new schema:"),
			"", &ok, this
		);
	if (!ok) return;

	TQString filename = nameToFilename(str);

	if (mQueries.contains(nameToFilename(filename))) return;

	QueryItem queryitem;
	queryitem.query = Query();
	queryitem.title = str;
	queryitem.changed=true;
	mSchemaList->insertItem(str);
	mQueries.insert(filename, queryitem);

	selectSchema(str);
}

void SchemaConfig::copySchema()
{
	bool ok;
	TQString str=KInputDialog::getText(
			i18n("Name of Schema"),
			i18n("Please enter the name of the new schema:"),
			"", &ok, this
		);
	if (!ok) return;

	TQString filename = nameToFilename(str);

	if (mQueries.contains(nameToFilename(filename))) return;

	QueryItem queryitem;
	queryitem.query = currentQuery()->query;
	queryitem.title = str;
	queryitem.changed=true;
	mSchemaList->insertItem(str);
	mQueries.insert(filename, queryitem);

	selectSchema(str);
}

void SchemaConfig::removeSchema()
{
	QueryItem *item = currentQuery();
	mSchemaList->removeItem(mSchemaList->currentItem());
	oblique()->removeSchema(nameToFilename(item->title));
	selectSchema(mSchemaList->currentText());
}

void SchemaConfig::selectSchema(const TQString &title)
{
	mSchemaTree->clear();
	mSchemaList->setCurrentText(title);

	mIgnore = true;
	if (QueryItem *grp = currentQuery())
	{
		if (QueryGroup *g = grp->query.firstChild())
			new QueryGroupItem(mSchemaTree, g);
	}
	mSchemaTree->setCurrentItem(mSchemaTree->firstChild());
	setCurrent(mSchemaTree->firstChild());
	mSchemaTree->setSelected(mSchemaTree->firstChild(), true);
	mIgnore=false;
}

void SchemaConfig::editValueRegexp()
{
	unless (mRegexpEditor)
	{
		mRegexpEditor =
			KParts::ComponentFactory::createInstanceFromQuery<TQDialog>(
					"KRegExpEditor/KRegExpEditor", TQString(), this
				);
	}

	if ( mRegexpEditor )
	{
		KRegExpEditorInterface *iface =
			static_cast<KRegExpEditorInterface*>(
					mRegexpEditor->tqt_cast( "KRegExpEditorInterface")
				);

		iface->setRegExp(mValueEdit->text());
		if (mRegexpEditor->exec() == TQDialog::Accepted)
			mValueEdit->setText(iface->regExp());
	}
}

void SchemaConfig::setCurrent(TQListViewItem *_item)
{
	if (!_item) return;
	QueryGroupItem *item = static_cast<QueryGroupItem*>(_item);
	mIgnore = true;
	mPropertyEdit->setText(item->item()->propertyName());
	mValueEdit->setText(item->item()->value().pattern());
	mPresentationEdit->setText(item->item()->presentation());

	mOptionPlayable->setChecked(item->item()->option(QueryGroup::Playable));
	mOptionAutoOpen->setChecked(item->item()->option(QueryGroup::AutoOpen));
	mOptionChildrenVisible->setChecked(item->item()->option(QueryGroup::ChildrenVisible));
	mIgnore = false;
}


void SchemaConfig::updateCurrent()
{
	QueryGroupItem *item = static_cast<QueryGroupItem*>(mSchemaTree->currentItem());
	if (mIgnore || !item) return;
	setCurrentModified();

	QueryGroup *mod = item->item();

	mod->setPropertyName(mPropertyEdit->text());
	mod->setPresentation(mPresentationEdit->text());
	mod->setValue(TQRegExp(mValueEdit->text()));

	mod->setOption(QueryGroup::AutoOpen, mOptionAutoOpen->isChecked());
	mod->setOption(QueryGroup::Playable, mOptionPlayable->isChecked());
	mod->setOption(QueryGroup::ChildrenVisible, mOptionChildrenVisible->isChecked());
	item->redisplay();
}

void SchemaConfig::move(TQListViewItem *_item, TQListViewItem *, TQListViewItem *_afterNow)
{
	setCurrentModified();
	QueryGroupItem *item = static_cast<QueryGroupItem*>(_item);
	QueryGroupItem *afterNow = static_cast<QueryGroupItem*>(_afterNow);

	QueryGroup *after, *under;
	under = item->parent() ? item->parent()->item() : 0;
	after = afterNow ? afterNow->item() : 0;

	item->item()->move(&currentQuery()->query, under, after);
}


void SchemaConfig::addSibling()
{
	QueryGroupItem *item = static_cast<QueryGroupItem*>(mSchemaTree->currentItem());
	unless (item)
	{
		addChild();
		return;
	}
	setCurrentModified();

	// add it
	QueryGroup * g = new QueryGroup;
	item->item()->insertAfter(g);

	// now match the action in the tree
	QueryGroupItem *created;
	if (item->parent())
		created = new QueryGroupItem(item->parent(), g, item);
	else
		created = new QueryGroupItem(item->listView(), g, item);

	// select it so the user can edit it now
	item->listView()->setCurrentItem(created);
	item->listView()->setSelected(created, true);
}


void SchemaConfig::addChild()
{
	QueryGroupItem *item = static_cast<QueryGroupItem*>(mSchemaTree->currentItem());
	setCurrentModified();

	// add it
	QueryGroup * g = new QueryGroup;
	if (item)
		item->item()->insertUnder(g);
	else
		currentQuery()->query.setFirstChild(g);

	// now match the action in the tree
	QueryGroupItem *created;
	if (item)
		created = new QueryGroupItem(item, g);
	else
		created = new QueryGroupItem(mSchemaTree, g);

	// select it so the user can edit it now
	mSchemaTree->setCurrentItem(created);
	mSchemaTree->setSelected(created, true);
}

void SchemaConfig::removeSelf()
{
	setCurrentModified();
	QueryGroupItem *item = static_cast<QueryGroupItem*>(mSchemaTree->currentItem());
	unless (item) return;
	QueryGroup *grp = item->item();
	delete item;
	currentQuery()->query.take(grp);
	delete grp;
}

void SchemaConfig::setCurrentModified()
{
	if (QueryItem *grp = currentQuery())
	  grp->changed = true;
}

SchemaConfig::QueryItem *SchemaConfig::currentQuery()
{
	TQString title = mSchemaList->currentText();
	for (TQMap<TQString,QueryItem>::Iterator i(mQueries.begin()); i != mQueries.end(); ++i)
	{
		if (i.data().title != title) continue;
		return &i.data();
	}
	return 0;
}


SliceConfig::SliceConfig(TQWidget *parent, Oblique *oblique)
	: TQWidget(parent)
{
	mOblique = oblique;
	(new TQVBoxLayout(this, 11, 7))->setAutoAdd(true);

	{
		TQHBox *middle = new TQHBox(this);
		middle->setSpacing(7);

		mSliceList = new TDEListView(middle);
		TQWhatsThis::add(mSliceList, i18n("The list of slices.  A Slice is part of the full collection. This allows you to categorize your playlist. You can add an item to the list by right clicking on it and selecting the Slice you want it in."));
		mSliceList->addColumn("");
		mSliceList->header()->hide();

		mSliceList->setSorting(0);
		mSliceList->setItemsRenameable(true);

		TQVBox *buttons = new TQVBox(middle);

		mAdd = new TQPushButton(BarIconSet("1rightarrow", TDEIcon::SizeSmall), "",buttons);
		mAdd->setFixedWidth(mAdd->height());
		connect(mAdd, TQ_SIGNAL(clicked()), TQ_SLOT(addSibling()));
		TQToolTip::add(mAdd, i18n("Create a new item"));

		mRemove = new TQPushButton(BarIconSet("document-new", TDEIcon::SizeSmall), "", buttons);
		mRemove->setFixedWidth(mRemove->height());
		connect(mRemove, TQ_SIGNAL(clicked()), TQ_SLOT(removeSelf()));
		TQToolTip::add(mRemove, i18n("Remove the selected item"));

		new TQWidget(buttons);
	}
}


class SliceListItem : public TDEListViewItem
{
	Slice *mSlice;
public:
	SliceListItem(TDEListView *parent, Slice *slice)
		 : TDEListViewItem(parent, slice->name()), mSlice(slice)
	{
	}

	/**
	 * new item
	 **/
	SliceListItem(TDEListView *parent)
		 : TDEListViewItem(parent, i18n("New Slice")), mSlice(0)
	{
	}

	Slice *slice() { return mSlice; }
};

SliceListItem *SliceConfig::currentItem()
{
	TQListViewItem *c = mSliceList->currentItem();
	return static_cast<SliceListItem*>(c);
}


void SliceConfig::reopen()
{
	mSliceList->clear();
	mRemovedItems.clear();
	mAddedItems.clear();

	TQPtrList<Slice> slices = oblique()->base()->slices();

	for (TQPtrListIterator<Slice> i(slices); *i; ++i)
	{
		Slice *slice = *i;
		new SliceListItem(mSliceList, slice);
	}
}

void SliceConfig::save()
{
	for (
			TQValueList<Slice*>::Iterator i(mRemovedItems.begin());
			i != mRemovedItems.end();
			++i
		)
	{
		(*i)->remove();
		delete *i;
	}

	for (
			TQValueList<SliceListItem*>::Iterator i(mAddedItems.begin());
			i != mAddedItems.end();
			++i
		)
	{
		oblique()->base()->addSlice((*i)->text(0));
	}

	for (TQListViewItem *i = mSliceList->firstChild(); i; i = i->nextSibling())
	{
		SliceListItem *si = static_cast<SliceListItem*>(i);

		if (si->slice())
		{
			si->slice()->setName(si->text(0));
		}
	}

	reopen();
}


void SliceConfig::addSibling()
{
	SliceListItem *si = new SliceListItem(mSliceList);
	mAddedItems.append(si);
}

void SliceConfig::removeSelf()
{
	SliceListItem *r = currentItem();
	if (mAddedItems.contains(r))
	{
		mAddedItems.remove(r);
	}
	else
	{
		Q_ASSERT(r->slice());
		mRemovedItems.append(r->slice());
	}

	delete r;
}



Configure::Configure(Oblique *oblique)
	: CModule(i18n("Oblique"), i18n("Configure Oblique Playlist"), "", oblique)
{
	(new TQVBoxLayout(this))->setAutoAdd(true);
	tabs = new TQTabWidget(this);

	tabs->addTab(slice = new SliceConfig(tabs, oblique), i18n("Slices"));
	tabs->addTab(schema = new SchemaConfig(tabs, oblique), i18n("Schemas"));
}

void Configure::reopen()
{
	schema->reopen();
	slice->reopen();
}

void Configure::save()
{
	schema->save();
	slice->save();
}


#include "cmodule.moc"

