//=============================================================================
//
//   File : OptionsDialog.cpp
//   Created on Fri 10 Feb 2006 18:52:18 by Szymon Stefanek
//   Based on dlg_options.cpp: Tue Jun 11 2000 02:39:12 by Szymon Stefanek
//
//   This file is part of the KVIrc IRC Client distribution
//   Copyright (C) 2000-2010 Szymon Stefanek <pragma at kvirc dot net>
//
//   This program is FREE software. You can redistribute it and/or
//   modify it under the terms of the GNU General Public License
//   as published by the Free Software Foundation; either version 2
//   of the License, or (at your opinion) any later version.
//
//   This program is distributed in the HOPE that it will be USEFUL,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//   See the GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program. If not, write to the Free Software Foundation,
//   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================

#include "OptionsDialog.h"

#include "KviApplication.h"
#include "KviOptions.h"
#include "KviLocale.h"
#include "KviIconManager.h"
#include "KviMainWindow.h"
#include "KviModule.h"
#include "KviPointerHashTable.h"
#include "KviTalVBox.h"
#include "KviTalToolTip.h"
#include "KviTalGroupBox.h"

#include <QLayout>
#include <QLabel>
#include <QSplitter>
#include <QPushButton>
#include <QPainter>
#include <QFont>
#include <QEvent>
#include <QCloseEvent>
#include <QShortcut>
#include <QHeaderView>
#include <QDesktopWidget>
#include <QStackedWidget>
#include <QMenu>

//FIXME: some headers must be hidden


//extern KviModule * g_pOptionsModule;
extern KviPointerHashTable<QString,OptionsDialog> * g_pOptionsDialogDict;

extern KVIRC_API KviApplication * g_pApp;
extern OptionsInstanceManager * g_pOptionsInstanceManager;

OptionsDialogFrontWidget::OptionsDialogFrontWidget(QWidget *parent,const QString &szText)
:KviOptionsWidget(parent)
{
	setObjectName("general_options_front_widget");
	createLayout();
	QLabel * l = new QLabel(szText,this);
	l->setWordWrap(true);
	l->setAlignment(Qt::AlignTop);
	layout()->addWidget(l,0,0);
}

OptionsDialogFrontWidget::~OptionsDialogFrontWidget()
{
}


OptionsDialogTreeWidgetItem::OptionsDialogTreeWidgetItem(QTreeWidget *parent,OptionsWidgetInstanceEntry * e)
:QTreeWidgetItem()
{
	m_pInstanceEntry = e;
	m_pOptionsWidget = 0;
	setText(0,e->szName);
	setIcon(0,*(g_pIconManager->getSmallIcon(e->eIcon)));
	parent->insertTopLevelItem(0, this);
}

OptionsDialogTreeWidgetItem::OptionsDialogTreeWidgetItem(QTreeWidgetItem *parent,OptionsWidgetInstanceEntry * e)
:QTreeWidgetItem()
{
	m_pInstanceEntry = e;
	m_pOptionsWidget = 0;
	setText(0,e->szName);
	setIcon(0,*(g_pIconManager->getSmallIcon(e->eIcon)));
	parent->insertChild(0, this);
}

OptionsDialogTreeWidgetItem::~OptionsDialogTreeWidgetItem()
{
}

OptionsDialog::OptionsDialog(QWidget * par,const QString &szGroup)
: QWidget(par)
{
	setObjectName("general_options_dialog");

	setWindowIcon(*(g_pIconManager->getSmallIcon(KviIconManager::KVIrc)));

	m_szGroup = szGroup;

	QString szDialogTitle;

	if(szGroup.isEmpty() || KviQString::equalCI(szGroup,"general"))
	{
		szDialogTitle = __tr2qs_ctx("General Preferences","options");
	} else if(KviQString::equalCI(szGroup,"theme"))
	{
		szDialogTitle = __tr2qs_ctx("Theme Preferences","options");
	} else {
		szDialogTitle = __tr2qs_ctx("KVIrc Preferences","options");
	}

	QString szDialog = __tr2qs_ctx("This dialog contains a set of KVIrc settings.<br> Use the icons " \
			"on the left to navigate through the option pages. The text box in the " \
			"bottom left corner is a small search engine. It will highlight the " \
			"pages that contain options related to the search term you have entered.","options");


	QString szInfoTips;
	szInfoTips = __tr2qs_ctx("Many settings have tooltips that can be shown by holding " \
			"the cursor over their label for a few seconds.","options");

	QString szOkCancelButtons = __tr2qs_ctx("When you have finished, click \"<b>OK</b>\" to accept your changes " \
			"or \"<b>Cancel</b>\" to discard them. Clicking \"<b>Apply</b>\" will commit your " \
			"changes without closing the window.","options");

	QString szFrontText = QString(
			"<table width=\"100%\" height=\"100%\" valign=\"top\" align=\"center\" cellpadding=\"4\">" \
				"<tr>" \
					"<td bgcolor=\"#303030\" valign=\"top\">" \
						"<center><h1><font color=\"#FFFFFF\">%1</font></h1></center>" \
					"</td>" \
				"</tr>" \
				"<tr>" \
					"<td valign=\"bottom\">" \
						"<br>" \
						"<br>" \
						"<p>" \
							"%2" \
						"</p>" \
						"<br>" \
						"<p>" \
							"%3" \
						"</p>" \
						"<br>" \
						"<p>" \
							"%4" \
						"</p>" \
					"</td>" \
				"</tr>" \
			"</table>"
		).arg(szDialogTitle, szDialog, szInfoTips, szOkCancelButtons);

	QString szCaption = szDialogTitle + " - KVIrc";
	setWindowTitle(szCaption);

	QGridLayout * g1 = new QGridLayout(this);
	QSplitter * spl = new QSplitter(Qt::Horizontal,this);
	spl->setChildrenCollapsible(false);

	g1->addWidget(spl,0,0,1,5);

	KviTalVBox * vbox = new KviTalVBox(spl);
	vbox->setSpacing(2);
	vbox->setMargin(3);

	// Controlling list view
	m_pTreeWidget = new QTreeWidget(vbox);

	m_pTreeWidget->header()->hide();
	m_pTreeWidget->setRootIsDecorated(true);
	m_pTreeWidget->setSortingEnabled(false);
	m_pTreeWidget->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding);

	connect(m_pTreeWidget,SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem *)),this,SLOT(treeWidgetItemSelectionChanged(QTreeWidgetItem *, QTreeWidgetItem *)));

	KviTalHBox * hbox = new KviTalHBox(vbox);
	hbox->setSpacing(2);
	hbox->setMargin(3);

	m_pSearchLineEdit = new QLineEdit(hbox);
	connect(m_pSearchLineEdit,SIGNAL(returnPressed()),this,SLOT(searchClicked()));
	m_pSearchButton = new QToolButton(hbox);
	m_pSearchButton->setIconSize(QSize(16,16));
	m_pSearchButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Search)));
	connect(m_pSearchButton,SIGNAL(clicked()),this,SLOT(searchClicked()));
	connect(m_pSearchLineEdit,SIGNAL(textChanged(const QString &)),this,SLOT(searchLineEditTextChanged(const QString &)));

	QString szTip = __tr2qs_ctx("<p>This is the search tool for this options dialog.</p>" \
			"<p>You can enter a search term either in your native " \
			"language or in english and press the button on the right. " \
			"The pages that contain some options related to the " \
			"search term will be highlighted and you will be able " \
			"to quickly find them.</p><p>Try \"nickname\" for example.</p>","options");
	KviTalToolTip::add(m_pSearchLineEdit,szTip);
	KviTalToolTip::add(m_pSearchButton,szTip);

	vbox = new KviTalVBox(spl);
	vbox->setSpacing(2);
	vbox->setMargin(3);

	m_pCategoryLabel = new QLabel("<b>&nbsp;</b>",vbox);
	m_pCategoryLabel->setObjectName("labgeneraloptions");

	QFrame * f = new QFrame(vbox);
	f->setFrameStyle(QFrame::HLine | QFrame::Sunken);

	// Widget stack
	m_pWidgetStack = new QStackedWidget(vbox);
	vbox->setStretchFactor(m_pWidgetStack,1);

	// First widget visible
	m_pFrontWidget = new OptionsDialogFrontWidget(m_pWidgetStack,szFrontText);
	m_pWidgetStack->addWidget(m_pFrontWidget);

	int idx = m_pWidgetStack->indexOf(m_pFrontWidget);
	m_pWidgetStack->widget(idx)->raise();

	//  Ok,Cancel,Help
	QPushButton * b = new QPushButton(__tr2qs_ctx("&OK","options"),this);
	b->setObjectName("btnok");
	KviTalToolTip::add(b,__tr2qs_ctx("Close this dialog, accepting all changes.","options"));
	connect(b,SIGNAL(clicked()),this,SLOT(okClicked()));
	//b->setMinimumWidth(whatIsThisWidth);
	b->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Accept)));
	g1->addWidget(b,1,2);

	b = new QPushButton(__tr2qs_ctx("&Apply","options"),this);
	b->setObjectName("btnapply");
	KviTalToolTip::add(b,__tr2qs_ctx("Commit all changes immediately.","options"));
	connect(b,SIGNAL(clicked()),this,SLOT(applyClicked()));
	b->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Accept)));
	g1->addWidget(b,1,3);

	b = new QPushButton(__tr2qs_ctx("Cancel","options"),this);
	b->setObjectName("btncancel");
	KviTalToolTip::add(b,__tr2qs_ctx("Close this dialog, discarding all changes.","options"));
	b->setDefault(true);
	//b->setMinimumWidth(whatIsThisWidth);
	connect(b,SIGNAL(clicked()),this,SLOT(cancelClicked()));
	b->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Discard)));
	g1->addWidget(b,1,4);

	g1->setRowStretch(0,1);
	g1->setColumnStretch(1,1);


	fillTreeWidget(0,g_pOptionsInstanceManager->instanceEntryTree(),szGroup);

	if(!parent())
	{
		if(KVI_OPTION_RECT(KviOption_rectGeneralOptionsDialogGeometry).y() < 5)
			KVI_OPTION_RECT(KviOption_rectGeneralOptionsDialogGeometry).setY(5);

		//setGeometry(KVI_OPTION_RECT(KviOption_rectGeneralOptionsDialogGeometry));
		resize(KVI_OPTION_RECT(KviOption_rectGeneralOptionsDialogGeometry).width(),
			KVI_OPTION_RECT(KviOption_rectGeneralOptionsDialogGeometry).height());
		move(KVI_OPTION_RECT(KviOption_rectGeneralOptionsDialogGeometry).x(),
			KVI_OPTION_RECT(KviOption_rectGeneralOptionsDialogGeometry).y());
	}

	new QShortcut(Qt::Key_Escape,this,SLOT(close()));
}

OptionsDialog::~OptionsDialog()
{
	if(!parent())
		KVI_OPTION_RECT(KviOption_rectGeneralOptionsDialogGeometry) = QRect(pos().x(),pos().y(),size().width(),size().height());
	if(g_pOptionsDialogDict)
		g_pOptionsDialogDict->remove(m_szGroup);
}

void OptionsDialog::showEvent(QShowEvent *e)
{
	// setup a minimum size and move to the screen the main kvirc window is
	QRect r = g_pApp->desktop()->screenGeometry(g_pMainWindow);

	int w = r.width();
	int h = r.height();

	int ww = width();
	int wh = height();

	if(w < 800)
	{
		// 640x480
		if(ww < 630)ww = 630;
	} else {
		if(ww < 770)ww = 770;
	}

	setGeometry(r.x() + ((w - ww) / 2),r.y() + ((h - wh) / 2),ww,wh);

	QWidget::showEvent(e);
}

void OptionsDialog::searchLineEditTextChanged(const QString &)
{
	QString txt = m_pSearchLineEdit->text().trimmed();
	m_pSearchButton->setEnabled(txt.length() > 0);
}
bool OptionsDialog::searchInSelectors(KviOptionsWidget *pOptionsWidget,const QStringList &lKeywords)
{
	KviPointerList<KviSelectorInterface> *selectors=pOptionsWidget->selectors();
	bool bFoundSomethingHere=false;
	if(selectors->count()>0)
	{
		for(unsigned int i=0;i<selectors->count();i++)
		{
			QString szText=selectors->at(i)->textForSearch();
			QWidget* pWidget=selectors->at(i)->widgetToHighlight();
			if (!pWidget) continue;
			QString szTmp=pWidget->toolTip();
			szTmp=szTmp.replace(QRegExp("<[^<>]+>"),"");
			szText.append(szTmp);
			if(!szText.isEmpty())
			{
				bool bOk = true;
				for(int j=0;j<lKeywords.count();j++)
				{
					if(szText.indexOf(lKeywords.at(j),0,Qt::CaseInsensitive) == -1)
					{
						bOk = false;
						break;
					}
				}
				if(bOk)
				{
					bFoundSomethingHere = true;
				}
				QFont font = pWidget->font();
				font.setBold(bOk);
				font.setUnderline(bOk);
				pWidget->setFont(font);
			}
		}
	}
	return bFoundSomethingHere;
}

bool OptionsDialog::recursiveSearch(OptionsDialogTreeWidgetItem * pItem,const QStringList &lKeywords)
{
	if(!pItem)return false;
	if(!pItem->m_pOptionsWidget)
	{
		pItem->m_pOptionsWidget = g_pOptionsInstanceManager->getInstance(pItem->m_pInstanceEntry,m_pWidgetStack);
		m_pWidgetStack->addWidget(pItem->m_pOptionsWidget);
	}

	bool bFoundSomethingHere = false;
	KviOptionsWidget *pOptionsWidget=pItem->m_pOptionsWidget;
	QTabWidget *pTab= pOptionsWidget->tabWidget();
	if (pTab)
	{
		for(int i=0;i<pTab->count();i++)
		{
			QString szTxt = pTab->tabText(i);
			if(KviQString::equalCIN(szTxt,">>> ",4))
			{
				szTxt.replace(">>> ","");
				szTxt.replace(" <<<","");
			}
			if (searchInSelectors((KviOptionsWidget*)pTab->widget(i),lKeywords))
			{
				bFoundSomethingHere = true;
				szTxt.insert(0,">>> ");
				szTxt += QString(" <<<");
			}
			pTab->setTabText(i,szTxt);
		}
	}
	else
	{
		if (searchInSelectors(pOptionsWidget,lKeywords))
			bFoundSomethingHere = true;
	}

	QStringList szInstanceKeywords = pItem->m_pInstanceEntry->szKeywords.split(QChar(','));
	// debug all the "search keywords" for each entry in the options tree
	// qDebug("OPT %s",pItem->m_pInstanceEntry->szKeywords.toUtf8().data());

	for(int i=0;i<szInstanceKeywords.count() && !bFoundSomethingHere;i++)
		for(int j=0;j<lKeywords.count() && !bFoundSomethingHere;j++)
			if(szInstanceKeywords.at(i).contains(lKeywords.at(j),Qt::CaseInsensitive))
				bFoundSomethingHere = true;
	
	if(bFoundSomethingHere)
	{
		pItem->setForeground(0, Qt::yellow);
		pItem->setBackground(0, Qt::red);
	} else {
		pItem->setForeground(0, Qt::black);
		pItem->setBackground(0, Qt::transparent);
	}

	bool bFoundSomethingInside = false;
	int ccount = pItem->childCount();
	if(ccount > 0)
	{
		for(int j=0;j<ccount;j++)
		{
			OptionsDialogTreeWidgetItem * pChild = (OptionsDialogTreeWidgetItem *)pItem->child(j);
			bool bRet = recursiveSearch(pChild,lKeywords);
			if(bRet)bFoundSomethingInside = true;
		}
	}

	pItem->setSelected(false);
	pItem->setExpanded(bFoundSomethingInside);
	return (bFoundSomethingInside || bFoundSomethingHere);
}

void OptionsDialog::search(const QStringList &lKeywords)
{
	m_pTreeWidget->setUpdatesEnabled(false);

	int count=m_pTreeWidget->topLevelItemCount();
	for (int i=0;i<count;i++)
	{
		OptionsDialogTreeWidgetItem * pChild = (OptionsDialogTreeWidgetItem *)m_pTreeWidget->topLevelItem(i);
		recursiveSearch(pChild,lKeywords);
	}
	m_pTreeWidget->setUpdatesEnabled(true);
	m_pTreeWidget->update();
}

void OptionsDialog::search(const QString &szKeywords)
{
	QStringList lKeywords = szKeywords.split(" ",QString::SkipEmptyParts);
	search(lKeywords);
}

void OptionsDialog::searchClicked()
{
	QString szTxt = m_pSearchLineEdit->text().trimmed();
	if(!szTxt.isEmpty())
		search(szTxt);
}

void OptionsDialog::fillTreeWidget(QTreeWidgetItem * p,KviPointerList<OptionsWidgetInstanceEntry> * l,const QString &szGroup,bool bNotContainedOnly)
{
	if(!l)return;

	OptionsDialogTreeWidgetItem * it;
	OptionsWidgetInstanceEntry * e;

	KviPointerList<OptionsWidgetInstanceEntry> tmp;
	tmp.setAutoDelete(false);


	for(e = l->first();e;e = l->next())
	{
		// must be in the correct group
		// if we want only containers then well.. must be one
		e->bDoInsert = KviQString::equalCI(szGroup,e->szGroup) && ((!bNotContainedOnly) || e->bIsContainer || e->bIsNotContained);
		OptionsWidgetInstanceEntry * ee = tmp.first();
		int idx = 0;
		while(ee)
		{
			if(ee->iPriority >= e->iPriority)break;
			idx++;
			ee = tmp.next();
		}
		tmp.insert(idx,e);
	}

	for(e = tmp.first();e;e = tmp.next())
	{
		if(e->bDoInsert)
		{
			if(p)it = new OptionsDialogTreeWidgetItem(p,e);
			else it = new OptionsDialogTreeWidgetItem(m_pTreeWidget,e);
			if(!it->m_pOptionsWidget)
			{
				it->m_pOptionsWidget = g_pOptionsInstanceManager->getInstance(it->m_pInstanceEntry,m_pWidgetStack);

				if(it->m_pOptionsWidget)
					m_pWidgetStack->addWidget(it->m_pOptionsWidget);
			}
		} else {
			it = (OptionsDialogTreeWidgetItem *)p;
		}

		if(e->pChildList)
		{
			if(e->bIsContainer)
			{
				// it's a container: add only eventual not contained children (containers or explicitly marked as not contained)
				fillTreeWidget(it,e->pChildList,szGroup,true);
			} else {
				// it's not a container, add any children
				fillTreeWidget(it,e->pChildList,szGroup,false);
			}
		}
	}
}

void OptionsDialog::treeWidgetItemSelectionChanged(QTreeWidgetItem* it, QTreeWidgetItem *)
{
	if(it)
	{
		QString str = it->text(0);
		QTreeWidgetItem * par = it->parent();

		while(par)
		{
			str.prepend(" :: ");
			str.prepend(par->text(0));
			par = par->parent();
		}
		str.prepend("<b>");
		str += "</b>";

		OptionsDialogTreeWidgetItem *i = (OptionsDialogTreeWidgetItem *)it;
		if(!i->m_pOptionsWidget)
		{
			i->m_pOptionsWidget = g_pOptionsInstanceManager->getInstance(i->m_pInstanceEntry,m_pWidgetStack);
			m_pWidgetStack->addWidget(i->m_pOptionsWidget);
		}

		//int idx = m_pWidgetStack->indexOf(i->m_pOptionsWidget);
		m_pWidgetStack->setCurrentWidget(i->m_pOptionsWidget);
		m_pCategoryLabel->setText(str);
	}
}

/*
OptionsDialogTreeWidgetItem * OptionsDialog::findItemByPage(OptionsDialogTreeWidgetItem *it,KviOptionsWidget * pPage)
{
	if(!it)return 0;
	if(it->m_pOptionsWidget == pPage)return it;

	OptionsDialogTreeWidgetItem *i;

	int ccount = it->childCount();

	for(int j=0;j<ccount;j++)
	{
		OptionsDialogTreeWidgetItem * i = (OptionsDialogTreeWidgetItem *)it->child(j);

		i = findItemByPage(i,pPage);
		if(i)return i;
	}

	// not found in the children tree.. look in the next sibling
	for(int j=0;j<ccount;j++)
	{
		OptionsDialogTreeWidgetItem * pChild = (OptionsDialogTreeWidgetItem *)pItem->child(j);

		i = findItemByPage(i,pPage);
		if(i)return i;
	}

	i = (OptionsDialogTreeWidgetItem *)(it->nextSibling());
	if(i)
	{
		i = findItemByPage(i,pPage);
		if(i)return i;
	}
	return 0;
}
*/
void OptionsDialog::pageWantsToSwitchToAdvancedPage(KviOptionsWidget *)
{
	// unused
}

void OptionsDialog::applyClicked()
{
	apply(false);
}

void OptionsDialog::apply(bool bDialogAboutToClose)
{
	int count=m_pTreeWidget->topLevelItemCount();
	int curTab=-1;
	OptionsDialogTreeWidgetItem * it;

	if(!bDialogAboutToClose)
	{
		// bring up the current widget again!
		it = (OptionsDialogTreeWidgetItem *)m_pTreeWidget->currentItem();
		//checking if the check of the check is checkable
		if(it)
			if(it->m_pOptionsWidget)
				if(it->m_pOptionsWidget->tabWidget())
					curTab = it->m_pOptionsWidget->tabWidget()->currentIndex();
	}

	for (int i=0;i<count;i++)
	{
		it = (OptionsDialogTreeWidgetItem *)m_pTreeWidget->topLevelItem(i);
		recursiveCommit(it);
	}

	if(!bDialogAboutToClose)
	{
		// bring up the current widget again!
		it = (OptionsDialogTreeWidgetItem *)m_pTreeWidget->currentItem();
		if(it)
		{
			treeWidgetItemSelectionChanged(it, 0);
			if(curTab > 0 && it->m_pOptionsWidget)
				if(it->m_pOptionsWidget->tabWidget())
					it->m_pOptionsWidget->tabWidget()->setCurrentIndex(curTab);
		}
	}

	g_pApp->saveConfiguration();

}

void OptionsDialog::okClicked()
{
	apply(true);
	deleteLater();
}

void OptionsDialog::cancelClicked()
{
	deleteLater();
}

void OptionsDialog::closeEvent(QCloseEvent *e)
{
	e->ignore();
	deleteLater();
}

void OptionsDialog::recursiveCommit(OptionsDialogTreeWidgetItem *it)
{
	// First commit the children
	if(!it) return;
	int count=it->childCount();
	for (int i=0;i<count;i++)
	{
		OptionsDialogTreeWidgetItem * pChild = (OptionsDialogTreeWidgetItem *)it->child(i);
		recursiveCommit(pChild);
	}

	// then the parents
	if(it->m_pOptionsWidget)
	{
		it->m_pOptionsWidget->commit();
		delete it->m_pOptionsWidget;
		it->m_pOptionsWidget = 0;
	}
	//refreshTreeWidget(); // <-- this tends to jump into infinite recursion
}

void  OptionsDialog::keyPressEvent( QKeyEvent * e )
{
	if(e->key()==Qt::Key_Return)
		e->accept();
	else
		e->ignore();
}

#ifndef COMPILE_USE_STANDALONE_MOC_SOURCES
#include "m_dialog.moc"
#endif //!COMPILE_USE_STANDALONE_MOC_SOURCES
