/* This file is part of ksirc
   Copyright (c) 2001 Malte Starostik <malte@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

/*
Color parser code courtesy of ksirc <http://www.kde.org>
Modified by Jason Keirstead <jason@keirstead.org>
*/

#include <knotifyclient.h>
#include <kdebug.h>
#include <tqbuffer.h>
#include <tqdatastream.h>
#include <tqstylesheet.h>
#include "ksparser.h"
#include <stdlib.h>

KSParser KSParser::m_parser;

const TQColor KSParser::IRC_Colors[17]=
{
	TQt::white,
	TQt::black,
	TQt::darkBlue,
	TQt::darkGreen,
	TQt::red,
	TQt::darkRed,
	TQt::darkMagenta,
	TQt::darkYellow,
	TQt::yellow,
	TQt::green,
	TQt::darkCyan,
	TQt::cyan,
	TQt::blue,
	TQt::magenta,
	TQt::darkGray,
	TQt::gray,
	TQColor() // default invalid color, must be the last
};

const TQRegExp KSParser::sm_colorsModeRegexp("(\\d{1,2})(?:,(\\d{1,2}))?");

template <typename _TYPE_>
	inline void swap(_TYPE_ &o1, _TYPE_ &o2)
{
	_TYPE_ tmp = o1;
	o1 = o2;
	o2 = tmp;
}

KSParser::KSParser()
{
	kdDebug(14120) << k_funcinfo << endl;
}

KSParser::~KSParser()
{
	kdDebug(14120) << k_funcinfo << endl;
}

/* NOTE: If thread corruption are seen simply ad a qlock here */
TQCString KSParser::parse(const TQCString &message)
{
	return m_parser._parse(message);
}

TQCString KSParser::_parse(const TQCString &message)
{
	TQCString data( message.size() * 2 );
	TQBuffer buff( data );
	buff.open( IO_WriteOnly );

	m_tags.clear();
	m_attributes.clear();

	TQRegExp colorsModeRegexp(sm_colorsModeRegexp);

	// should be set to the current default colors ....
	TQColor fgColor; /*KopeteMesage::fg().name()*/
	TQColor bgColor; /*KopeteMesage::bg().name()*/

	uint chars = 0;
	for(uint i = 0; i < message.length(); ++i)
	{
		const TQChar &cur = message[i];
		TQString toAppend;

		switch (cur)
		{
			case 0x02:	//Bold: ^B
				toAppend= toggleTag("b");
				break;
			case 0x03:	//Color code: ^C
				if (colorsModeRegexp.search(message, i+1) == (int)i+1)
				{
					i += colorsModeRegexp.matchedLength(); // + 1 will be added by ++
					TQString tagStyle;

					fgColor = ircColor(colorsModeRegexp.cap(1));
					bgColor = ircColor(colorsModeRegexp.cap(2));

					toAppend = pushColorTag(fgColor, bgColor);
				}
				else
				{
					toAppend = popTag(TQString::fromLatin1("span"));
				}
				break;
			case 0x07:	//System bell: ^G
				KNotifyClient::beep( TQString::fromLatin1("IRC beep event received in a message") );
				break;
			case '\t':	// 0x09
				toAppend = TQString::fromLatin1("&nbsp;&nbsp;&nbsp;&nbsp;");
				break;
			case '\n':	// 0x0D
				toAppend= TQString::fromLatin1("<br/>");
				break;
			case 0x0D:	// Italics: ^N
				toAppend = toggleTag("i");
				break;
			case 0x0F:	//Plain Text, close all tags: ^O
				toAppend = popAll();
				break;
	//		case 0x12:	// Reverse original text colors: ^R
	//			break;
			case 0x16:	//Invert Colors: ^V
				swap(fgColor, bgColor);
				toAppend = pushColorTag(fgColor, bgColor);
				break;
			case 0x1F:	//Underline
				toAppend = toggleTag("u");
				break;
			case '<':
				toAppend = TQString::fromLatin1("&lt;");
				break;
			case '>':
				toAppend = TQString::fromLatin1("&gt;");
				break;
			default:
				if (cur < TQChar(' ')) // search for control characters
					toAppend = TQString::fromLatin1("&lt;%1&gt;").arg(cur, 2, 16).upper();
				else
					toAppend = TQStyleSheet::escape(cur);
		}

		chars += toAppend.length();
		buff.writeBlock( toAppend.latin1(), toAppend.length() );
	}

	TQString toAppend = popAll();
	chars += toAppend.length();
	buff.writeBlock( toAppend.latin1(), toAppend.length() );

	// Make sure we have enough room for NULL character.
	if (data.size() < chars+1)
		data.resize(chars+1);

	data[chars] = '\0';

	return data;
}

TQString KSParser::pushTag(const TQString &tag, const TQString &attributes)
{
	TQString res;
	m_tags.push(tag);
	if(!m_attributes.contains(tag))
		m_attributes.insert(tag, attributes);
	else if(!attributes.isEmpty())
		m_attributes.replace(tag, attributes);
	res.append("<" + tag);
	if(!m_attributes[tag].isEmpty())
		res.append(" " + m_attributes[tag]);
	return res + ">";
}

TQString KSParser::pushColorTag(const TQColor &fgColor, const TQColor &bgColor)
{
	TQString tagStyle;

	if (fgColor.isValid())
		tagStyle += TQString::fromLatin1("color:%1;").arg(fgColor.name());
	if (bgColor.isValid())
		tagStyle += TQString::fromLatin1("background-color:%1;").arg(bgColor.name());

	if(!tagStyle.isEmpty())
		tagStyle = TQString::fromLatin1("style=\"%1\"").arg(tagStyle);

	return pushTag(TQString::fromLatin1("span"), tagStyle);;
}

TQString KSParser::popTag(const TQString &tag)
{
	if (!m_tags.contains(tag))
		return TQString();

	TQString res;
	TQValueStack<TQString> savedTags;
	while(m_tags.top() != tag)
	{
		savedTags.push(m_tags.pop());
		res.append("</" + savedTags.top() + ">");
	}
	res.append("</" + m_tags.pop() + ">");
	m_attributes.remove(tag);
	while(!savedTags.isEmpty())
		res.append(pushTag(savedTags.pop()));
	return res;
}

TQString KSParser::toggleTag(const TQString &tag)
{
	return m_attributes.contains(tag)?popTag(tag):pushTag(tag);
}

TQString KSParser::popAll()
{
	TQString res;
	while(!m_tags.isEmpty())
		res.append("</" + m_tags.pop() + ">");
	m_attributes.clear();
	return res;
}

TQColor KSParser::ircColor(const TQString &color)
{
	bool success;
	unsigned int intColor = color.toUInt(&success);

	if (success)
		return ircColor(intColor);
	else
		return TQColor();
}

TQColor KSParser::ircColor(unsigned int color)
{
	unsigned int maxcolor = sizeof(IRC_Colors)/sizeof(TQColor);
	return color<=maxcolor?IRC_Colors[color]:IRC_Colors[maxcolor];
}

int KSParser::colorForHTML(const TQString &html)
{
	TQColor color(html);
	for(uint i=0; i<sizeof(IRC_Colors)/sizeof(TQColor); i++)
	{
		if(IRC_Colors[i] == color)
			return i;
	}
	return -1;
}