/***************************************************************************
 *   Copyright (C) 2006-2012 by Thomas Schweitzer                          *
 *   thomas-schweitzer(at)arcor.de                                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License version 2.0 as   *
 *   published by the Free Software Foundation.                            *
 *                                                                         *
 *   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 in the file LICENSE.GPL; if not, write to the *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "UiGuiHighlighter.h"

#include "SettingsPaths.h"

#include <tqaction.h>
#include <tqsettings.h>

#include <tqextscintilla.h>
#include <tqextscintillalexer.h>
#include <tqextscintillalexerbash.h>
#include <tqextscintillalexerbatch.h>
#include <tqextscintillalexercpp.h>
#include <tqextscintillalexercsharp.h>
#include <tqextscintillalexercss.h>
#include <tqextscintillalexerdiff.h>
#include <tqextscintillalexerhtml.h>
#include <tqextscintillalexeridl.h>
#include <tqextscintillalexerjava.h>
#include <tqextscintillalexerjavascript.h>
#include <tqextscintillalexerlua.h>
#include <tqextscintillalexermakefile.h>
#include <tqextscintillalexerperl.h>
#include <tqextscintillalexerpov.h>
#include <tqextscintillalexerproperties.h>
#include <tqextscintillalexerpython.h>
#include <tqextscintillalexerruby.h>
#include <tqextscintillalexersql.h>
#include <tqextscintillalexertex.h>

/*
    \class UiGuiHighlighter
    \ingroup grp_EditorComponent
    \brief UiGuiHighlighter used for selecting the syntax highlighter/lexer for the TQextScintilla component.
*/

/*
    \brief The constructor initializes some regular expressions and keywords to identify cpp tokens
 */
UiGuiHighlighter::UiGuiHighlighter(TQextScintilla *parent) :
		TQObject((TQObject*)parent)
{
	m_qsciEditorParent = parent;

	// Create the highlighter m_settings object from the uiguisyntaxhighlightconfigrc file.
	m_settings = new TQSettings(TQSettings::Ini);
	// The next lines make user the settings are always stored in
	// $HOME/.universalindentgui/uiguisyntaxhighlightconfigrc
	m_settings->insertSearchPath(TQSettings::Unix, SettingsPaths::getSettingsPath());
  m_settings->setPath("UniversalIndentGUI", "UniversalIndentGUI", TQSettings::User);

	m_highlightingIsOn = true;

	m_fileExtensions["Bash"]       = TQStringList() << "sh";
	m_fileExtensions["Batch"]      = TQStringList() << "bat";
	m_fileExtensions["C++"]        = TQStringList() << "c" << "h" << "cpp" << "hpp" << "cxx"<< "hxx";
	m_fileExtensions["C#"]         = TQStringList() << "cs";
	m_fileExtensions["CSS"]        = TQStringList() << "css";
	m_fileExtensions["Diff"]       = TQStringList() << "diff";
	m_fileExtensions["HTML"]       = TQStringList() << "html" << "htm";
	m_fileExtensions["IDL"]        = TQStringList() << "idl";
	m_fileExtensions["Java"]       = TQStringList() << "java";
	m_fileExtensions["JavaScript"] = TQStringList() << "js";
	m_fileExtensions["LUA"]        = TQStringList() << "lua";
	m_fileExtensions["Makefile"]   = TQStringList() << "makefile";
	m_fileExtensions["Perl"]       = TQStringList() << "perl" << "pl" << "pm";
	m_fileExtensions["PHP"]        = TQStringList() << "php";
	m_fileExtensions["POV"]        = TQStringList() << "pov";
	m_fileExtensions["Ini"]        = TQStringList() << "ini";
	m_fileExtensions["Python"]     = TQStringList() << "py";
	m_fileExtensions["Ruby"]       = TQStringList() << "rub" << "rb";
	m_fileExtensions["SQL"]        = TQStringList() << "sql";
	m_fileExtensions["TeX"]        = TQStringList() << "tex";
	m_fileExtensions["XML"]        = TQStringList() << "xml";

	m_lexer = NULL;

	// Set default highlighter to C++ highlighter.
	setLexer("cpp");
}

UiGuiHighlighter::~UiGuiHighlighter()
{
	delete m_settings;
}

/*
    \brief Returns the available highlighters as TQStringList.
 */
TQStringList UiGuiHighlighter::getAvailableHighlighters()
{
	return m_fileExtensions.keys();
}

/*
    \brief This slot handles signals coming from selecting another syntax highlighter.
 */
void UiGuiHighlighter::setHighlighterByAction(TQAction *highlighterAction)
{
	TQString highlighterName = highlighterAction->text();
	setLexer(m_fileExtensions[highlighterName].first());
	// TODO: This is really no nice way. How do it better?
	// Need to do this "text update" to update the syntax highlighting. Otherwise highlighting is
	// wrong.
	// TODO not available in TQScintilla 1.71
  //---	int scrollPos = m_qsciEditorParent->verticalScrollBar()->value();
	m_qsciEditorParent->setText(m_qsciEditorParent->text());
  //---	m_qsciEditorParent->verticalScrollBar()->setValue(scrollPos);
}

/*
    \brief Turns the syntax parser on.
*/
void UiGuiHighlighter::turnHighlightOn()
{
 	m_highlightingIsOn = true;
	m_qsciEditorParent->setLexer(m_lexer);
	readCurrentSettings();
}

/*
    \brief Turns the syntax parser off.
*/
void UiGuiHighlighter::turnHighlightOff()
{
 	m_highlightingIsOn = false;
	m_qsciEditorParent->setLexer();
	m_qsciEditorParent->setFont(TQFont("Monospace", 10, TQFont::Normal));
	m_qsciEditorParent->setMarginsFont(TQFont("Monospace", 10, TQFont::Normal));
}

/*
    \brief Read the settings for the current lexer from the settings file.
 */
bool UiGuiHighlighter::readCurrentSettings()
{
	// settings are read from the uiguisyntaxhighlightconfigrc file, group <lexer name>
	m_settings->resetGroup();
	m_settings->beginGroup(TQString("uiguisyntaxhighlightconfig/") + m_lexer->language());

	bool     ok, flag, rc = true;
	int      num;

	// Reset lists containing fonts and colors for each style
	m_fontForStyles.clear();
	m_colorForStyles.clear();

	// Read the styles.
	for (int i = 0; i < 128; ++i)
	{
		// Ignore invalid styles.
		if (m_lexer->description(i).isEmpty())
		{
			continue;
		}

		TQString key = TQString();
		key.sprintf("style%02d/", i);
		key.replace("+", "p");

		// Read the foreground color.
		num = m_settings->readNumEntry(key + "color", 0, &ok);
		if (ok)
		{
			setColor(TQColor((num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff), i);
		}
		else
		{
			rc = false;
		}

		// Read the end-of-line fill.
		flag = m_settings->readBoolEntry(key + "eolfill", false, &ok);
		if (ok)
		{
			m_lexer->setEolFill(flag, i);
		}
		else
		{
			rc = false;
		}

		// Read the font
		TQStringList fdesc;
		fdesc = m_settings->readListEntry(key + "font", &ok);
		if (ok && fdesc.count() == 5)
		{
			TQFont f;
			f.setFamily(fdesc[0]);
			f.setPointSize(fdesc[1].toInt());
			f.setBold(fdesc[2].toInt());
			f.setItalic(fdesc[3].toInt());
			f.setUnderline(fdesc[4].toInt());
			setFont(f, i);
		}
		else
		{
			rc = false;
		}

		// Read the background color.
		num = m_settings->readNumEntry(key + "paper", 0, &ok);
		if (ok)
		{
			m_lexer->setPaper(TQColor((num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff), i);
		}
		else
		{
			rc = false;
		}
	}

	// Read the properties.
	m_lexer->refreshProperties();

	return rc;
}

/*
    \brief Write the settings for the current lexer to the settings file.
 */
void UiGuiHighlighter::writeCurrentSettings()
{
	// settings are stored in the uiguisyntaxhighlightconfigrc file, group <lexer name>
	m_settings->resetGroup();
	m_settings->beginGroup(TQString("uiguisyntaxhighlightconfig/") + m_lexer->language());

	// Write the styles.
	for (int i = 0; i < 128; ++i)
	{
		// Ignore invalid styles.
		if (m_lexer->description(i).isEmpty())
		{
			continue;
		}

		int     num;
		TQColor c;

		TQString key = TQString();
		key.sprintf("style%02d/", i);
		key.replace("+", "p");

		// Write style name
		m_settings->writeEntry(key + "", m_lexer->description(i));

		// Write the foreground color.
		if (m_colorForStyles.contains(i))
		{
			c = m_colorForStyles[i];
		}
		else
		{
			c = m_lexer->color(i);
		}
		num = (c.red() << 16) | (c.green() << 8) | c.blue();

		m_settings->writeEntry(key + "color", num);

		// Write the end-of-line fill.
		m_settings->writeEntry(key + "eolfill", m_lexer->eolFill(i));

		// Write the font
		TQStringList fdesc;
		TQString     fmt("%1");
		TQFont       f;
		if (m_fontForStyles.contains(i))
		{
			f = m_fontForStyles[i];
		}
		else
		{
			f = m_lexer->font(i);
		}

		fdesc += f.family();
		fdesc += fmt.arg(f.pointSize());
		fdesc += fmt.arg((int)f.bold());
		fdesc += fmt.arg((int)f.italic());
		fdesc += fmt.arg((int)f.underline());

		m_settings->writeEntry(key + "font", fdesc);

		// Write the background color.
		c   = m_lexer->paper(i);
		num = (c.red() << 16) | (c.green() << 8) | c.blue();

		m_settings->writeEntry(key + "paper", num);
	}
}

/*
    \brief Sets the \a color for the given \a style.
 */
void UiGuiHighlighter::setColor(const TQColor &color, int style)
{
	m_colorForStyles[style] = color;
	m_lexer->setColor(color, style);
}

/*
    \brief Sets the \a font for the given \a style.
 */
void UiGuiHighlighter::setFont(const TQFont &font, int style)
{
	m_fontForStyles[style] = font;
	m_lexer->setFont(font, style);
}

/*
    \brief Sets the proper highlighter/lexer for the given file \a extension. Returns the index of the used lexer in the list.
 */
int UiGuiHighlighter::setLexer(TQString extension)
{
	int indexOfHighlighter = 0;
	extension = extension.lower();

	if (m_lexer)
	{
		writeCurrentSettings();
		delete m_lexer;
		m_lexer = nullptr;
	}

	if (extension == "cpp" || extension == "hpp" || extension == "c" || extension == "h" ||
	    extension == "cxx" || extension == "hxx")
	{
		m_lexer = new TQextScintillaLexerCPP();
	}
	else if (extension == "sh")
	{
		m_lexer = new TQextScintillaLexerBash();
	}
	else if (extension == "bat")
	{
		m_lexer = new TQextScintillaLexerBatch();
	}
	else if (extension == "cs")
	{
		m_lexer = new TQextScintillaLexerCSharp();
	}
	else if (extension == "css")
	{
		m_lexer = new TQextScintillaLexerCSS();
	}
	else if (extension == "diff")
	{
		m_lexer = new TQextScintillaLexerDiff();
	}
	else if (extension == "html" || extension == "htm")
	{
		m_lexer = new TQextScintillaLexerHTML();
	}
	else if (extension == "idl")
	{
		m_lexer = new TQextScintillaLexerIDL();
	}
	else if (extension == "java")
	{
		m_lexer = new TQextScintillaLexerJava();
	}
	else if (extension == "js")
	{
		m_lexer = new TQextScintillaLexerJavaScript();
	}
	else if (extension == "lua")
	{
		m_lexer = new TQextScintillaLexerLua();
	}
	else if (extension == "makefile")
	{
		m_lexer = new TQextScintillaLexerMakefile();
	}
	else if (extension == "perl" || extension == "pl" || extension == "pm")
	{
		m_lexer = new TQextScintillaLexerPerl();
	}
	else if (extension == "php")
	{
		m_lexer = new TQextScintillaLexerHTML();
	}
	else if (extension == "pov")
	{
		m_lexer = new TQextScintillaLexerPOV();
	}
	else if (extension == "ini")
	{
		m_lexer = new TQextScintillaLexerProperties();
	}
	else if (extension == "py")
	{
		m_lexer = new TQextScintillaLexerPython();
	}
	else if (extension == "rub" || extension == "rb")
	{
		m_lexer = new TQextScintillaLexerRuby();
	}
	else if (extension == "sql")
	{
		m_lexer = new TQextScintillaLexerSQL();
	}
	else if (extension == "tex")
	{
		m_lexer = new TQextScintillaLexerTeX();
	}
	else if (extension == "xml")
	{
		m_lexer = new TQextScintillaLexerHTML();
	}
	else
	{
		m_lexer    = new TQextScintillaLexerCPP();
		extension = "cpp";
	}

	// Find the index of the selected m_lexer.
	indexOfHighlighter = 0;
	for (const TQString &hlExtList : m_fileExtensions.keys())
	{
		if (m_fileExtensions[hlExtList].contains(extension))
		{
			break;
		}
		indexOfHighlighter++;
	}

	// Set the m_lexer for the TQScintilla widget.
	if (m_highlightingIsOn)
	{
		m_qsciEditorParent->setLexer(m_lexer);
	}

	// Read the m_settings for the m_lexer properties from file.
	readCurrentSettings();

	return indexOfHighlighter;
}

#include "UiGuiHighlighter.moc"