/*
 * Copyright (C) 2018 The UBports project
 * Copyright (C) 2014-2016 Canonical Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 3, as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 * MERCHANTABILITY, SATISFACTORY QUALITY, 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 "System.h"

#include <QDBusPendingCall>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDBusMetaType>
#include <QDir>
#include <QFile>
#include <QLocale>
#include <QMap>
#include <QProcess>
#include <QDebug>
#include <QSettings>
#include <QStringBuilder>

System::System()
    : QObject()
{
    // Register the argument needed for UpdateActivationEnvironment below
    qDBusRegisterMetaType<QMap<QString,QString>>();

    if(!wizardEnabled()) {
        m_fsWatcher.addPath(wizardEnabledPath());
    }
    connect(&m_fsWatcher, &QFileSystemWatcher::fileChanged, this, &System::watcherFileChanged);
}

QString System::wizardEnabledPath()
{
    return QDir::home().filePath(QStringLiteral(".config/lomiri/wizard-has-run"));
}

QString System::currentFrameworkPath()
{
    QFileInfo f("/usr/share/click/frameworks/current");
    return f.canonicalFilePath();
}

/*
wizardEnabled and isUpdate logic

if wizard-has-run does NOT exist == is new install
if wizard-has-run exists but does NOT match current framework == is update
if wizard-has-run exists but does match current framework == show no wizard
*/

bool System::wizardPathExists() {
  return QFile::exists(wizardEnabledPath());
}

bool System::wizardEnabled() const
{
    if (!wizardPathExists()) {
        return true;
    }
    return isUpdate();
}

QString System::readCurrentFramework()
{
    QFile f(currentFrameworkPath());
    if (!f.open(QFile::ReadOnly | QFile::Text)) return "";
    QTextStream in(&f);
    return in.readAll();
}

QString System::readWizardEnabled()
{
    QFile f(wizardEnabledPath());
    if (!f.open(QFile::ReadOnly | QFile::Text)) return "";
    QTextStream in(&f);
    return in.readAll();
}

QString System::version() const
{
    return readCurrentFramework();
}

bool System::isUpdate() const
{
    if (!wizardPathExists()) {
        return false;
    }

    return readCurrentFramework() != readWizardEnabled();
}

void System::setWizardEnabled(bool enabled)
{
    if (wizardEnabled() == enabled && !isUpdate())
        return;

    if (enabled) {
        QFile::remove(wizardEnabledPath());
    } else {
        QDir(wizardEnabledPath()).mkpath(QStringLiteral(".."));
        if (QFile::exists(wizardEnabledPath())) {
            QFile::remove(wizardEnabledPath());
        }
        // For special cases check if wizardEnabledPath is a folder
        if (QDir(wizardEnabledPath()).exists()) {
            QDir(wizardEnabledPath()).removeRecursively();
        }
        if (!QFile::copy(currentFrameworkPath(), wizardEnabledPath())) {
            // Make en empty file if framework does not exist
            QFile f(wizardEnabledPath());
            f.open(QFile::WriteOnly);
        }
        m_fsWatcher.addPath(wizardEnabledPath());
        Q_EMIT wizardEnabledChanged();
        Q_EMIT isUpdateChanged();
    }
}

void System::watcherFileChanged()
{
    Q_EMIT wizardEnabledChanged();
    Q_EMIT isUpdateChanged();
    m_fsWatcher.removePath(wizardEnabledPath());
}

void System::setSessionVariable(const QString &variable, const QString &value)
{
    // We need to update both systemd's and DBus's environment
    QStringList vars = { variable % QChar('=') % value };
    QDBusMessage systemdMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"),
                                                             QStringLiteral("/org/freedesktop/systemd1"),
                                                             QStringLiteral("org.freedesktop.systemd1.Manager"),
                                                             QStringLiteral("SetEnvironment"));
    systemdMsg << QVariant::fromValue(vars);
    QDBusConnection::sessionBus().asyncCall(systemdMsg);

    QMap<QString,QString> valueMap;
    valueMap.insert(variable, value);

    QDBusMessage dbusMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"),
                                                      QStringLiteral("/org/freedesktop/DBus"),
                                                      QStringLiteral("org.freedesktop.DBus"),
                                                      QStringLiteral("UpdateActivationEnvironment"));

    dbusMsg << QVariant::fromValue(valueMap);
    QDBusConnection::sessionBus().asyncCall(dbusMsg);
}

void System::updateSessionLocale(const QString &locale)
{
    const QString language = locale.split(QStringLiteral("."))[0];

    setSessionVariable(QStringLiteral("LANGUAGE"), language);
    setSessionVariable(QStringLiteral("LANG"), locale);
    setSessionVariable(QStringLiteral("LC_ALL"), locale);

    // QLocale caches the default locale on startup, and Qt uses that cached
    // copy when formatting dates.  So manually update it here.
    QLocale::setDefault(QLocale(locale));

    // Restart bits of the session to pick up new language.
    // FIXME not implemented
}

void System::skipUntilFinishedPage()
{
    QSettings settings;
    settings.setValue(QStringLiteral("Wizard/SkipUntilFinishedPage"), true);
    settings.sync();
}
