/***************************************************************************
 *   Copyright (C) 2003 by S�astien Laot                                 *
 *   slaout@linux62.org                                                    *
 *                                                                         *
 *   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 option) 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 <kdebug.h>
#include <tqstring.h>
#include <tqpixmap.h>
#include <tqimage.h>
#include <tqstylesheet.h>
#include <tqregexp.h>
#include <tqvaluestack.h>
#include <tqfileinfo.h>
#include <tqdir.h>
#include <tqmime.h>
#include <tqfont.h>
#include <tqfontinfo.h>
#include <tqobjectlist.h>

#include "tools.h"

TQMemArray<TQTime>  StopWatch::starts;
TQMemArray<double> StopWatch::totals;
TQMemArray<uint>   StopWatch::counts;

void StopWatch::start(uint id)
{
	if (id >= starts.size()) {
		totals.resize(id + 1);
		counts.resize(id + 1);
		for (uint i = starts.size(); i <= id; i++) {
			totals[i] = 0;
			counts[i] = 0;
		}
		starts.resize(id + 1);
	}
	starts[id] = TQTime::currentTime();
}

void StopWatch::check(uint id)
{
	if (id >= starts.size())
		return;
	double time = starts[id].msecsTo(TQTime::currentTime()) / 1000.0;
	totals[id] += time;
	counts[id]++;
	kdDebug() << k_funcinfo << "Timer_" << id << ": " << time << " s    [" << counts[id] << " times, total: " << totals[id] << " s, average: " << totals[id] / counts[id] << " s]" <<  endl;
}

TQString Tools::textToHTML(const TQString &text)
{
	if (text.isEmpty())
		return "<p></p>";
	if (/*text.isEmpty() ||*/ text == " " || text == "&nbsp;")
		return "<p>&nbsp;</p>";

	// convertFromPlainText() replace "\n\n" by "</p>\n<p>": we don't want that
	TQString htmlString = TQStyleSheet::convertFromPlainText(text, TQStyleSheetItem::WhiteSpaceNormal);
	return htmlString.replace("</p>\n", "<br>\n<br>\n").replace("\n<p>", "\n"); // Don't replace first and last tags
}

TQString Tools::textToHTMLWithoutP(const TQString &text)
{
	// textToHTML(text) return "<p>HTMLizedText</p>". We remove the strating "<p>" and ending </p>"
	TQString HTMLizedText = textToHTML(text);
	return HTMLizedText.mid(3, HTMLizedText.length() - 3 - 4);
}

TQString Tools::htmlToParagraph(const TQString &html)
{
	TQString result = html;
	bool startedBySpan = false;

	// Remove the <html> start tag, all the <head> and the <body> start
	// Because <body> can contain style="..." parameter, we transform it to <span>
	int pos = result.find("<body");
	if (pos != -1) {
		result = "<span" + result.mid(pos + 5);
		startedBySpan = true;
	}

	// Remove the ending "</p>\n</body></html>", each tag can be separated by space characters (%s)
	// "</p>" can be omitted (eg. if the HTML doesn't contain paragraph but tables), as well as "</body>" (optinal)
	pos = result.find(TQRegExp("(?:(?:</p>[\\s\\n\\r\\t]*)*</body>[\\s\\n\\r\\t]*)*</html>", false)); // Case unsensitive
	if (pos != -1)
		result = result.left(pos);

	if (startedBySpan)
		result += "</span>";

	return result.replace("</p>", "<br><br>").replace("<p>", "");
}

// The following is adapted from KStringHanlder::tagURLs
// The adaptation lies in the change to urlEx
// Thanks to Richard Heck
TQString Tools::tagURLs(const TQString &text)
{
	TQRegExp urlEx("(www\\.(?!\\.)|([a-zA-z]+)://)[\\d\\w\\./,:_~\\?=&;#@\\-\\+\\%\\$]+[\\d\\w/]");

	TQString richText(text);
	int urlPos = 0;
	int urlLen;
	while ((urlPos = urlEx.search(richText, urlPos)) >= 0) {
		urlLen = urlEx.matchedLength();
		TQString href = richText.mid(urlPos, urlLen);
		// TQt doesn't support (?<=pattern) so we do it here
		if ((urlPos > 0) && richText[urlPos-1].isLetterOrNumber()) {
			urlPos++;
			continue;
		}
		TQString anchor = "<a href=\"" + href + "\">" + href + "</a>";
		richText.replace(urlPos, urlLen, anchor);
		urlPos += anchor.length();
	}
	return richText;
}

TQString Tools::htmlToText(const TQString &html)
{
	TQString text = htmlToParagraph(html);
	text.remove("\n");
	text.replace("</h1>", "\n");
	text.replace("</h2>", "\n");
	text.replace("</h3>", "\n");
	text.replace("</h4>", "\n");
	text.replace("</h5>", "\n");
	text.replace("</h6>", "\n");
	text.replace("</li>", "\n");
	text.replace("</dt>", "\n");
	text.replace("</dd>", "\n");
	text.replace("<dd>",  "   ");
	text.replace("</div>","\n");
	text.replace("</blockquote>","\n");
	text.replace("</caption>","\n");
	text.replace("</tr>", "\n");
	text.replace("</th>", "  ");
	text.replace("</td>", "  ");
	text.replace("<br>",  "\n");
	text.replace("<br />","\n");
	// FIXME: Format <table> tags better, if possible
	// TODO: Replace &eacute; and co. by theire equivalent!

	// To manage tags:
	int pos = 0;
	int pos2;
	TQString tag, tag3;
	// To manage lists:
	int deep = 0;            // The deep of the current line in imbriqued lists
	TQValueStack<bool> ul;    // true if current list is a <ul> one, false if it's an <ol> one
	TQValueStack<int>  lines; // The line number if it is an <ol> list
	// We're removing every other tags, or replace them in the case of li:
	while ( (pos = text.find("<"), pos) != -1 ) {
		// What is the current tag?
		tag  = text.mid(pos + 1, 2);
		tag3 = text.mid(pos + 1, 3);
		// Lists work:
		if (tag == "ul") {
			deep++;
			ul.push(true);
			lines.push(-1);
		} else if (tag == "ol") {
			deep++;
			ul.push(false);
			lines.push(0);
		} else if (tag3 == "/ul" || tag3 == "/ol") {
			deep--;
			ul.pop();
			lines.pop();
		}
		// Where the tag closes?
		pos2 = text.find(">");
		if (pos2 != -1) {
			// Remove the tag:
			text.remove(pos, pos2 - pos + 1);
			// And replace li with "* ", "x. "... without forbidding to indent that:
			if (tag == "li") {
				// How many spaces before the line (indentation):
				TQString spaces = "";
				for (int i = 1; i < deep; i++)
					spaces += "  ";
				// The bullet or number of the line:
				TQString bullet = "* ";
				if (ul.top() == false) {
					lines.push(lines.pop() + 1);
					bullet = TQString::number(lines.top()) + ". ";
				}
				// Insertion:
				text.insert(pos, spaces + bullet);
			}
			if ( (tag3 == "/ul" || tag3 == "/ol") && deep == 0 )
				text.insert(pos, "\n"); // Empty line before and after a set of lists
		}
		++pos;
	}

	text.replace("&gt;",   ">");
	text.replace("&lt;",   "<");
	text.replace("&quot;", "\"");
	text.replace("&nbsp;", " ");
	text.replace("&amp;",  "&"); // CONVERT IN LAST!!

	return text;
}

TQString Tools::cssFontDefinition(const TQFont &font, bool onlyFontFamily)
{
	// The font definition:
	TQString definition = TQString(font.italic() ? "italic " : "") +
	                     TQString(font.bold()   ? "bold "   : "") +
	                     TQString::number(TQFontInfo(font).pixelSize()) + "px ";

	// Then, try to match the font name with a standard CSS font family:
	TQString genericFont = "";
	if (definition.contains("serif", false) || definition.contains("roman", false))
		genericFont = "serif";
	// No "else if" because "sans serif" must be counted as "sans". So, the order between "serif" and "sans" is important
	if (definition.contains("sans", false) || definition.contains("arial", false) || definition.contains("helvetica", false))
		genericFont = "sans-serif";
	if (definition.contains("mono",       false) || definition.contains("courier", false) ||
	    definition.contains("typewriter", false) || definition.contains("console", false) ||
	    definition.contains("terminal",   false) || definition.contains("news",    false))
		genericFont = "monospace";

	// Eventually add the generic font family to the definition:
	TQString fontDefinition = "\"" + font.family() + "\"";
	if (!genericFont.isEmpty())
		fontDefinition += ", " + genericFont;

	if (onlyFontFamily)
		return fontDefinition;

	return definition + fontDefinition;
}

TQString Tools::stripEndWhiteSpaces(const TQString &string)
{
	uint length = string.length();
	uint i;
	for (i = length; i > 0; --i)
		if (!string[i-1].isSpace())
			break;
	if (i == 0)
		return "";
	else
		return string.left(i);
}



bool Tools::isWebColor(const TQColor &color)
{
	int r = color.red();   // The 216 web colors are those colors whose RGB (Red, Green, Blue)
	int g = color.green(); //  values are all in the set (0, 51, 102, 153, 204, 255).
	int b = color.blue();

	return ( ( r ==   0 || r ==  51 || r == 102 ||
			   r == 153 || r == 204 || r == 255    ) &&
			 ( g ==   0 || g ==  51 || g == 102 ||
			   g == 153 || g == 204 || g == 255    ) &&
			 ( b ==   0 || b ==  51 || b == 102 ||
			   b == 153 || b == 204 || b == 255    )    );
}

TQColor Tools::mixColor(const TQColor &color1, const TQColor &color2)
{
	TQColor mixedColor;
	mixedColor.setRgb( (color1.red()   + color2.red())   / 2,
	                   (color1.green() + color2.green()) / 2,
	                   (color1.blue()  + color2.blue())  / 2 );
	return mixedColor;
}

bool Tools::tooDark(const TQColor &color)
{
	int dontCare, value;
	color.getHsv(/*hue:*/&dontCare, /*saturation:*/&dontCare, &value);
	return value < 175;
}


// TODO: Use it for all indentPixmap()
TQPixmap Tools::normalizePixmap(const TQPixmap &pixmap, int width, int height)
{
	if (height <= 0)
		height = width;

	if (pixmap.isNull() || (pixmap.width() == width && pixmap.height() == height))
		return pixmap;

	return pixmap;
}

TQPixmap Tools::indentPixmap(const TQPixmap &source, int depth, int deltaX)
{
	// Verify if it is possible:
	if (depth <= 0 || source.isNull())
		return source;

	// Compute the number of pixels to indent:
	if (deltaX <= 0)
		deltaX = 2 * source.width() / 3;
	int indent = depth * deltaX;

	// Create the images:
	TQImage resultImage(indent + source.width(), source.height(), 32);
	TQImage sourceImage = source.convertToImage();
	resultImage.setAlphaBuffer(true);

	// Clear the indent part (the left part) by making it fully transparent:
	uint *p;
	for (int row = 0; row < resultImage.height(); ++row) {
		for (int column = 0; column < resultImage.width(); ++column) {
			p = (uint *)resultImage.scanLine(row) + column;
			*p = 0; // tqRgba(0, 0, 0, 0)
		}
	}

	// Copy the source image byte per byte to the right part:
	uint *q;
	for (int row = 0; row < sourceImage.height(); ++row) {
		for (int column = 0; column < sourceImage.width(); ++column) {
			p = (uint *)resultImage.scanLine(row) + indent + column;
			q = (uint *)sourceImage.scanLine(row) + column;
			*p = *q;
		}
	}

	// And return the result:
	TQPixmap result;
	result.convertFromImage(resultImage);
	return result;
}

#include <iostream>

void Tools::deleteRecursively(const TQString &folderOrFile)
{
	if (folderOrFile.isEmpty())
		return;

	TQFileInfo fileInfo(folderOrFile);
	if (fileInfo.isDir()) {
		// Delete the child files:
		TQDir dir(folderOrFile, TQString(), TQDir::Name | TQDir::IgnoreCase, TQDir::All | TQDir::Hidden);
		TQStringList list = dir.entryList();
		for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it )
			if ( *it != "." && *it != ".." )
				deleteRecursively(folderOrFile + "/" + *it);
		// And then delete the folder:
		dir.rmdir(folderOrFile);
	} else
		// Delete the file:
		TQFile::remove(folderOrFile);
}

TQString Tools::fileNameForNewFile(const TQString &wantedName, const TQString &destFolder)
{
	TQString fileName  = wantedName;
	TQString fullName  = destFolder + fileName;
	TQString extension = "";
	int     number    = 2;
	TQDir    dir;

	// First check if the file do not exists yet (simplier and more often case)
	dir = TQDir(fullName);
	if ( ! dir.exists(fullName) )
		return fileName;

	// Find the file extension, if it exists : Split fileName in fileName and extension
	// Example : fileName == "note5-3.txt" => fileName = "note5-3" and extension = ".txt"
	int extIndex = fileName.findRev('.');
	if (extIndex != -1 && extIndex != int(fileName.length()-1))  { // Extension found and fileName do not ends with '.' !
		extension = fileName.mid(extIndex);
		fileName.truncate(extIndex);
	} // else fileName = fileName and extension = ""

	// Find the file number, if it exists : Split fileName in fileName and number
	// Example : fileName == "note5-3" => fileName = "note5" and number = 3
	int extNumber = fileName.findRev('-');
	if (extNumber != -1 && extNumber != int(fileName.length()-1))  { // Number found and fileName do not ends with '-' !
		bool isANumber;
		int  theNumber = fileName.mid(extNumber + 1).toInt(&isANumber);
		if (isANumber) {
			number = theNumber;
			fileName.truncate(extNumber);
		} // else :
	} // else fileName = fileName and number = 2 (because if the file already exists, the genereated name is at last the 2nd)

	TQString finalName;
	for (/*int number = 2*/; ; ++number) { // TODO: FIXME: If overflow ???
		finalName = fileName + "-" + TQString::number(number) + extension;
		fullName = destFolder + finalName;
		dir = TQDir(fullName);
		if ( ! dir.exists(fullName) )
			break;
	}

	return finalName;
}


// TODO: Move it from NoteFactory
/*TQString NoteFactory::iconForURL(const KURL &url)
{
	TQString icon = KMimeType::iconForURL(url.url());
	if ( url.protocol() == "mailto" )
		icon = "message";
	return icon;
}*/

bool Tools::isAFileCut(TQMimeSource *source)
{
	if (source->provides("application/x-tde-cutselection")) {
		TQByteArray array = source->encodedData("application/x-tde-cutselection");
		return !array.isEmpty() && TQCString(array.data(), array.size() + 1).at(0) == '1';
	} else
		return false;
}

void Tools::printChildren(TQObject* parent)
{
	const TQObjectList objs = parent->childrenListObject();
	TQObjectListIt it(objs);
	TQObject *obj;

	while((obj = it.current())!= 0){
		++it;
		kdDebug() << k_funcinfo << obj->className() << ": " << obj->name() << endl;
	}
}
