//=============================================================================
//
//   File : KviStringConversion.cpp
//   Creation date : Thu Oct 20 2000 14:12:21 CEST by Szymon Stefanek
//
//   This file is part of the KVIrc IRC client distribution
//   Copyright (C) 2000-2010 Szymon Stefanek (pragma at kvirc dot net)
//
//   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.
//
//=============================================================================

#define _KVI_STRINGCONVERSION_CPP_
#include "KviStringConversion.h"
#include "KviCString.h"
#include "KviMessageTypeSettings.h"
#include "KviPixmap.h"
#include "KviQString.h"

#include <QColor>
#include <QFont>
#include <QRect>
#include <QString>
#include <QStringList>
#include <cstdio>
#include <array>

QString g_szGlobalDir;
QString g_szLocalDir;

namespace KviStringConversion
{
	void init(const QString & szGlobalDir, const QString & szLocalDir)
	{
		g_szGlobalDir = szGlobalDir;
		g_szLocalDir = szLocalDir;
	}

	void encodePath(QString & szBuffer)
	{
		if(!szBuffer.isEmpty())
		{
			if(!g_szLocalDir.isEmpty())
			{
				if(szBuffer.indexOf(g_szLocalDir, 0, Qt::CaseSensitive) == 0)
				{
					szBuffer.remove(0, g_szLocalDir.length());
					szBuffer.prepend("local://");
				}
			}
			if(!g_szGlobalDir.isEmpty())
			{
				if(szBuffer.indexOf(g_szGlobalDir, 0, Qt::CaseSensitive) == 0)
				{
					szBuffer.remove(0, g_szGlobalDir.length());
					szBuffer.prepend("global://");
				}
			}
		}
	}

	void decodePath(QString & szBuffer)
	{
		if(!szBuffer.isEmpty())
		{
			if(!g_szLocalDir.isEmpty())
			{
				if(szBuffer.indexOf("local://", 0, Qt::CaseSensitive) == 0)
				{
					szBuffer.remove(0, 8);
					szBuffer.prepend(g_szLocalDir);
				}
			}
			if(!g_szGlobalDir.isEmpty())
			{
				if(szBuffer.indexOf("global://", 0, Qt::CaseSensitive) == 0)
				{
					szBuffer.remove(0, 9);
					szBuffer.prepend(g_szGlobalDir);
				}
			}
		}
	}

	void encodePath(QStringList & buffer)
	{
		for(auto & it : buffer)
			encodePath(it);
	}

	void decodePath(QStringList & buffer)
	{
		for(auto & it : buffer)
			decodePath(it);
	}

	void toString(const bool bValue, QString & szBuffer)
	{
		szBuffer = bValue ? '1' : '0';
	}

	bool fromString(const QString & szValue, bool & bBuffer)
	{
		if(szValue.isEmpty())
			bBuffer = false;
		else
			bBuffer = !((KviQString::equalCS(szValue, "0")) || (KviQString::equalCI(szValue, "false")));
		return true;
	}

	void toString(const int iValue, QString & szBuffer)
	{
		szBuffer.setNum(iValue);
	}

	bool fromString(const QString & szValue, int & iBuffer)
	{
		bool bOk;
		iBuffer = szValue.toInt(&bOk);
		return bOk;
	}

	void toString(const unsigned int uValue, QString & szBuffer)
	{
		szBuffer.setNum(uValue);
	}

	bool fromString(const QString & szValue, unsigned int & uBuffer)
	{
		bool bOk;
		uBuffer = szValue.toUInt(&bOk);
		return bOk;
	}

	void toString(const QRect & rValue, QString & szBuffer)
	{
		szBuffer = QString::asprintf("%d,%d,%d,%d", rValue.x(), rValue.y(), rValue.width(), rValue.height());
	}

	bool fromString(const QString & szValue, QRect & buffer)
	{
		QByteArray tmp = szValue.toUtf8();
		const char * c = tmp.data();
		if(!c)
			return false;
		int l, t, w, h;
		if(sscanf(c, "%d,%d,%d,%d", &l, &t, &w, &h) != 4)
			return false;
		buffer.setRect(l, t, w, h);
		return true;
	}

	void toString(const QString & szValue, QString & szBuffer)
	{
		szBuffer = szValue;
	}

	bool fromString(const QString & szValue, QString & szBuffer)
	{
		szBuffer = szValue;
		return true;
	}

	void toString(const KviPixmap & pixmap, QString & szBuffer)
	{
		szBuffer = pixmap.path();
		encodePath(szBuffer);
	}

	bool fromString(const QString & szValue, KviPixmap & buffer)
	{
		QString szPath(szValue);
		decodePath(szPath);
		if(szPath.isEmpty())
		{
			buffer.setNull();
			return true;
		}
		else
		{
			return buffer.load(szPath);
		}
	}

	void toString(const KviMessageTypeSettings & mValue, QString & szBuffer)
	{
		szBuffer = QString::asprintf("%d,%u,%u,%d,%d", mValue.m_iPixId, mValue.m_cForeColor, mValue.m_cBackColor, mValue.m_bLogEnabled, mValue.m_iLevel);
	}

	bool fromString(const QString & szValue, KviMessageTypeSettings & buffer)
	{
		int iId, iLog, iLevel;
		unsigned int uFore, uBack;
		QByteArray tmp = szValue.toUtf8();
		char * cx = tmp.data();
		if(!cx)
			return false;
		if(sscanf(cx, "%d,%u,%u,%d,%d", &iId, &uFore, &uBack, &iLog, &iLevel) != 5)
			return false;
		buffer = KviMessageTypeSettings(buffer.m_szType, iId, uFore, uBack, iLog, iLevel);
		return true;
	}

	void toString(const QColor & value, QString & szBuffer)
	{
		szBuffer = value.name();
	}

	bool fromString(const QString & szValue, QColor & buffer)
	{
#if (QT_VERSION < QT_VERSION_CHECK(6, 4, 0))
		buffer.setNamedColor(szValue);
#else
		buffer = QColor::fromString(szValue);
#endif
		return true;
	}

	void toString(const QFont & font, QString & szBuffer)
	{
		QString szFamily(font.family());
		QString szOptions;
		if(font.bold())
			szOptions.append('b');
		if(font.italic())
			szOptions.append('i');
		if(font.underline())
			szOptions.append('u');
		if(font.strikeOut())
			szOptions.append('s');
		if(font.fixedPitch())
			szOptions.append('f');

		szBuffer = QString::asprintf("%s,%d,%d,%d,%s,%s", szFamily.toUtf8().data(), font.pointSize(), font.styleHint(), font.weight(), szOptions.toUtf8().data(), font.styleName().toUtf8().data());
	}

	/* Helper function to convert Qt < 6.0 font weight to OpenType font weight */
	static int fromLegacyWeight(int weight)
	{
		static constexpr std::array<int, 2> weightMap[] = {
			{ 0, QFont::Thin },
			{ 12, QFont::ExtraLight },
			{ 25, QFont::Light },
			{ 50, QFont::Normal },
			{ 57, QFont::Medium },
			{ 63, QFont::DemiBold },
			{ 75, QFont::Bold },
			{ 81, QFont::ExtraBold },
			{ 87, QFont::Black },
		};

		int closestDist = INT_MAX;
		int result = -1;
		for (auto item: weightMap) {
			const int dist = qAbs(item[0] - weight);
			if (dist < closestDist) {
				result = item[1];
				closestDist = dist;
			} else {
				break;
			}
		}

		return result;
	}

	bool fromString(const QString & szValue, QFont & buffer)
	{
		KviCString str = szValue;
		KviCString family, pointSize, styleHint, weight, options;
		str.getToken(family, ',');
		str.getToken(pointSize, ',');
		str.getToken(styleHint, ',');
		str.getToken(weight, ',');
		str.getToken(options, ',');
		if(!family.isEmpty())
			buffer.setFamily(family.ptr());
		int i;
		bool bOk;
		i = pointSize.toInt(&bOk);
		if(bOk && (i > 0))
			buffer.setPointSize(i);
		i = styleHint.toInt(&bOk);
		if(bOk && (i >= 0))
			buffer.setStyleHint((QFont::StyleHint)i);
		i = weight.toInt(&bOk);
		if(bOk && (i >= 0)) {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
			buffer.setWeight(i);
#else
			/*
			 * KVIrc <= 5.2.2 used Qt5 font weights (0 = thinner, 99 = bolder)
			 * Qt6 introduced opentype (css) font weight (100 = thinner, 900 = bolder)
			 * if the config is using an old weight, convert it
			 */
			if(i < 100) {
				i = KviStringConversion::fromLegacyWeight(i);
			}
			buffer.setWeight(QFont::Weight(i));
#endif
		}
		buffer.setBold(options.contains("b"));
		buffer.setItalic(options.contains("i"));
		buffer.setUnderline(options.contains("u"));
		buffer.setStrikeOut(options.contains("s"));
		buffer.setFixedPitch(options.contains("f"));
		if(!str.isEmpty())
			buffer.setStyleName(str.ptr());
		return true;
	}

	void toString(const QStringList & list, QString & szBuffer)
	{
		szBuffer = list.join(",");
	}

	bool fromString(const QString & szValue, QStringList & buffer)
	{
		buffer = szValue.split(",");
		return true;
	}
}
