/* This file is part of the KDE project
   Copyright (C) 2003-2007 Jaroslaw Staniek <js@iidea.pl>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library 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
   Library General Public License for more details.

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

#include "utils.h"
#include "utils_p.h"

#include <tqregexp.h>
#include <tqpainter.h>
#include <tqimage.h>
#include <tqwmatrix.h>
#include <tqiconset.h>
#include <tqbitmap.h>
#include <tqfile.h>

#include <kdebug.h>
#include <kcursor.h>
#include <tdeapplication.h>
#include <kpixmap.h>
#include <kiconeffect.h>
#include <kpixmapeffect.h>
#include <kiconloader.h>

#if defined(TQ_WS_WIN)
# include <win32_utils.h>
#endif

using namespace KexiUtils;

DelayedCursorHandler::DelayedCursorHandler() 
 : startedOrActive(false)
{
	connect(&timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(show()));
}
void DelayedCursorHandler::start(bool noDelay) {
	startedOrActive = true;
	timer.start(noDelay ? 0 : 1000, true);
}
void DelayedCursorHandler::stop() {
	startedOrActive = false;
	timer.stop();
	TQApplication::restoreOverrideCursor();
}
void DelayedCursorHandler::show() {
	TQApplication::setOverrideCursor( KCursor::waitCursor(), true/*replace*/ );
}

DelayedCursorHandler _delayedCursorHandler;

void KexiUtils::setWaitCursor(bool noDelay) {
	if (kapp->guiEnabled())
		_delayedCursorHandler.start(noDelay);
}
void KexiUtils::removeWaitCursor() {
	if (kapp->guiEnabled())
		_delayedCursorHandler.stop();
}

WaitCursor::WaitCursor(bool noDelay)
{
	setWaitCursor(noDelay);
}

WaitCursor::~WaitCursor()
{
	removeWaitCursor();
}

WaitCursorRemover::WaitCursorRemover()
{
	m_reactivateCursor = _delayedCursorHandler.startedOrActive;
	_delayedCursorHandler.stop();
}

WaitCursorRemover::~WaitCursorRemover()
{
	if (m_reactivateCursor)
		_delayedCursorHandler.start(true);
}

//--------------------------------------------------------------------------------

TQString KexiUtils::fileDialogFilterString(const KMimeType::Ptr& mime, bool kdeFormat)
{
	if (mime==0)
		return TQString();

	TQString str;
	if (kdeFormat) {
		if (mime->patterns().isEmpty())
			str = "*";
		else
			str = mime->patterns().join(" ");
		str += "|";
	}
	str += mime->comment();
	if (!mime->patterns().isEmpty() || !kdeFormat) {
		str += " (";
		if (mime->patterns().isEmpty())
			str += "*";
		else
			str += mime->patterns().join("; ");
		str += ")";
	}
	if (kdeFormat)
		str += "\n";
	else
		str += ";;";
	return str;
}

TQString KexiUtils::fileDialogFilterString(const TQString& mimeString, bool kdeFormat)
{
	KMimeType::Ptr ptr = KMimeType::mimeType(mimeString);
	return fileDialogFilterString( ptr, kdeFormat );
}

TQString KexiUtils::fileDialogFilterStrings(const TQStringList& mimeStrings, bool kdeFormat)
{
	TQString ret;
	TQStringList::ConstIterator endIt = mimeStrings.constEnd();
	for(TQStringList::ConstIterator it = mimeStrings.constBegin(); it != endIt; ++it)
		ret += fileDialogFilterString(*it, kdeFormat);
	return ret;
}

TQColor KexiUtils::blendedColors(const TQColor& c1, const TQColor& c2, int factor1, int factor2)
{
	return TQColor(
		int( (c1.red()*factor1+c2.red()*factor2)/(factor1+factor2) ),
		int( (c1.green()*factor1+c2.green()*factor2)/(factor1+factor2) ),
		int( (c1.blue()*factor1+c2.blue()*factor2)/(factor1+factor2) ) );
}

TQColor KexiUtils::contrastColor(const TQColor& c)
{
	int g = tqGray( c.rgb() );
	if (g>110)
		return c.dark(200);
	else if (g>80)
		return c.light(150);
	else if (g>20)
		return c.light(300);
	return TQt::gray;
}

TQColor KexiUtils::bleachedColor(const TQColor& c, int factor)
{
	int h, s, v;
	c.getHsv( &h, &s, &v );
	TQColor c2;
	if (factor < 100)
		factor = 100;
	if (s>=250 && v>=250) //for colors like cyan or red, make the result more white
		s = TQMAX(0, s - factor - 50);
	else if (s<=5 && s<=5)
		v += factor-50;
	c2.setHsv(h, s, TQMIN(255,v + factor-100));
	return c2;
}

TQIconSet KexiUtils::colorizeIconToTextColor(const TQPixmap& icon, const TQPalette& palette)
{
	TQPixmap pm(
		TDEIconEffect().apply( icon, TDEIconEffect::Colorize, 1.0f, palette.active().buttonText(), false ) );

	KPixmap kpm(pm);
	return TQIconSet(
		KPixmapEffect::fade( kpm, 0.33, palette.active().button() ) );
}

TQPixmap KexiUtils::emptyIcon(TDEIcon::Group iconGroup)
{
	TQPixmap noIcon( IconSize( iconGroup ), IconSize( iconGroup ) );
	TQBitmap bmpNoIcon(noIcon.size());
	bmpNoIcon.fill(TQt::color0);
	noIcon.setMask(bmpNoIcon);
	return noIcon;
}

void KexiUtils::serializeMap(const TQMap<TQString,TQString>& map, const TQByteArray& array)
{
	TQDataStream ds(array, IO_WriteOnly);
	ds << map;
}

void KexiUtils::serializeMap(const TQMap<TQString,TQString>& map, TQString& string)
{
	TQByteArray array;
	TQDataStream ds(array, IO_WriteOnly);
	ds << map;
	kdDebug() << array[3] << " " << array[4] << " " << array[5] << endl;
	const uint size = array.size();
	string = TQString();
	string.reserve(size);
	for (uint i=0; i<size; i++) {
		string[i]=TQChar(ushort(array[i]+1));
	}
}

TQMap<TQString,TQString> KexiUtils::deserializeMap(const TQByteArray& array)
{
	TQMap<TQString,TQString> map;
	TQDataStream ds(array, IO_ReadOnly);
	ds >> map;
	return map;
}

TQMap<TQString,TQString> KexiUtils::deserializeMap(const TQString& string)
{
	const uint size = string.length();
	TQCString cstr(string.latin1());
	TQByteArray array( size );
	for (uint i=0; i<size; i++) {
		array[i] = char(string[i].unicode()-1);
	}
	TQMap<TQString,TQString> map;
	TQDataStream ds(array, IO_ReadOnly);
	ds >> map;
	return map;
}

TQString KexiUtils::stringToFileName(const TQString& string)
{
	TQString _string(string);
	_string.replace(TQRegExp("[\\\\/:\\*?\"<>|]"), " ");
	return _string.simplifyWhiteSpace();
}

void KexiUtils::simpleCrypt(TQString& string)
{
	for (uint i=0; i<string.length(); i++)
		string[i] = TQChar( string[i].unicode() + 47 + i );
}

void KexiUtils::simpleDecrypt(TQString& string)
{
	for (uint i=0; i<string.length(); i++)
		string[i] = TQChar( string[i].unicode() - 47 - i );
}

void KexiUtils::drawPixmap( TQPainter& p, int lineWidth, const TQRect& rect, 
	const TQPixmap& pixmap, int alignment, bool scaledContents, bool keepAspectRatio)
{
	if (pixmap.isNull())
		return;

	const bool fast = pixmap.width()>1000 && pixmap.height()>800; //fast drawing needed
	const int w = rect.width()-lineWidth-lineWidth;
	const int h = rect.height()-lineWidth-lineWidth;
//! @todo we can optimize drawing by drawing rescaled pixmap here 
//! and performing detailed painting later (using TQTimer)
	TQPixmap pixmapBuffer;
	TQPainter p2;
	TQPainter *target;
	if (fast) {
		target = &p;
	}
	else {
//moved		pixmapBuffer.resize(rect.size()-TQSize(lineWidth, lineWidth));
//moved		p2.begin(&pm, p.device());
		target = &p2;
	}
//! @todo only create buffered pixmap of the minimum size and then do not fillRect()
//	target->fillRect(0,0,rect.width(),rect.height(), backgroundColor);

	TQPoint pos;
	if (scaledContents) {
		if (keepAspectRatio) {
			TQImage img(pixmap.convertToImage());
			img = img.smoothScale(w, h, TQ_ScaleMin);
			pos = rect.topLeft(); //0, 0);
			if (img.width() < w) {
				int hAlign = TQApplication::horizontalAlignment( alignment );
				if ( hAlign & TQt::AlignRight )
					pos.setX(pos.x() + w-img.width());
				else if ( hAlign & TQt::AlignHCenter )
					pos.setX(pos.x() + w/2-img.width()/2);
			}
			else if (img.height() < h) {
				if ( alignment & TQt::AlignBottom )
					pos.setY(pos.y() + h-img.height());
				else if ( alignment & TQt::AlignVCenter )
					pos.setY(pos.y() + h/2-img.height()/2);
			}
			pixmapBuffer.convertFromImage(img);
			if (!fast) {
				p2.begin(TQT_TQPAINTDEVICE(&pixmapBuffer), p.device());
			}
			else
				target->drawPixmap(pos, pixmapBuffer);
		}
		else {
			if (!fast) {
				pixmapBuffer.resize(rect.size()-TQSize(lineWidth, lineWidth));
				p2.begin(TQT_TQPAINTDEVICE(&pixmapBuffer), p.device());
				p2.drawPixmap(TQRect(rect.x(), rect.y(), w, h), pixmap);
			}
			else
				target->drawPixmap(TQRect(rect.x() + lineWidth, rect.y() + lineWidth, w, h), pixmap);
		}
	}
	else {
		int hAlign = TQApplication::horizontalAlignment( alignment );
		if ( hAlign & TQt::AlignRight )
			pos.setX(pos.x() + w-pixmap.width());
		else if ( hAlign & TQt::AlignHCenter )
			pos.setX(pos.x() + w/2-pixmap.width()/2);
		else //left, etc.
			pos.setX(pos.x());

		if ( alignment & TQt::AlignBottom )
			pos.setY(pos.y() + h-pixmap.height());
		else if ( alignment & TQt::AlignVCenter )
			pos.setY(pos.y() + h/2-pixmap.height()/2);
		else //top, etc. 
			pos.setY(pos.y());
//		target->drawPixmap(pos, pixmap);
//		if (!fast)
//			p2.begin(&pixmapBuffer, p.device());
		p.drawPixmap(lineWidth+pos.x(), lineWidth+pos.y(), pixmap);
	}
	if (scaledContents && !fast && p.isActive()) {
		p2.end();
		bitBlt( p.device(), 
//			pos.x(), 
//			pos.y(), 
			(int)p.worldMatrix().dx() + rect.x() + lineWidth + pos.x(), 
			(int)p.worldMatrix().dy() + rect.y() + lineWidth + pos.y(), 
			&pixmapBuffer);
	}
}

TQString KexiUtils::ptrToStringInternal(void* ptr, uint size)
{
	TQString str;
	unsigned char* cstr_ptr = (unsigned char*)&ptr;
	for (uint i=0; i<size; i++) {
		TQString s;
		s.sprintf("%2.2x", cstr_ptr[i]);
		str.append( s );
	}
	return str;
}

void* KexiUtils::stringToPtrInternal(const TQString& str, uint size)
{
	TQByteArray array(size);
	if ((str.length()/2)<size)
		return 0;
	bool ok;
	for (uint i=0; i<size; i++) {
		array[i]=(unsigned char)(str.mid(i*2, 2).toUInt(&ok, 16));
		if (!ok)
			return 0;
	}
	return *(void**)(array.data());
}

void KexiUtils::setFocusWithReason(TQWidget* widget, TQFocusEvent::Reason reason)
{
	TQEvent fe( TQEvent::FocusIn );
	TQT_TQFOCUSEVENT(&fe)->setReason(reason);
	TQApplication::sendEvent( widget, &fe );
	TQT_TQFOCUSEVENT(&fe)->resetReason();
}

void KexiUtils::unsetFocusWithReason(TQWidget* widget, TQFocusEvent::Reason reason)
{
	TQEvent fe( TQEvent::FocusOut );
	TQT_TQFOCUSEVENT(&fe)->setReason(reason);
	TQApplication::sendEvent( widget, &fe );
	TQT_TQFOCUSEVENT(&fe)->resetReason();
}

CopyFileResult KexiUtils::copyFile(const TQString& src, const TQString& dest)
{
#ifdef TQ_WS_WIN
	int res = fcopy( TQFile::encodeName( src ), TQFile::encodeName( dest ) );
	if (res == fcopy_src_err)
		return CopyReadError;
	else if (res == fcopy_dest_err)
		return CopyWriteError;
	
	return CopySuccess;
#else
# define _fcopy_BUFLEN 1024*32
	char _fcopy_buf[_fcopy_BUFLEN];
	FILE *in, *out;
	int c_in=0, c_out=0;
	CopyFileResult res=CopySuccess;
	
	in=fopen(TQFile::encodeName( src ), "rb");
	if (!in)
		return CopyReadError;
	out=fopen(TQFile::encodeName( dest ), "wb");
	if (!out)
		return CopyWriteError;
	while (!feof(in) && !ferror(in) && !ferror(out)) {
		c_in=fread(_fcopy_buf, 1, _fcopy_BUFLEN, in);
		if (ferror(in) || c_in==0)
			break;
		c_out=fwrite(_fcopy_buf, 1, c_in, out);
		if (ferror(out) || c_in!=c_out)
			break;
	}
	
	if (ferror(in))
		res=CopyReadError;
	else if (ferror(out))
		res=CopyWriteError;
	else if (c_in!=c_out)
		res=CopyWriteError;
	fclose(in);
	fclose(out);
	return res;
#endif
}

#include "utils_p.moc"
