/*
 * Copyright (C) 2014-2024 CZ.NIC
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <cstddef> /* NULL */
#include <QLocale>
#include <QMap>

#include "src/datovka_shared/compat_qt/misc.h" /* _Qt_SkipEmptyParts */
#include "src/datovka_shared/localisation/countries.h"
#include "src/datovka_shared/log/log.h"

#define CODE_LEN 2

/* TODO -- Check whether the codes correspond to ISO 3166. */

struct Localisation::countryEntry Localisation::countryList[] = {
    {"CZ", "Česká republika"},
    {"AF", "Afghánistán"},
    {"AL", "Albánie"},
    {"DZ", "Alžírsko"},
    {"VI", "Americké Panenské ostrovy"},
    {"AD", "Andorra"},
    {"AO", "Angola"},
    {"AI", "Anguilla"},
    {"AG", "Antigua a Barbuda"},
    {"AR", "Argentina"},
    {"AM", "Arménie"},
    {"AW", "Aruba"},
    {"AU", "Austrálie"},
    {"AZ", "Ázerbájdžán"},
    {"BS", "Bahamy"},
    {"BH", "Bahrajn"},
    {"BD", "Bangladéš"},
    {"BB", "Barbados"},
    {"BE", "Belgie"},
    {"BZ", "Belize"},
    {"BY", "Bělorusko"},
    {"BJ", "Benin"},
    {"BM", "Bermudy"},
    {"BT", "Bhútán"},
    {"BO", "Bolívie"},
    {"BA", "Bosna a Hercegovina"},
    {"BW", "Botswana"},
    {"BR", "Brazílie"},
    {"IO", "Britské indickooceánské území"},
    {"VG", "Britské Panenské ostrovy"},
    {"BN", "Brunej"},
    {"BG", "Bulharsko"},
    {"BF", "Burkina Faso"},
    {"BI", "Burundi"},
    {"CK", "Cookovy ostrovy – Nový Zéland"},
    {"TD", "Čad"},
    {"ME", "Černá Hora"},
    {"CN", "Čína"},
    {"DK", "Dánsko"},
    {"FO", "Dánsko – Faerské ostrovy"},
    {"GL", "Dánsko – Grónsko"},
    {"DM", "Dominika"},
    {"DO", "Dominikánská rep."},
    {"DJ", "Džibutsko"},
    {"EG", "Egypt"},
    {"EC", "Ekvádor"},
    {"ER", "Eritrea"},
    {"EE", "Estonsko"},
    {"ET", "Etiopie"},
    {"FK", "Falklandy"},
    {"FJ", "Fidži"},
    {"PH", "Filipíny"},
    {"FI", "Finsko"},
    {"FR", "Francie"},
    {"GF", "Francouzská Guyana"},
    {"TF", "Francouzská jižní území"},
    {"PF", "Francouzská Polynésie"},
    {"GA", "Gabon"},
    {"GM", "Gambie"},
    {"GH", "Ghana"},
    {"GI", "Gibraltar"},
    {"GD", "Grenada"},
    {"GE", "Gruzie"},
    {"GP", "Guadeloupe"},
    {"GT", "Guatemala"},
    {"GN", "Guinea"},
    {"GW", "Guinea – Bissau"},
    {"GY", "Guyana"},
    {"HT", "Haiti"},
    {"HN", "Honduras"},
    {"HK", "Hongkong"},
    {"CL", "Chile"},
    {"HR", "Chorvatsko"},
    {"IN", "Indie"},
    {"ID", "Indonésie"},
    {"IQ", "Irák"},
    {"IR", "Írán"},
    {"IE", "Irsko (kromě Severního Irska)"},
    {"IS", "Island"},
    {"IT", "Itálie"},
    {"IL", "Izrael"},
    {"JM", "Jamajka"},
    {"JP", "Japonsko"},
    {"YE", "Jemen"},
    {"ZA", "Jižní Afrika"},
    {"JO", "Jordánsko"},
    {"KY", "Kajmanské ostrovy"},
    {"KH", "Kambodža"},
    {"CM", "Kamerun"},
    {"CA", "Kanada"},
    {"CV", "Kapverdy"},
    {"QA", "Katar"},
    {"KZ", "Kazachstán"},
    {"KE", "Keňa"},
    {"KI", "Kiribati"},
    {"CO", "Kolumbie"},
    {"KM", "Komory"},
    {"CG", "Kongo"},
    {"CD", "Konžská dem. republika"},
    {"KP", "Korejská lid. dem. rep."},
    {"KR", "Korejská republika"},
    {"XK", "Kosovo"},
    {"CR", "Kostarika"},
    {"CU", "Kuba"},
    {"KW", "Kuvajt"},
    {"CY", "Kypr"},
    {"KG", "Kyrgyzstán"},
    {"LA", "Laos"},
    {"LS", "Lesotho"},
    {"LB", "Libanon"},
    {"LR", "Libérie"},
    {"LY", "Libye"},
    {"LI", "Lichtenštejnsko"},
    {"LT", "Litva"},
    {"LV", "Lotyšsko"},
    {"LU", "Lucembursko"},
    {"MO", "Macao"},
    {"MG", "Madagaskar"},
    {"HU", "Maďarsko"},
    {"MK", "Makedonie"},
    {"MY", "Malajsie"},
    {"MW", "Malawi"},
    {"MV", "Maledivy"},
    {"ML", "Mali"},
    {"MT", "Malta"},
    {"MA", "Maroko"},
    {"MH", "Marshallovy ostrovy"},
    {"MQ", "Martinik"},
    {"MU", "Mauricius"},
    {"MR", "Mauritánie"},
    {"MX", "Mexiko"},
    {"FM", "Mikronésie"},
    {"MD", "Moldavsko"},
    {"MC", "Monako"},
    {"MN", "Mongolsko"},
    {"MS", "Montserrat"},
    {"MZ", "Mosambik"},
    {"MM", "Myanmar"},
    {"NA", "Namibie"},
    {"NR", "Nauru"},
    {"DE", "Německo"},
    {"NP", "Nepál"},
    {"NE", "Niger"},
    {"NG", "Nigérie"},
    {"NI", "Nikaragua"},
    {"NU", "Niue"},
    {"AN", "Nizozemské Antily"},
    {"NA", "Nizozemské Antily"},
    {"NL", "Nizozemsko"},
    {"NO", "Norsko"},
    {"NC", "Nová Kaledonie"},
    {"NU", "Nový Zéland – Niue"},
    {"TK", "Nový Zéland – Tokelau"},
    {"NZ", "Nový Zéland (bez Tokelau, Cookových ostrovů a Niue)"},
    {"OM", "Omán"},
    {"PK", "Pákistán"},
    {"PW", "Palau"},
    {"PS", "Palestina"},
    {"PA", "Panama"},
    {"PG", "Papua – Nová Guinea"},
    {"PY", "Paraguay"},
    {"PE", "Peru"},
    {"PN", "Pitcairnovy ostrovy"},
    {"CI", "Pobřeží slonoviny (Côte ď Ivoire)"},
    {"PL", "Polsko"},
    {"PR", "Portoriko"},
    {"PT", "Portugalsko"},
    {"AT", "Rakousko"},
    {"RE", "Réunion"},
    {"GQ", "Rovníková Guinea"},
    {"RO", "Rumunsko"},
    {"RU", "Rusko"},
    {"RW", "Rwanda"},
    {"GR", "Řecko"},
    {"SH", "S. Helena"},
    {"KN", "S. Kitts a Nevis"},
    {"LC", "S. Lucie"},
    {"SM", "S. Marino"},
    {"PM", "S. Pierre a Miquelon"},
    {"ST", "S. Tomé a Principe"},
    {"VC", "S. Vincenc a Grenadiny"},
    {"SV", "Salvador"},
    {"WS", "Samoa"},
    {"SA", "Saudská Arábie"},
    {"SN", "Senegal"},
    {"SC", "Seychely"},
    {"SL", "Sierra Leone"},
    {"SG", "Singapur"},
    {"SK", "Slovenská republika"},
    {"SI", "Slovinsko"},
    {"SO", "Somálsko"},
    {"AE", "Spojené arabské emiráty"},
    {"US", "Spojené státy americké"},
    {"RS", "Srbsko"},
    {"LK", "Srí Lanka"},
    {"CF", "Středoafrická republika"},
    {"SD", "Súdán"},
    {"SR", "Surinam"},
    {"SZ", "Svazijsko"},
    {"SY", "Sýrie"},
    {"SB", "Šalomounovy ostrovy"},
    {"ES", "Španělsko"},
    {"SE", "Švédsko"},
    {"CH", "Švýcarsko"},
    {"TJ", "Tádžikistán"},
    {"TW", "Taiwan"},
    {"TZ", "Tanzanie"},
    {"TH", "Thajsko"},
    {"TG", "Togo"},
    {"TO", "Tonga"},
    {"TT", "Trinidad a Tobago"},
    {"TA", "Tristan da Cunha"},
    {"TN", "Tunisko"},
    {"TR", "Turecko"},
    {"TM", "Turkmenistán"},
    {"TC", "Turks a Caicos"},
    {"TV", "Tuvalu"},
    {"UG", "Uganda"},
    {"UA", "Ukrajina"},
    {"UY", "Uruguay"},
    {"UZ", "Uzbekistán"},
    {"VU", "Vanuatu"},
    {"VA", "Vatikán"},
    {"GG", "Velká Británie – Guernsey"},
    {"JE", "Velká Británie – Jersey"},
    {"IM", "Velká Británie - o. Man"},
    {"GB", "Velká Británie a Severní Irsko (kromě Guernsey, Jersey a ostrova Man)"},
    {"VE", "Venezuela"},
    {"VN", "Vietnam"},
    {"TL", "Východní Timor"},
    {"WF", "Wallis a Futuna"},
    {"ZM", "Zambie"},
    {"ZW", "Zimbabwe"},
    {NULL, NULL}
};

int Localisation::countryListSize(const struct countryEntry *countryList)
{
	if (Q_UNLIKELY(countryList == Q_NULLPTR)) {
		return 0;
	}

	int size = 0;
	while (countryList->code != NULL) {
		++countryList;
	}

	return size;
}

int Localisation::posOfCountryCode(const struct countryEntry *countryList,
    const QString &code)
{
	if (Q_UNLIKELY(countryList == Q_NULLPTR)) {
		Q_ASSERT(0);
		return -1;
	}

	if (code.size() != CODE_LEN) {
		return -1;
	}

	const QString codeUpper = code.toUpper();

	int pos = 0;
	while (countryList->code != NULL) {
		if (codeUpper == countryList->code) {
			return pos;
		}

		++pos; ++countryList;
	}

	/* Not found. */
	return -1;
}

QString Localisation::countryCodeAt(const struct countryEntry *countryList,
    int pos)
{
	if (Q_UNLIKELY((countryList == Q_NULLPTR) || (pos < 0))) {
		Q_ASSERT(0);
		return QString();
	}

	return QString::fromUtf8(countryList[pos].code);
}

QString Localisation::countryNameCzAt(const struct countryEntry *countryList,
    int pos)
{
	if (Q_UNLIKELY((countryList == Q_NULLPTR) || (pos < 0))) {
		Q_ASSERT(0);
		return QString();
	}

	return QString::fromUtf8(countryList[pos].name_cz);
}

QList< QPair<QString, int> > Localisation::countryNamesCz(
    const struct countryEntry *countryList)
{
	if (Q_UNLIKELY(countryList == Q_NULLPTR)) {
		Q_ASSERT(0);
		return QList< QPair<QString, int> >();
	}

	QList< QPair<QString, int> > countryNameList;
	int pos = 0; /* Position in countryList. */
	while (countryList->code != NULL) {
		QString str = QString("%1 - %2")
		    .arg(QString::fromUtf8(countryList->code))
		    .arg(QString::fromUtf8(countryList->name_cz));
		countryNameList.append(QPair<QString, int>(str, pos));

		++pos; ++countryList;
	}

	return countryNameList;
}

/*!
 * @brief Obtain country code from supplied locale.
 *
 * @param[in] locale Locale entry.
 * @return Two-letter upper-case country code, null string on error.
 */
static
QString countryCode(const QLocale &locale)
{
	QList<QString> list = locale.name().split(QChar('_'), _Qt_SkipEmptyParts);
	if (list.size() < 2) {
		logWarningNL("Cannot obtain country code from locale '%s'.",
		    locale.name().toUtf8().constData());
		return QString();
	}

	return list.at(1).toUpper();
}

QList< QPair<QString, int> > Localisation::countryNamesEn(
    const struct countryEntry *countryList)
{
	if (Q_UNLIKELY(countryList == Q_NULLPTR)) {
		Q_ASSERT(0);
		return QList< QPair<QString, int> >();
	}

	/* All available locale entries. */
	QMap<QString, QString> countryMap; /* Maps code onto name. */
	{
		const QList<QLocale> allLocales = QLocale::matchingLocales(
		    QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
		foreach (const QLocale &locale, allLocales) {
			const QString code = countryCode(locale);
			if (!code.isEmpty()) {
				if (code.size() != CODE_LEN) {
					logWarningNL(
					    "Unexpected country code '%s' in '%s'.",
					    code.toUtf8().constData(),
					    locale.name().toUtf8().constData());
				}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0))
				countryMap[code] =
				    QLocale::territoryToString(locale.territory());
#else /* < Qt-6.2 */
				countryMap[code] =
				    QLocale::countryToString(locale.country());
#endif /* >= Qt-6.2 */
			}
		}
	}

	QList< QPair<QString, int> > countryNameList;
	int pos = 0; /* Position in countryList. */
	while (countryList->code != NULL) {
		QString str = QString("%1 - %2")
		    .arg(QString::fromUtf8(countryList->code))
		    .arg(countryMap.value(countryList->code, QString()));
		countryNameList.append(QPair<QString, int>(str, pos));

		++pos; ++countryList;
	}

	return countryNameList;
}
