// Written by James Turner, started October 2020
//
// Copyright (C) 2020 James Turner <james@flightgear.org>
//
// 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.

#include "LauncherNotificationsController.hxx"

#include <QAbstractListModel>
#include <QDebug>
#include <QQmlEngine>
#include <QSettings>

#include "SettingsWrapper.hxx"

static LauncherNotificationsController* static_instance = nullptr;

namespace {

const int IdRole = Qt::UserRole + 1;
const int SourceRole = Qt::UserRole + 2;
const int ArgsRole = Qt::UserRole + 3;

} // namespace

class LauncherNotificationsController::NotificationsModel : public QAbstractListModel
{
public:
    int rowCount(const QModelIndex&) const override
    {
        return static_cast<int>(_data.size());
    }

    QVariant data(const QModelIndex& index, int role) const override
    {
        const int row = index.row();
        if ((row < 0) || (row >= static_cast<int>(_data.size()))) {
            return {};
        }

        const auto& d = _data.at(row);
        switch (role) {
        case IdRole: return d.id;
        case SourceRole: return d.source;
        case ArgsRole: return QVariant::fromValue(d.args);
        default:
            break;
        }

        return {};
    }

    QHash<int, QByteArray> roleNames() const override
    {
        QHash<int, QByteArray> result = QAbstractListModel::roleNames();
        result[IdRole] = "id";
        result[SourceRole] = "source";
        result[ArgsRole] = "args";
        return result;
    }

    void removeIndex(int row)
    {
        // work aroud the role-by-role destruction order of model data
        // clear out the source first so the Loader unloads, before we
        // null args. This avoids 'args is null' warnings from the loaded
        // notification
        _data[row].source.clear();
        const auto idx = index(row, 0);
        emit dataChanged(idx, idx, {SourceRole});
        
        // now we can remove everything else
        beginRemoveRows({}, row, row);
        _data.erase(_data.begin() + row);
        endRemoveRows();
    }

    void append(QString id, QUrl source, QJSValue args)
    {
        const int newRow = static_cast<int>(_data.size());
        beginInsertRows({}, newRow, newRow);
        _data.push_back({id, source, args});
        endInsertRows();
    }

    struct Data {
        QString id;
        QUrl source;
        QJSValue args;
    };

    std::vector<Data> _data;
};

LauncherNotificationsController::LauncherNotificationsController(QObject* pr, QQmlEngine* engine) : QObject(pr)
{
    Q_ASSERT(static_instance == nullptr);
    static_instance = this;

    _model = new NotificationsModel;

    _qmlEngine = engine;
}

LauncherNotificationsController::~LauncherNotificationsController()
{
    static_instance = nullptr;
}

LauncherNotificationsController* LauncherNotificationsController::instance()
{
    return static_instance;
}

QAbstractItemModel* LauncherNotificationsController::notifications() const
{
    return _model;
}

QJSValue LauncherNotificationsController::argsForIndex(int index) const
{
    if ((index < 0) || (index >= static_cast<int>(_model->_data.size()))) {
        return {};
    }
    const auto& d = _model->_data.at(index);
    qDebug() << Q_FUNC_INFO << index;
    return d.args;
}

QJSEngine* LauncherNotificationsController::jsEngine()
{
    return _qmlEngine;
}

void LauncherNotificationsController::dismissIndex(int index)
{
    const auto& d = _model->_data.at(index);

    // if the notificsation supports persistent dismissal, then record this
    // fact in the global settings, so we don't show it again.
    // restore defaults will of course clear these settings, but that's
    // desirable anyway.
    if (d.args.property("persistent-dismiss").toBool()) {
        auto settings = flightgear::getQSettings();
        settings.beginGroup("dismissed-notifications");
        settings.setValue(d.id, true);
    }

    _model->removeIndex(index);
}

void LauncherNotificationsController::postNotification(QString id, QUrl source, QJSValue args)
{
    const bool supportsPersistentDismiss = args.property("persistent-dismiss").toBool();
    if (supportsPersistentDismiss) {
        auto settings = flightgear::getQSettings();
        settings.beginGroup("dismissed-notifications");
        bool alreadyDimissed = settings.value(id).toBool();
        if (alreadyDimissed) {
            return;
        }
    }

    _model->append(id, source, args);
}
