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

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

#include <kregexpeditorinterface.h>
#include <kparts/componentfactory.h>
#include <klocale.h>
#include <klistview.h>
#include <klineedit.h>
#include <kiconloader.h>
#include <klineeditdlg.h>
#include <kconfig.h>

#include <qgroupbox.h>
#include <qcheckbox.h>
#include <qlabel.h>
#include <qvbox.h>
#include <qhbox.h>
#include <qfileinfo.h>
#include <qtooltip.h>
#include <qpushbutton.h>
#include <qlayout.h>
#include <qcombobox.h>
#include <qwhatsthis.h>

Configure::Configure(Oblique *parent)
	: CModule(i18n("Oblique"), i18n("Configure Oblique Playlist"), "", parent)
{
	mOblique = parent;
	mIgnore = true;

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


	{
		QHBox *box = new QHBox(this);
		mSchemaList = new QComboBox(box);
		connect(
				mSchemaList, SIGNAL(activated(const QString&)),
				SLOT(selectSchema(const QString&))
			);

		mAdd = new QPushButton(BarIconSet("filenew"), 0, box);
		mAdd->setFixedWidth(mAdd->height());
		QToolTip::add(mAdd, i18n("Create new schema"));
		connect(mAdd, SIGNAL(clicked()), SLOT(newSchema()));

		mRemove = new QPushButton(BarIconSet("editdelete"), 0, box);
		mRemove->setFixedWidth(mRemove->height());
		QToolTip::add(mRemove, i18n("Remove this schema"));
		connect(mRemove, SIGNAL(clicked()), SLOT(removeSchema()));

		mCopy = new QPushButton(BarIconSet("editcopy"), 0, box);
		mCopy->setFixedWidth(mCopy->height());
		QToolTip::add(mCopy, i18n("Copy this schema"));
		connect(mCopy, SIGNAL(clicked()), SLOT(copySchema()));
	}


	{
		QHBox *middle = new QHBox(this);

		mSchemaTree = new KListView(middle);
		connect(
				mSchemaTree, SIGNAL(currentChanged(QListViewItem*)),
				SLOT(setCurrent(QListViewItem*))
			);
		connect(
				mSchemaTree, SIGNAL(moved(QListViewItem *, QListViewItem *, QListViewItem *)),
				SLOT(move(QListViewItem *, QListViewItem *, QListViewItem *))
			);

		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"));

		QVBox *buttons = new QVBox(middle);

		mAddSibling = new QPushButton(BarIconSet("1rightarrow", KIcon::SizeSmall), "",buttons);
		mAddSibling->setFixedWidth(mAddSibling->height());
		connect(mAddSibling, SIGNAL(clicked()), SLOT(addSibling()));
		QToolTip::add(mAddSibling, i18n("Create a new item after the selected one"));

		mAddChild = new QPushButton(BarIconSet("2rightarrow", KIcon::SizeSmall), "", buttons);
		mAddChild->setFixedWidth(mAddChild->height());
		connect(mAddChild, SIGNAL(clicked()), SLOT(addChild()));
		QToolTip::add(mAddChild, i18n("Create a new child item under the selected one"));

		mRemoveSelf = new QPushButton(BarIconSet("filenew", KIcon::SizeSmall), "", buttons);
		mRemoveSelf->setFixedWidth(mRemoveSelf->height());
		connect(mRemoveSelf, SIGNAL(clicked()), SLOT(removeSelf()));
		QToolTip::add(mRemoveSelf, i18n("Remove the selected item"));

		new QWidget(buttons);
	}


	{
		QVBox *side = new QVBox(this);
		// the controllers
		{
			QWidget *topSide = new QWidget(side);
			QGridLayout *grid = new QGridLayout(topSide);

			QLabel *label;

			label = new QLabel(i18n("&Property:"), topSide);
			mPropertyEdit = new KLineEdit(topSide);
			label->setBuddy(mPropertyEdit);
			grid->addWidget(label, 0, 0);
			grid->addMultiCellWidget(mPropertyEdit, 0, 0, 1, 2);
			connect(mPropertyEdit, SIGNAL(textChanged(const QString&)), SLOT(updateCurrent()));

			label = new QLabel(i18n("&Value:"), topSide);
			mValueEdit = new KLineEdit(topSide);
			label->setBuddy(mPropertyEdit);
			grid->addWidget(label, 1, 0);
			grid->addMultiCellWidget(mValueEdit, 1, 1, 1, 1);
			connect(mValueEdit, SIGNAL(textChanged(const QString&)), SLOT(updateCurrent()));

			QPushButton *editRe = new QPushButton(i18n("&Edit..."), topSide);
			grid->addWidget(editRe, 1, 2);
			connect(editRe, SIGNAL(clicked()), SLOT(editValueRegexp()));

			label = new QLabel(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, SIGNAL(textChanged(const QString&)), SLOT(updateCurrent()));
		}

		{
			QGroupBox *groupbox = new QGroupBox(
					4, Qt::Horizontal, i18n("Options"), side
				);

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

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

			mOptionAutoOpen = new QCheckBox(i18n("Auto &open"), groupbox);
			QWhatsThis::add(mOptionAutoOpen, i18n("This branch is marked as open immediately."));
			connect(mOptionAutoOpen, SIGNAL(toggled(bool)), SLOT(updateCurrent()));
		}

	}

}

class QueryGroupItem : public KListViewItem
{
	QueryGroup *mItem;

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

	QueryGroupItem(KListView *parent, QueryGroup *group, QueryGroupItem *after=0)
		: KListViewItem(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*>(KListViewItem::parent()); }

	KListView *listView()
		{ return static_cast<KListView*>(KListViewItem::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 Configure::reopen()
{
	mSchemaList->clear();
	mQueries.clear();
	mSchemaTree->clear();

	QStringList names = oblique()->schemaCollection().names();

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

		mSchemaList->insertItem(qi.title);

		if (oblique()->fileOfQuery() == *i)
			selectSchema(qi.title);

	}

}

void Configure::save()
{
	for (QMap<QString,QueryItem>::Iterator i(mQueries.begin()); i != mQueries.end(); ++i)
	{
		QString name = i.key();
		name = QFileInfo(name).fileName();
		if (i.data().changed)
		{
			QString fn = oblique()->schemaCollection().saveFile(name);
			i.data().query.save(i.data().title, fn);
		}

 		if (currentQuery() == &i.data())
		{
			KConfigGroup g(KGlobal::config(), "oblique");
			g.writeEntry("schema", name);
			oblique()->loadQuery(name);
		}
	}
}


QString Configure::nameToFilename(const QString &_name)
{
	QString name = _name;
	return name;
}

void Configure::newSchema()
{
	bool ok;
	QString str=KLineEditDlg::getText(i18n("Name of schema:"), "", &ok, this);
	if (!ok) return;

	QString filename = nameToFilename(str);

	if (mQueries.contains(nameToFilename(filename))) return;
	QString save = oblique()->schemaCollection().saveFile(filename);

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

	selectSchema(str);
}

void Configure::copySchema()
{
	bool ok;
	QString str=KLineEditDlg::getText(i18n("Name of schema:"), "", &ok, this);
	if (!ok) return;

	QString filename = nameToFilename(str);

	if (mQueries.contains(nameToFilename(filename))) return;
	QString save = oblique()->schemaCollection().saveFile(filename);

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

	selectSchema(str);
}

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

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

	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);
}

void Configure::editValueRegexp()
{
	unless (mRegexpEditor)
	{
		mRegexpEditor =
			KParts::ComponentFactory::createInstanceFromQuery<QDialog>(
					"KRegExpEditor/KRegExpEditor", QString::null, this
				);
	}

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

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

void Configure::setCurrent(QListViewItem *_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 Configure::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(QRegExp(mValueEdit->text()));

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

void Configure::move(QListViewItem *_item, QListViewItem *, QListViewItem *_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 Configure::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 Configure::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 Configure::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 Configure::setCurrentModified()
{
	if (QueryItem *grp = currentQuery())
	  grp->changed = true;
}

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


#include "cmodule.moc"

