//=============================================================================
//
//   File : ScriptEditorImplementation.cpp
//   Creation date : Sun Mar 28 1999 16:11:48 CEST by Szymon Stefanek
//
//   This file is part of the KVIrc IRC client distribution
//   Copyright (C) 1999 Szymon Stefanek <pragma at kvirc dot net>
//   Copyright (C) 2005 Tonino Imbesi
//   Copyright (C) 2005-2008 Alessandro Carbone (elfonol at gmail dot com)
//
//   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 "ScriptEditorImplementation.h"

#include "KviFileUtils.h"
#include "kvi_fileextensions.h"
#include "KviLocale.h"
#include "KviFileDialog.h"
#include "KviQString.h"
#include "KviConfigurationFile.h"
#include "KviModule.h"
#include "KviPointerList.h"
#include "KviApplication.h"
#include "KviConsoleWindow.h"
#include "KviWindow.h"
#include "KviIconManager.h"
#include "KviKvsKernel.h"
#include "KviModuleManager.h"
#include "KviConfigurationFile.h"
#include "KviTalVBox.h"
#include "KviTalHBox.h"
#include "KviTalGroupBox.h"

#include <QScrollBar>
#include <QLayout>
#include <QMessageBox>
#include <QTimer>
#include <QCursor>
#include <QFont>
#include <QRect>
#include <QEvent>
#include <QKeyEvent>
#include <QMenu>
#include <QListWidget>
#include <QPainter>

extern KVIRC_API KviModuleManager                    * g_pModuleManager;
extern KviPointerList<ScriptEditorImplementation> * g_pScriptEditorWindowList;
extern KviModule                                     * g_pEditorModulePointer;

static QColor g_clrBackground(0,0,0);
static QColor g_clrNormalText(100,255,0);
static QColor g_clrBracket(255,0,0);
static QColor g_clrComment(0,120,0);
static QColor g_clrFunction(255,255,0);
static QColor g_clrKeyword(120,120,150);
static QColor g_clrVariable(200,200,200);
static QColor g_clrPunctuation(180,180,0);
static QColor g_clrFind(255,0,0);

#if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
	#define KVI_SCRIPTEDITOR_DEFAULT_FONT "Courier New"
#else
	#define KVI_SCRIPTEDITOR_DEFAULT_FONT "Monospace"
#endif
static QFont g_fntNormal(KVI_SCRIPTEDITOR_DEFAULT_FONT,8);

static bool bSemaphore=false;
static bool bCompleterReady=false;


ScriptEditorWidget::ScriptEditorWidget(QWidget * pParent)
: QTextEdit(pParent)
{
	m_pSyntaxHighlighter=0;
	setTabStopWidth(48);
	setAcceptRichText(false);
	setWordWrapMode(QTextOption::NoWrap);
	m_pParent = pParent;
	m_szHelp = "Nothing";
	updateOptions();
	m_szFind = "";
	m_pCompleter = 0;
	QStringList szListFunctionsCommands;
	QString tmp("kvscompleter.idx");
	iModulesCount=0;
	iIndex=0;
	QString szPath;
	g_pApp->getLocalKvircDirectory(szPath,KviApplication::ConfigPlugins,tmp);

	if(!KviFileUtils::fileExists(szPath))
	{
		if (!bSemaphore){
			bSemaphore=true;
			m_pStartTimer = new QTimer();
			m_pStartTimer->setInterval(1000);
			connect(m_pStartTimer,SIGNAL(timeout()),this,SLOT(asyncCompleterCreation()));
			m_pStartTimer->start(500);
		}
		else{
			m_pStartTimer = new QTimer();
			m_pStartTimer->setInterval(2000);
			connect(m_pStartTimer,SIGNAL(timeout()),this,SLOT(checkReadyCompleter()));
			m_pStartTimer->start(1000);
		}
	} else loadCompleterFromFile();
}

ScriptEditorWidget::~ScriptEditorWidget()
{
	if(m_pCompleter)
		delete m_pCompleter;
	disableSyntaxHighlighter();
}

void ScriptEditorWidget::checkReadyCompleter()
{
	if(bCompleterReady)
	{
		m_pStartTimer->stop();
		delete m_pStartTimer;
		m_pStartTimer=0;
		loadCompleterFromFile();
	}
}

void ScriptEditorWidget::asyncCompleterCreation()
{
	//static int iIndex = 0;
	//static int iModulesCount = 0;
	if(!iIndex)
	{
		m_pListCompletition = new QStringList();
		QString szPath;

		g_pApp->getGlobalKvircDirectory(szPath,KviApplication::Modules);

		QDir d(szPath);

#if defined(COMPILE_ON_WINDOWS)
		d.setNameFilters(QStringList("kvi*.dll"));
#elif defined(COMPILE_ON_MINGW)
		d.setNameFilters(QStringList("libkvi*.dll"));
#else
		d.setNameFilters(QStringList("libkvi*.so"));
#endif
		m_pListModulesNames = new QStringList(d.entryList(QDir::Files | QDir::Readable));
		iModulesCount = m_pListModulesNames->count();
	}
	QString szModuleName = m_pListModulesNames->at(iIndex);
	iIndex++;

#if defined(COMPILE_ON_WINDOWS)
	szModuleName=szModuleName.replace("kvi","");
	szModuleName=szModuleName.replace(".dll","");
#elif defined(COMPILE_ON_MINGW)
	szModuleName=szModuleName.replace("libkvi","");
	szModuleName=szModuleName.replace(".dll","");
#else
	szModuleName=szModuleName.replace("libkvi","");
	szModuleName=szModuleName.replace(".so","");
#endif

	KviModule * pModule = g_pModuleManager->getModule(szModuleName);
	if(pModule)
	{
		pModule->getAllFunctionsCommandsModule(m_pListCompletition,szModuleName);
	}

	if(iIndex == iModulesCount)
	{
		m_pStartTimer->stop();
		m_pStartTimer->deleteLater();
		m_pStartTimer = 0;
		QString szTmp("kvscompleter.idx");
		QString szPath;
		g_pApp->getLocalKvircDirectory(szPath,KviApplication::ConfigPlugins,szTmp);
		KviKvsKernel::instance()->getAllFunctionsCommandsCore(m_pListCompletition);
		QString szBuffer = m_pListCompletition->join(",");
		QFile f(szPath);
		f.open(QIODevice::WriteOnly);
		f.write(szBuffer.toUtf8());
		f.close();
		createCompleter(*m_pListCompletition);
		iIndex = 0;
		iModulesCount = 0;
		bCompleterReady=true;
		delete m_pListCompletition;
		delete m_pListModulesNames;
	}
}

void ScriptEditorWidget::loadCompleterFromFile()
{
	QStringList szListFunctionsCommand;
	QString szTmp("kvscompleter.idx");
	QString szPath;
	g_pApp->getLocalKvircDirectory(szPath,KviApplication::ConfigPlugins,szTmp);
	QString szBuffer;
	QFile f(szPath);
	f.open(QIODevice::ReadOnly);
	szBuffer = f.readAll();
	f.close();
	szListFunctionsCommand = szBuffer.split(',');
	createCompleter(szListFunctionsCommand);
}

void ScriptEditorWidget::createCompleter(QStringList & list)
{
	m_pCompleter = new QCompleter(list);
	m_pCompleter->setCaseSensitivity(Qt::CaseInsensitive);
	m_pCompleter->setWrapAround(false);
	m_pCompleter->setWidget(this);
	m_pCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel);
	m_pCompleter->setCompletionMode(QCompleter::PopupCompletion);
	m_pCompleter->setCaseSensitivity(Qt::CaseInsensitive);
	QObject::connect(m_pCompleter, SIGNAL(activated(const QString &)),this, SLOT(insertCompletion(const QString &)));
}

void ScriptEditorWidget::insertCompletion(const QString & szCompletion)
{
	QTextCursor tc = textCursor();
	int iExtra = szCompletion.length() - m_pCompleter->completionPrefix().length();

	tc.movePosition(QTextCursor::Left);
	tc.movePosition(QTextCursor::EndOfWord);

	QString szTmp = szCompletion.right(iExtra);
	if(szCompletion.left(1)=="$")
		szTmp.append("(");
	else
		szTmp.append(" ");

	tc.insertText(szTmp);
	setTextCursor(tc);
}

void ScriptEditorWidget::contextMenuEvent(QContextMenuEvent * e)
{
	QMenu * pMenu = createStandardContextMenu();
	pMenu->addAction(__tr2qs_ctx("Context sensitive help","editor"),this,SLOT(slotHelp()),Qt::CTRL+Qt::Key_H);
	pMenu->addAction(__tr2qs_ctx("&Replace","editor"),this,SLOT(slotReplace()),Qt::CTRL+Qt::Key_R);
	pMenu->exec(e->globalPos());
	delete pMenu;
}

void ScriptEditorWidget::slotFind()
{
	m_szFind = ((ScriptEditorImplementation*)m_pParent)->findLineEdit()->text();
	setText(toPlainText());
}

void ScriptEditorWidget::slotReplace()
{
	ScriptEditorReplaceDialog * pDialog = new ScriptEditorReplaceDialog(this,__tr2qs_ctx("Find & Replace","editor"));
	connect(pDialog,SIGNAL(replaceAll(const QString &,const QString &)),m_pParent,SLOT(slotReplaceAll(const QString &,const QString &)));
	connect(pDialog,SIGNAL(initFind()),m_pParent,SLOT(slotInitFind()));
	connect(pDialog,SIGNAL(nextFind(const QString &)),m_pParent,SLOT(slotNextFind(const QString &)));

	if(pDialog->exec()){};
}

void ScriptEditorWidget::slotHelp()
{
	contextSensitiveHelp();
}
void ScriptEditorWidget::enableSyntaxHighlighter()
{
	// we currently delete and re-create the m_pSyntaxHighlighter
	// as a trick to ensure proper re-highlightning to occur
	if (!m_pSyntaxHighlighter)
	    m_pSyntaxHighlighter = new ScriptEditorSyntaxHighlighter(this);
}
void ScriptEditorWidget::disableSyntaxHighlighter()
{
	if (m_pSyntaxHighlighter) delete m_pSyntaxHighlighter;
	m_pSyntaxHighlighter = 0;
}

void ScriptEditorWidget::updateOptions()
{
	QPalette p = palette();
	p.setColor(QPalette::Base,g_clrBackground);
	p.setColor(QPalette::Text,g_clrNormalText);
	setPalette(p);
	setFont(g_fntNormal);
	setTextColor(g_clrNormalText);
	disableSyntaxHighlighter();
	enableSyntaxHighlighter();

	p = ((ScriptEditorImplementation*)m_pParent)->findLineEdit()->palette();
	p.setColor(foregroundRole(),g_clrFind);
	((ScriptEditorImplementation*)m_pParent)->findLineEdit()->setPalette(p);
}

void ScriptEditorWidget::keyPressEvent(QKeyEvent * e)
{
	if(m_pCompleter && m_pCompleter->popup()->isVisible())
	{
		switch(e->key())
		{
			case Qt::Key_Enter:
			case Qt::Key_Return:
			case Qt::Key_Escape:
			case Qt::Key_Tab:
			case Qt::Key_Backtab:
				e->ignore();
				return;
			default:
				break;
		}
	}

	if(e->modifiers() == Qt::ControlModifier)
	{
		switch(e->key())
		{
			case Qt::Key_B:
				insertPlainText("$b");
				return;
			case Qt::Key_K:
				insertPlainText("$k");
				return;
			case Qt::Key_O:
				insertPlainText("$o");
				return;
			case Qt::Key_U:
				insertPlainText("$u");
				return;
			case Qt::Key_Enter:
			case Qt::Key_Return:
			case Qt::Key_PageUp:
				 e->ignore(); // allow the parent to process it
				 return;
			break;
		}
	}
// Adapted from QT4 QCompleter example
	bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E
	if (!m_pCompleter || !isShortcut) // don't process the shortcut when we have a completer
		QTextEdit::keyPressEvent(e);
	const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
	if (!m_pCompleter || (ctrlOrShift && e->text().isEmpty()))
		return;
	static QString eow("~!@#$%^&*()_+{}|:\"<>?,/;'[]\\-="); // end of word
	bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
	QString completionPrefix = textUnderCursor();
	if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3 || eow.contains(e->text().right(1))))
	{
		m_pCompleter->popup()->hide();
		return;
	}
	if (completionPrefix != m_pCompleter->completionPrefix())
	{
		m_pCompleter->setCompletionPrefix(completionPrefix);
		m_pCompleter->popup()->setCurrentIndex(m_pCompleter->completionModel()->index(0, 0));
	}
	QRect cr = cursorRect();
	cr.setWidth(m_pCompleter->popup()->sizeHintForColumn(0)+ m_pCompleter->popup()->verticalScrollBar()->sizeHint().width());
	m_pCompleter->complete(cr);
}

QString ScriptEditorWidget::textUnderCursor() const
{
	QString szWord;
	QTextCursor tc = textCursor();
	if(tc.atBlockStart())
		return QString();
	tc.clearSelection();
	tc.movePosition(QTextCursor::StartOfWord,QTextCursor::KeepAnchor);
	if(tc.atBlockStart())
	{
		szWord.append(tc.selectedText());
		tc.movePosition(QTextCursor::EndOfWord,QTextCursor::KeepAnchor);
		szWord.append(tc.selectedText());
		if(tc.atBlockEnd()){
			return szWord;
		}
		tc.movePosition(QTextCursor::NextCharacter,QTextCursor::KeepAnchor);
		szWord.append(tc.selectedText());
		if(szWord.right(1)!=".")
			szWord.chop(1);
		return szWord;
	}

	tc.movePosition(QTextCursor::PreviousCharacter,QTextCursor::KeepAnchor);
	szWord=tc.selectedText();
	if(szWord.left(1)==".")
	{
		tc.movePosition(QTextCursor::StartOfWord);
		tc.movePosition(QTextCursor::PreviousCharacter);
		tc.movePosition(QTextCursor::PreviousWord);
		tc.movePosition(QTextCursor::EndOfWord,QTextCursor::KeepAnchor,1);
		szWord.prepend(tc.selectedText());
	} else szWord.remove(0,1);
	return szWord;
}
/*
void ScriptEditorWidget::mouseReleaseEvent(QMouseEvent * e)
{

	return;
		QRect r = cursorRect();
		QTextCursor cur = cursorForPosition(e->pos());
	//	cur.select(


	//completelistbox->hide();
	if (e->button() == Qt::RightButton)
	{
		QString szBuffer;
		/\*
		int iPara = paragraphAt(e->pos());
		int iIndex=charAt(e->pos(),&iPara);
		szBuffer = this->text(iPara);
		getWordOnCursor(szBuffer,iIndex);
		*\/
		QRect r = cursorRect();
		QTextCursor cur = cursorForPosition(e->pos());
		cur.select(QTextCursor::WordUnderCursor);
		szBuffer = cur.selectedText();
		QString szTmp = szBuffer;
		KviPointerList<QString> * pList;
		if(szTmp.left(1) == "$")
		{
			szTmp.remove(0,1);
			pList = KviKvsKernel::instance()->completeCommandAllocateResult(szTmp);
			if (pList->count() != 1)
				szBuffer = "";
			else
				szBuffer = *(pList->at(0));
		} else {
			pList = KviKvsKernel::instance()->completeCommandAllocateResult(szTmp);
			if (pList->count() != 1)
				szBuffer = "";
			else
				szBuffer = *(pList->at(0));
		}

		for (unsigned int i=0; i < pList->count(); i++)
			QString str = *(pList->at(i));

		KviKvsKernel::instance()->freeCompletionResult(pList);
		m_szHelp = szBuffer;
	}

	QTextEdit::mouseReleaseEvent(e);
}
*/
bool ScriptEditorWidget::contextSensitiveHelp() const
{
	/*
	QString szBuffer;
	int iPara, iIndex;
	getCursorPosition(&iPara,&iIndex);
	szBuffer = text(iPara);
	getWordOnCursor(szBuffer,iIndex);
	*/

	QRect r = cursorRect();
	QTextCursor cur = cursorForPosition(QPoint(r.x(),r.y()));
	cur.select(QTextCursor::WordUnderCursor);
	QString szText = cur.selectedText();
	QString szTmp = szText;
	/*
	KviPointerList<QString> * pList;
	if(szTmp.left(1) == "$")
	{
		szTmp.remove(0,1);
		pList = g_pUserParser->completeFunctionAllocateResult(szTmp);
	} else {
		pList = g_pUserParser->completeCommandAllocateResult(szTmp);
	}
	qDebug("command or func %s",szTmp.toUtf8().data());

	bool bOk = false;
	if(pList)
	{
		for(QString * s = pList->first(); s; s = pList->next())
		{
			if(KviQString::equalCI(*s,szBuffer))
			{
				pList->last();
				bOk = true;
			}
		}
	}

	g_pUserParser->freeCompletionResult(pList);
	if(!bOk)return false;

	QString szParse = QString("timer -s (help,0){ help -s %Q; }").arg(szBuffer);
	qDebug("parsing %s",szParse.toLatin1());
	KviKvsScript::run(szParse,(KviWindow*)g_pApp->activeConsole());
	*/

	return true;
}

ScriptEditorWidgetColorOptions::ScriptEditorWidgetColorOptions(QWidget * pParent)
: QDialog(pParent)
{
	m_pSelectorInterfaceList = new KviPointerList<KviSelectorInterface>;
	m_pSelectorInterfaceList->setAutoDelete(false);
	setWindowTitle(__tr2qs_ctx("Preferences","editor"));
	QGridLayout * g = new QGridLayout(this);
	KviTalVBox * box = new KviTalVBox(this);
	g->addWidget(box,0,0);
	box->setMargin(0);
	box->setSpacing(0);
	KviFontSelector * f = new KviFontSelector(box,__tr2qs_ctx("Font:","editor"),&g_fntNormal,true);
	m_pSelectorInterfaceList->append(f);
	KviTalGroupBox * gbox = new KviTalGroupBox(Qt::Horizontal,__tr2qs_ctx("Colors","editor"),box);
	gbox->setInsideSpacing(0);

	addColorSelector(gbox,__tr2qs_ctx("Background:","editor"),&g_clrBackground,true);
	addColorSelector(gbox,__tr2qs_ctx("Normal text:","editor"),&g_clrNormalText,true);
	addColorSelector(gbox,__tr2qs_ctx("Brackets:","editor"),&g_clrBracket,true);
	addColorSelector(gbox,__tr2qs_ctx("Comments:","editor"),&g_clrComment,true);
	addColorSelector(gbox,__tr2qs_ctx("Functions:","editor"),&g_clrFunction,true);
	addColorSelector(gbox,__tr2qs_ctx("Keywords:","editor"),&g_clrKeyword,true);
	addColorSelector(gbox,__tr2qs_ctx("Variables:","editor"),&g_clrVariable,true);
	addColorSelector(gbox,__tr2qs_ctx("Punctuation:","editor"),&g_clrPunctuation,true);
	addColorSelector(gbox,__tr2qs_ctx("Find:","editor"),&g_clrFind,true);

	KviTalHBox * hbox = new KviTalHBox(box);

	QPushButton * b = new QPushButton(__tr2qs_ctx("OK","editor"),hbox);
	b->setDefault(true);
	connect(b,SIGNAL(clicked()),this,SLOT(okClicked()));

	b = new QPushButton(__tr2qs_ctx("Cancel","editor"),hbox);
	connect(b,SIGNAL(clicked()),this,SLOT(reject()));
}

ScriptEditorWidgetColorOptions::~ScriptEditorWidgetColorOptions()
{
	delete m_pSelectorInterfaceList;
}

KviColorSelector * ScriptEditorWidgetColorOptions::addColorSelector(QWidget * pParent,const QString & txt,QColor * pOption,bool bEnabled)
{
	KviColorSelector * s = new KviColorSelector(pParent,txt,pOption,bEnabled);
	m_pSelectorInterfaceList->append(s);
		return s;
}

void ScriptEditorWidgetColorOptions::okClicked()
{
	for(KviSelectorInterface * i = m_pSelectorInterfaceList->first();i;i = m_pSelectorInterfaceList->next())
	{
		i->commit();
	}
	accept();
}


ScriptEditorSyntaxHighlighter::ScriptEditorSyntaxHighlighter(ScriptEditorWidget * pWidget)
: QSyntaxHighlighter(pWidget),m_pTextEdit(pWidget)
{
	// code adpated from QT4 example

	// FIX-ME: "function ..." - "function internal ..."
	// FIX-ME: "@$"
	// FIX-ME: "\" escape char
	updateSyntaxtTextFormat();

	KviScriptHighlightingRule rule;

	rule.pattern=QRegExp("([=()[\\]!\"?<>;:.,+-])+");
	rule.format=punctuationFormat;
	highlightingRules.append(rule);

	rule.pattern=QRegExp("[{};](|[a-zA-Z]|[a-zA-Z]+[a-zA-Z0-9_\\.:]*)");
	rule.format=keywordFormat;
	highlightingRules.append(rule);


	rule.pattern=QRegExp("[$](|[a-zA-Z0-9]+[a-zA-Z0-9_\\.:]*)");
	rule.format=functionFormat;
	highlightingRules.append(rule);

	rule.pattern=QRegExp("[%](|[a-zA-Z]|[a-zA-Z]+[a-zA-Z0-9_\\.]*)");
	rule.format=variableFormat;
	highlightingRules.append(rule);

	rule.pattern=QRegExp("([{}])+");
	rule.format=bracketFormat;
	highlightingRules.append(rule);
}

ScriptEditorSyntaxHighlighter::~ScriptEditorSyntaxHighlighter()
{
}

void ScriptEditorSyntaxHighlighter::updateSyntaxtTextFormat()
{
	punctuationFormat.setForeground(g_clrPunctuation);
	keywordFormat.setForeground(g_clrKeyword);
	functionFormat.setForeground(g_clrFunction);
	variableFormat.setForeground(g_clrVariable);
	bracketFormat.setForeground(g_clrBracket);
	commentFormat.setForeground(g_clrComment);
	findFormat.setForeground(g_clrFind);
}

#define SKIP_SPACES \
	while(iIndexStart < szText.size()) \
	{ \
		if(szText.at(iIndexStart).unicode()=='\t' || szText.at(iIndexStart).unicode()==' ') \
		{ \
			iIndexStart++; \
		} else { \
			break; \
		} \
	} \

void ScriptEditorSyntaxHighlighter::highlightBlock(const QString & szText)
{
	if(szText.isEmpty())
	{
		setCurrentBlockState(previousBlockState());
		return;
	}

	int iIndexStart=0;
	setCurrentBlockState(0);

	if(previousBlockState() == 1)
	{
		// inside a multiline comment
		int iCommentEnd = szText.indexOf("*/");
		if(iCommentEnd < 0)
		{
			// not found: comment until the end of the block
			setCurrentBlockState(1);
			setFormat(iIndexStart,szText.length(),commentFormat);
			return;
		}
		// skip 2 chars
		iCommentEnd+=2;
		// end of comment found
		setFormat(iIndexStart,iCommentEnd - iIndexStart,commentFormat);
		iIndexStart = iCommentEnd;
	}

	SKIP_SPACES

	if (iIndexStart == szText.size())
		return;

	// check 'commands' and comments
	int iCommandStart=iIndexStart;
	if (szText.at(iIndexStart).unicode()!='$' && szText.at(iIndexStart).unicode()!='{' && szText.at(iIndexStart).unicode()!='}' && szText.at(iIndexStart).unicode()!='%')
	{
		switch(szText.at(iIndexStart).unicode())
		{
			case '#':
				// single line comment, bash style
				while(iIndexStart<szText.size() && szText.at(iIndexStart).unicode() != '\n')
					iIndexStart++;
				setFormat(iCommandStart,iIndexStart-iCommandStart,commentFormat);
				break;
			case '/':
				if((iIndexStart+1)<szText.size())
				{
					if(szText.at(iIndexStart+1).unicode()=='/')
					{
						// single line comment, c++ style
						iIndexStart++;
						while(iIndexStart<szText.size() && szText.at(iIndexStart).unicode() != '\n')
							iIndexStart++;
						setFormat(iCommandStart,iIndexStart-iCommandStart,commentFormat);
						break;
					}

					if(szText.at(iIndexStart+1).unicode()=='*')
					{
						// multi line comment, c style
						iIndexStart++;
						setCurrentBlockState(1);
						while(iIndexStart<szText.size())
						{
							if((iIndexStart+1)<szText.size())
							{
								if(szText.at(iIndexStart).unicode() == '*' &&
									szText.at(iIndexStart+1).unicode() == '/')
								{
									iIndexStart+=2;
									setCurrentBlockState(0);
									break;
								}
							}
							iIndexStart++;
						}
						setFormat(iCommandStart,iIndexStart-iCommandStart,commentFormat);

						SKIP_SPACES

						if(currentBlockState()==0)
						{
							iCommandStart=iIndexStart;
						} else {
							break;
						}
						// else fallback to command (a command can start at the end of a /* comment */)
					}
					// else not a comment, fallback to command
				}
			default:
				//command
				while(iIndexStart<szText.size() && (szText.at(iIndexStart).isLetterOrNumber() || (szText.at(iIndexStart).unicode() == '.') || (szText.at(iIndexStart).unicode() == '_') || (szText.at(iIndexStart).unicode()== ':') ))
				{
					iIndexStart++;
				}
				setFormat(iCommandStart,iIndexStart-iCommandStart,keywordFormat);
				break;
		}
	}

	// code from QT4 example
	int index=0;
	foreach (KviScriptHighlightingRule rule, highlightingRules)
	{
		QRegExp expression(rule.pattern);
		QString sz=expression.pattern();

		index = szText.indexOf(expression,iIndexStart);

		while (index >= 0)
		{
			int length = expression.matchedLength();
			setFormat(index, length, rule.format);
			index = szText.indexOf(expression, index + length);
		}
	}

	// 'found matches' highlighting
	ScriptEditorWidget * pEditor = ((ScriptEditorWidget *)textEdit());
	QString szFind = pEditor->m_szFind;
	if (!szFind.isEmpty())
	{
		index = szText.indexOf(szFind,0,Qt::CaseInsensitive);
		int length=szFind.length();
		while (index >= 0)
		{
			setFormat(index, length, findFormat);
			index = szText.indexOf(szFind,index + length,Qt::CaseInsensitive);
		}
	}
}


ScriptEditorImplementation::ScriptEditorImplementation(QWidget * par)
:KviScriptEditor(par)
{
	m_pOptionsDialog=0;
	if(g_pScriptEditorWindowList->isEmpty())loadOptions();
	g_pScriptEditorWindowList->append(this);
	m_lastCursorPos = 0;
	QGridLayout * g = new QGridLayout(this);

	m_pFindLineEdit = new QLineEdit(" ",this);
	m_pFindLineEdit->setText("");

	QPalette p = m_pFindLineEdit->palette();
	p.setColor(foregroundRole(), g_clrFind);
	m_pFindLineEdit->setPalette(p);

	m_pEditor = new ScriptEditorWidget(this);

	g->addWidget(m_pEditor, 0, 0, 1,4);
	g->setRowStretch(0,1);

	QToolButton * b = new QToolButton(this);
	b->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Options)));

	b->setObjectName("ToolButtonEditor");
	b->setMinimumWidth(24);
	g->addWidget(b,1,0);

	KviTalPopupMenu * pop = new KviTalPopupMenu(b);
	pop->insertItem(__tr2qs_ctx("&Open...","editor"),this,SLOT(loadFromFile()));
	pop->insertItem(__tr2qs_ctx("&Save As...","editor"),this,SLOT(saveToFile()));
	pop->insertSeparator();
	pop->insertItem(__tr2qs_ctx("&Configure Editor...","editor"),this,SLOT(configureColors()));
	b->setMenu(pop);
	b->setPopupMode(QToolButton::InstantPopup);

	g->setColumnStretch(1,1);
	g->setColumnStretch(2,10);
	g->addWidget(m_pFindLineEdit,1,2);

	QLabel * pLab = new QLabel(this);
	pLab->setText(__tr2qs_ctx("Find","editor"));
	g->addWidget(pLab,1,1);

	m_pRowColLabel = new QLabel("0",this);
	m_pRowColLabel->setFrameStyle(QFrame::Sunken | QFrame::Panel);
	m_pRowColLabel->setMinimumWidth(80);
	g->addWidget(m_pRowColLabel,1,3);

	connect(m_pFindLineEdit,SIGNAL(returnPressed()),m_pEditor,SLOT(slotFind()));
	connect(m_pFindLineEdit,SIGNAL(returnPressed()),this,SLOT(slotFind()));
	connect(m_pEditor,SIGNAL(cursorPositionChanged()),this,SLOT(updateRowColLabel()));
	connect(m_pEditor,SIGNAL(selectionChanged()),this,SLOT(updateRowColLabel()));
	m_lastCursorPos = 0;
}

ScriptEditorImplementation::~ScriptEditorImplementation()
{
	if(m_pOptionsDialog)
	{
		m_pOptionsDialog->deleteLater();
		m_pOptionsDialog=0;
	}
	g_pScriptEditorWindowList->removeRef(this);
	if(g_pScriptEditorWindowList->isEmpty())
		saveOptions();
}

void ScriptEditorImplementation::loadOptions()
{
	QString szTmp;
	g_pEditorModulePointer->getDefaultConfigFileName(szTmp);

	KviConfigurationFile cfg(szTmp,KviConfigurationFile::Read);
	g_clrBackground = cfg.readColorEntry("Background",QColor(0,0,0));;
	g_clrNormalText = cfg.readColorEntry("NormalText",QColor(100,255,0));
	g_clrBracket = cfg.readColorEntry("Bracket",QColor(255,0,0));
	g_clrComment = cfg.readColorEntry("Comment",QColor(0,120,0));
	g_clrFunction = cfg.readColorEntry("Function",QColor(255,255,0));
	g_clrKeyword = cfg.readColorEntry("Keyword",QColor(120,120,150));
	g_clrVariable = cfg.readColorEntry("Variable",QColor(200,200,200));
	g_clrPunctuation = cfg.readColorEntry("Punctuation",QColor(180,180,0));
	g_clrFind = cfg.readColorEntry("Find",QColor(255,0,0));
	g_fntNormal = cfg.readFontEntry("Font",QFont(KVI_SCRIPTEDITOR_DEFAULT_FONT,8));
}

bool ScriptEditorImplementation::isModified()
{
	return m_pEditor->document()->isModified();
}
void ScriptEditorImplementation::setModified(bool bModified)
{
	return m_pEditor->document()->setModified(bModified);
}
void ScriptEditorImplementation::slotFind()
{
	emit find(m_pFindLineEdit->text());
}

void ScriptEditorImplementation::slotNextFind(const QString &) //szText
{
//	emit nextFind(const QString & szText);
}

void ScriptEditorImplementation::slotInitFind()
{
	emit initFind();
}

void ScriptEditorImplementation::slotReplaceAll(const QString & szToReplace, const QString & szReplaceWith)
{
	emit replaceAll(szToReplace,szReplaceWith);
}

void ScriptEditorImplementation::saveOptions()
{
	QString szTmp;
	g_pEditorModulePointer->getDefaultConfigFileName(szTmp);

	KviConfigurationFile cfg(szTmp,KviConfigurationFile::Write);
	cfg.writeEntry("Background",g_clrBackground);;
	cfg.writeEntry("NormalText",g_clrNormalText);
	cfg.writeEntry("Bracket",g_clrBracket);
	cfg.writeEntry("Comment",g_clrComment);
	cfg.writeEntry("Function",g_clrFunction);
	cfg.writeEntry("Keyword",g_clrKeyword);
	cfg.writeEntry("Variable",g_clrVariable);
	cfg.writeEntry("Punctuation",g_clrPunctuation);
	cfg.writeEntry("Find",g_clrFind);
	cfg.writeEntry("Font",g_fntNormal);
}

void ScriptEditorImplementation::setFocus()
{
	m_pEditor->setFocus();
}

void ScriptEditorImplementation::focusInEvent(QFocusEvent *)
{
	m_pEditor->setFocus();
}

void ScriptEditorImplementation::setEnabled(bool bEnabled)
{
	QWidget::setEnabled(bEnabled);
	m_pEditor->setEnabled(bEnabled);
	m_pRowColLabel->setEnabled(bEnabled);
}

void ScriptEditorImplementation::saveToFile()
{
	QString szFileName;
	if(KviFileDialog::askForSaveFileName(szFileName,
		__tr2qs_ctx("Choose a Filename - KVIrc","editor"),
		QString::null,
		QString::null,false,true,true))
	{
		QString szBuffer = m_pEditor->toPlainText();

		//if(tmp.isEmpty())tmp = "";
		//KviCString buffer = tmp.toUtf8().data();
		if(!KviFileUtils::writeFile(szFileName,szBuffer))
		{
			QString szTmp;
			QMessageBox::warning(this,
				__tr2qs_ctx("Save Failed - KVIrc","editor"),
				szTmp = QString(__tr2qs_ctx("Can't open the file %1 for writing.","editor")).arg(szFileName));
		}
	}
}

void ScriptEditorImplementation::setText(const char * txt)
{
	setText(QByteArray(txt));
}
void ScriptEditorImplementation::setUnHighlightedText(const QString & szText)
{
	m_pEditor->disableSyntaxHighlighter();
	m_pEditor->setText(szText);
}
void ScriptEditorImplementation::setText(const QByteArray & szText)
{
	m_pEditor->setPlainText(szText.data());
	m_pEditor->document()->setModified(false);
	updateRowColLabel();
}
void ScriptEditorImplementation::setReadOnly(bool bReadOnly)
{
	m_pEditor->setReadOnly(bReadOnly);
}
void ScriptEditorImplementation::getText(QByteArray & szText)
{
	szText = m_pEditor->toPlainText().toUtf8();
}

void ScriptEditorImplementation::setText(const QString & szText)
{
	m_pEditor->enableSyntaxHighlighter();
	m_pEditor->setPlainText(szText);
	QTextCursor cur=m_pEditor->textCursor();
	cur.movePosition(QTextCursor::End);
	m_pEditor->setTextCursor(cur);
	m_pEditor->document()->setModified(false);
	updateRowColLabel();
}

void ScriptEditorImplementation::getText(QString & szText)
{
	szText = m_pEditor->toPlainText();
}

void ScriptEditorImplementation::setFindText(const QString & szText)
{
	m_pFindLineEdit->setText(szText);
	m_pEditor->slotFind();
}

void ScriptEditorImplementation::updateRowColLabel()
{
	if(m_lastCursorPos==m_pEditor->textCursor().position())
		return;
	int iRow = m_pEditor->textCursor().blockNumber();
	int iCol = m_pEditor->textCursor().columnNumber();
	QString szTmp = QString(__tr2qs_ctx("Row: %1 Col: %2","editor")).arg(iRow).arg(iCol);
	m_pRowColLabel->setText(szTmp);
	m_lastCursorPos = m_pEditor->textCursor().position();
}

void ScriptEditorImplementation::setCursorPosition(int iPos)
{
	QTextCursor cur = m_pEditor->textCursor();
	cur.setPosition(iPos);
	m_pEditor->setTextCursor(cur);
	updateRowColLabel();
}

void ScriptEditorImplementation::loadFromFile()
{
	QString szFileName;
	if(KviFileDialog::askForOpenFileName(szFileName,
		__tr2qs_ctx("Load Script File - KVIrc","editor"),
		QString::null,KVI_FILTER_SCRIPT,false,true))
	{
		QString szBuffer;
		if(KviFileUtils::loadFile(szFileName,szBuffer))
		{
                        m_pEditor->setPlainText(szBuffer);
			setCursorPosition(0);
			//m_pEditor->moveCursor(QTextEdit::MoveEnd,false);
			//updateRowColLabel();
		} else {
			QString szTmp;
			QMessageBox::warning(this,
				__tr2qs_ctx("Open Failed - KVIrc","editor"),
				szTmp = QString(__tr2qs_ctx("Can't open the file %1 for reading.","editor")).arg(szFileName));
		}
	}
}

void ScriptEditorImplementation::configureColors()
{
	if(!m_pOptionsDialog)
	{
		m_pOptionsDialog = new ScriptEditorWidgetColorOptions(this);
		connect(m_pOptionsDialog,SIGNAL(finished(int)),this,SLOT(optionsDialogFinished(int)));
	}
	m_pOptionsDialog->show();
}

void ScriptEditorImplementation::optionsDialogFinished(int iResult)
{
	if(iResult == QDialog::Accepted)
	{
		m_pEditor->updateOptions();
		saveOptions();
	}
}

ScriptEditorReplaceDialog::ScriptEditorReplaceDialog(QWidget * pParent, const QString & szName)
: QDialog(pParent)
{
	setObjectName(szName);

	m_pParent = pParent;
	emit initFind();
	QPalette p = palette();
	p.setColor(foregroundRole(),QColor( 0, 0, 0 ));
	p.setColor(backgroundRole(),QColor( 236, 233, 216 ));
	setPalette(p);

	QGridLayout * pLayout = new QGridLayout(this);
	pLayout->setObjectName("replace layout");

	QLabel * m_pFindLabel = new QLabel(this);
	m_pFindLabel->setObjectName("findlabel");
	m_pFindLabel->setText(__tr2qs_ctx("Word to Find","editor"));
	pLayout->addWidget(m_pFindLabel,0,0);

	m_pFindLineEdit = new QLineEdit(this);
	m_pFindLineEdit->setObjectName("findlineedit");
	pLayout->addWidget(m_pFindLineEdit,0,1);

	QLabel * m_pReplaceLabel = new QLabel(this);
	m_pReplaceLabel->setObjectName("replacelabel");
	m_pReplaceLabel->setText(__tr2qs_ctx("Replace with","editor"));
	pLayout->addWidget(m_pReplaceLabel,1,0);

	m_pReplaceLineEdit = new QLineEdit(this);
	m_pReplaceLineEdit->setObjectName("replacelineedit");
	pLayout->addWidget(m_pReplaceLineEdit,1,1);


	m_pFindLineEdit->setFocus();

	m_pCheckReplaceAll = new QCheckBox(this);
	m_pCheckReplaceAll->setObjectName("replaceAll");
	m_pCheckReplaceAll->setText(__tr2qs_ctx("&Replace in all editor's items","editor"));
	pLayout->addWidget(m_pCheckReplaceAll,2,0);

	QPushButton * pCancelButton = new QPushButton(this);
	pCancelButton->setObjectName("cancelButton");
	pCancelButton->setText(__tr2qs_ctx("&Cancel","editor"));
	pLayout->addWidget(pCancelButton,3,0);

	m_pReplaceButton = new QPushButton(this);
	m_pReplaceButton->setObjectName("replacebutton");
	m_pReplaceButton->setText(__tr2qs_ctx("&Replace","editor"));
	m_pReplaceButton->setEnabled(false);
	pLayout->addWidget(m_pReplaceButton,3,1);

	setLayout(pLayout);

	// signals and slots connections
	connect(m_pReplaceButton, SIGNAL(clicked()), this, SLOT(slotReplace()));
	connect(pCancelButton, SIGNAL(clicked()), this, SLOT(reject()));
	connect(m_pFindLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));
}

ScriptEditorReplaceDialog::~ScriptEditorReplaceDialog()
{
}

void ScriptEditorReplaceDialog::textChanged(const QString & szText)
{
	if(!szText.isEmpty())
		m_pReplaceButton->setEnabled(true);
	else
		m_pReplaceButton->setEnabled(false);
}

void ScriptEditorReplaceDialog::slotReplace()
{
	QString szText = ((ScriptEditorWidget *)m_pParent)->toPlainText();
	if(m_pCheckReplaceAll->isChecked())
		emit replaceAll(m_pFindLineEdit->text(),m_pReplaceLineEdit->text());

	szText.replace(m_pFindLineEdit->text(),m_pReplaceLineEdit->text(),Qt::CaseInsensitive);
	((ScriptEditorWidget *)m_pParent)->setText(szText);
	((ScriptEditorWidget *)m_pParent)->document()->setModified(true);
	m_pFindLineEdit->setText("");
	m_pReplaceLineEdit->setText("");
	setTabOrder(m_pFindLineEdit,m_pReplaceLineEdit);
}

void ScriptEditorReplaceDialog::slotNextFind()
{
	emit nextFind(m_pFindLineEdit->text());
}
