/****************************************
 *
 *   INSERT-PROJECT-NAME-HERE - INSERT-GENERIC-NAME-HERE
 *   Copyright (C) 2020 Victor Tran
 *
 *   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/>.
 *
 * *************************************/
#include "localemanager.h"

#include "private/keyboardlayoutselector.h"
#include "private/localeselector.h"
#include <QDir>
#include <QMap>
#include <QTranslator>
#include <tapplication.h>
#include <tpopover.h>
#include <tpromise.h>
#include <tsettings.h>

struct LocaleManagerPrivate {
        QMap<int, QTranslator*> translators;
        QMap<int, QStringList> searchPaths;

        tSettings settings;
        QStringList preferredLocales;
        QString formats;

        int current = 0;
};

LocaleManager::LocaleManager(QObject* parent) :
    QObject(parent) {
    d = new LocaleManagerPrivate();

    d->preferredLocales = d->settings.delimitedList("Locale/locales");
    if (d->preferredLocales.count() == 1 && d->preferredLocales.first() == "") d->preferredLocales = QStringList({"C"});
    d->formats = d->settings.value("Locale/formats").toString();

    this->updateLocales();
    this->addTranslationSet({QDir::cleanPath(qApp->applicationDirPath() + "/../libthedesk/translations"),
        "/usr/share/thedesk/libthedesk/translations"});

    connect(&d->settings, &tSettings::settingChanged, this, [=](QString key, QVariant value) {
        if (key == "Locale/locales") {
            d->preferredLocales = d->settings.delimitedList("Locale/locales");
            if (d->preferredLocales.count() == 1 && d->preferredLocales.first() == "") d->preferredLocales = QStringList({"C"});
            this->updateLocales();
            emit localesChanged();
        } else if (key == "Locale/formats") {
            d->formats = value.toString();
            this->updateLocales();
            emit formatCountryChanged();
        }
    });
}

LocaleManager::~LocaleManager() {
    delete d;
}

int LocaleManager::addTranslationSet(QStringList searchPaths) {
    int id = d->current;
    d->current++;

    QTranslator* translator = new QTranslator(this);
    qApp->installTranslator(translator);

    d->translators.insert(id, translator);
    d->searchPaths.insert(id, searchPaths);

    updateTranslator(id);

    return id;
}

void LocaleManager::removeTranslationSet(int translationSet) {
    if (!d->translators.contains(translationSet)) return;

    QTranslator* translator = d->translators.value(translationSet);
    qApp->removeTranslator(translator);
    translator->deleteLater();

    d->translators.remove(translationSet);
    d->searchPaths.remove(translationSet);
}

QLocale LocaleManager::showLocaleSelector(QWidget* parent, bool* ok) {
    if (ok) *ok = true;

    tPromiseResults<QLocale> results = TPROMISE_CREATE_SAME_THREAD(QLocale, {
        bool* stillValid = new bool(true);

        LocaleSelector* selector = new LocaleSelector();
        tPopover* popover = new tPopover(selector);
        popover->setPopoverWidth(SC_DPI(500));
        connect(selector, &LocaleSelector::rejected, this, [=] {
            rej("");
            *stillValid = false;
            popover->dismiss();
        });
        connect(selector, &LocaleSelector::accepted, this, [=](QLocale locale) {
            res(locale);
            *stillValid = false;
            popover->dismiss();
        });
        connect(popover, &tPopover::dismissed, this, [=] {
            if (*stillValid) rej("");
            popover->deleteLater();
            selector->deleteLater();
            delete stillValid;
        });
        popover->show(parent);
    })->await();

    if (!results.error.isEmpty() && ok) *ok = false;
    return results.result;
}

void LocaleManager::addLocale(QLocale locale) {
    if (!d->preferredLocales.contains(locale.bcp47Name())) {
        d->preferredLocales.append(locale.bcp47Name());
        if (d->preferredLocales.contains("C")) d->preferredLocales.removeAll("C");
        d->settings.setDelimitedList("Locale/locales", d->preferredLocales);
    }
}

void LocaleManager::prependLocale(QLocale locale) {
    if (!d->preferredLocales.contains(locale.bcp47Name())) {
        d->preferredLocales.prepend(locale.bcp47Name());
        if (d->preferredLocales.contains("C")) d->preferredLocales.removeAll("C");
        d->settings.setDelimitedList("Locale/locales", d->preferredLocales);
    }
}

void LocaleManager::removeLocale(QLocale locale) {
    d->preferredLocales.removeAll(locale.bcp47Name());
    d->settings.setDelimitedList("Locale/locales", d->preferredLocales);
}

void LocaleManager::moveLocaleUp(QLocale locale) {
    int index = d->preferredLocales.indexOf(locale.bcp47Name());
    if (index != 0) {
        d->preferredLocales.removeAt(index);
        d->preferredLocales.insert(index - 1, locale.bcp47Name());
        d->settings.setDelimitedList("Locale/locales", d->preferredLocales);
    }
}

void LocaleManager::moveLocaleDown(QLocale locale) {
    int index = d->preferredLocales.indexOf(locale.bcp47Name());
    if (index != d->preferredLocales.count() - 1) {
        d->preferredLocales.removeAt(index);
        d->preferredLocales.insert(index + 1, locale.bcp47Name());
        d->settings.setDelimitedList("Locale/locales", d->preferredLocales);
    }
}

QList<QLocale> LocaleManager::locales() {
    QList<QLocale> locales;
    for (QString locale : d->preferredLocales) {
        locales.append(QLocale(locale));
    }
    return locales;
}

QLocale::Country LocaleManager::formatCountry() {
    return QLocale(d->formats).country();
}

void LocaleManager::setFormatCountry(QLocale::Country country) {
    // Choose the first locale
    QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, country);
    if (locales.isEmpty()) locales.append(QLocale());
    d->settings.setValue("Locale/formats", locales.first().name());
}

QString LocaleManager::showKeyboardLayoutSelector(QWidget* parent, bool* ok) {
    if (ok) *ok = true;

    tPromiseResults<QString> results = TPROMISE_CREATE_SAME_THREAD(QString, {
        bool* stillValid = new bool(true);

        KeyboardLayoutSelector* selector = new KeyboardLayoutSelector();
        tPopover* popover = new tPopover(selector);
        popover->setPopoverWidth(SC_DPI(500));
        connect(selector, &KeyboardLayoutSelector::rejected, this, [=] {
            rej("");
            *stillValid = false;
            popover->dismiss();
        });
        connect(selector, &KeyboardLayoutSelector::accepted, this, [=](QString locale) {
            res(locale);
            *stillValid = false;
            popover->dismiss();
        });
        connect(popover, &tPopover::dismissed, this, [=] {
            if (*stillValid) rej("");
            popover->deleteLater();
            selector->deleteLater();
            delete stillValid;
        });
        popover->show(parent);
    })->await();

    if (!results.error.isEmpty() && ok) *ok = false;
    return results.result;
}

QLocale LocaleManager::formatLocale() {
    QLocale locale(d->preferredLocales.first());
    QLocale formatsTryLocale(d->formats);
    QLocale formatsLocale(locale.language(), formatsTryLocale.country());
    if (formatsLocale.country() != formatsTryLocale.country()) formatsLocale = formatsTryLocale;

    return formatsLocale;
}

void LocaleManager::updateTranslator(int id) {
    QTranslator* translator = d->translators.value(id);
    QStringList searchPaths = d->searchPaths.value(id);
    for (QString path : searchPaths) {
        if (translator->load(QLocale(), "", "", path, ".qm")) return; // Success!
    }
}

void LocaleManager::updateLocales() {
    if (d->preferredLocales.isEmpty()) d->preferredLocales.append("C");
    QLocale locale(d->preferredLocales.first());
    QLocale::setDefault(locale);

    QLocale formatsLocale = this->formatLocale();

    QByteArray mainLocale = glibName(locale).toUtf8();
    QByteArray formatsLocaleStr = glibName(formatsLocale).toUtf8();
    qputenv("LANG", mainLocale);
    qputenv("LC_CTYPE", mainLocale);
    qputenv("LC_COLLATE", mainLocale);
    qputenv("LC_TIME", formatsLocaleStr);
    qputenv("LC_NUMERIC", formatsLocaleStr);
    qputenv("LC_MONETARY", formatsLocaleStr);
    qputenv("LC_MESSAGES", mainLocale);
    qputenv("LC_PAPER", formatsLocaleStr);
    qputenv("LC_MEASUREMENT", formatsLocaleStr);
    qputenv("LC_NAME", formatsLocaleStr);
    qputenv("LC_ADDRESS", formatsLocaleStr);
    qputenv("LC_TELEPHONE", formatsLocaleStr);
    //    qputenv("LC_ALL", glibName(locale).toUtf8());

    QStringList languages;
    for (QString locale : d->preferredLocales) {
        languages.append(glibName(QLocale(locale)));
    }
    qputenv("LANGUAGE", languages.join(":").toUtf8());

    // Update all the translators
    for (int id : d->translators.keys()) {
        updateTranslator(id);
    }

    static_cast<tApplication*>(tApplication::instance())->installTranslators();
}

QString LocaleManager::glibName(QLocale locale, QString encoding) {
    return locale.name() + "." + encoding;
}
