/****************************************************************************
 ** ScStw Libraries
 ** Copyright (C) 2020  Itsblue development
 **
 ** 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 "../headers/scstwsettings.h"

ScStwSettings::ScStwSettings(QObject *parent) : QObject(parent)
{
    QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QDir dir(path);
    this->settingsFile = new QFile(path + "/settings.json");
    if(!this->settingsFile->exists())
        if(!dir.mkpath(path))
            qFatal("[FATAL] Couldn't create settings dir %s", qPrintable(path));

    if(!this->settingsFile->open(QFile::ReadWrite))
        qFatal("[FATAL] Couldn't open settings file %s", qPrintable(path + "/settings.json"));

    if(!this->loadSettingsFromFile() && this->settingsFile->size() != 0)
        qFatal("[FATAL] Settings file (%s) is of invalid format!", qPrintable(path + "/settings.json"));

    connect(this, &ScStwSettings::settingChanged, this, &ScStwSettings::writeSettingsToFile);

    this->registerKeyLevelConverters(ScStwSettings::KeyLevel, &ScStwSettings::keyToString, &ScStwSettings::keyToType);

    this->setDefaultSetting(ScStwSettings::BaseStationSetting::ReadySoundEnableSetting, false);
    this->setDefaultSetting(ScStwSettings::BaseStationSetting::ReadySoundDelaySetting, 0);
    this->setDefaultSetting(ScStwSettings::BaseStationSetting::AtYourMarksSoundEnableSetting, false);
    this->setDefaultSetting(ScStwSettings::BaseStationSetting::AtYourMarksSoundDelaySetting, 0);
    this->setDefaultSetting(ScStwSettings::SoundVolumeSetting, 1);
    this->setDefaultSetting(ScStwSettings::CompetitionModeSetting, false);
}

QVariant ScStwSettings::readSetting(BaseStationSetting key) {
    return this->readSetting(key, 0);
}

QVariant ScStwSettings::readSetting(int key, int keyLevel) {
    if(this->keyToStringConverters.contains(keyLevel))
        return this->readSetting(this->keyToStringConverters[keyLevel](key), key, keyLevel);
    else
        return QVariant();
}

bool ScStwSettings::writeSetting(BaseStationSetting key, QVariant value) {
    return this->writeSetting(key, 0, value);
}

bool ScStwSettings::writeSetting(int key, int keyLevel, QVariant value) {
    if(this->keyToStringConverters.contains(keyLevel))
        return this->writeSetting(this->keyToStringConverters[keyLevel](key), value, key, keyLevel);
    else
        return false;
}

bool ScStwSettings::setDefaultSetting(BaseStationSetting key, QVariant defaultValue) {
    return this->setDefaultSetting(key, 0, defaultValue);
}

bool ScStwSettings::setDefaultSetting(int key, int keyLevel, QVariant defaultValue) {
    if(this->keyToStringConverters.contains(keyLevel))
        return this->setDefaultSetting(this->keyToStringConverters[keyLevel](key), defaultValue, key, keyLevel);
    else
        return false;
}

QVariant ScStwSettings::readSetting(QString key, int keyInt, int keyLevel) {
    Q_UNUSED(keyInt)
    Q_UNUSED(keyLevel)

    if(this->settingsCache.contains(key))
        return this->settingsCache[key];
    else
        return QVariant();
}

bool ScStwSettings::writeSetting(QString key, QVariant value, int keyInt, int keyLevel) {

    // check if the value type is valid
    if(!this->keyToTypeConverters.contains(keyLevel) || !value.convert(this->keyToTypeConverters[keyLevel](keyInt)) || value.type() == QVariant::Invalid) {
        return false;
    }

    // write the setting
    if(!this->settingsCache.contains(key))
        this->settingsCache.insert(key, value);
    else if (this->settingsCache[key] == value)
        return true;
    else
        this->settingsCache[key] = value;

    emit this->settingChanged(keyInt, keyLevel, value);

    return true;
}

bool ScStwSettings::setDefaultSetting(QString key, QVariant defaultValue, int keyInt, int keyLevel) {
    if(!this->settingsCache.contains(key))
        return this->writeSetting(key, defaultValue, keyInt, keyLevel);
    return true;
}

ScStwSetting * ScStwSettings::getSetting(int key, int keyLevel) {
    if(!this->internalSettingHandlers.contains(keyLevel))
        this->internalSettingHandlers.insert(keyLevel, {});

    if(!this->internalSettingHandlers[keyLevel].contains(key))
        this->internalSettingHandlers[keyLevel].insert(key, new ScStwSetting(key, keyLevel, this, this));

    return this->internalSettingHandlers[keyLevel][key];
}

bool ScStwSettings::registerKeyLevelConverters(int keyLevel, keyToStringConverter keyToStringConverterFunction, keyToTypeConverter keyToTypeConverterFunction) {
    if(this->keyToStringConverters.contains(keyLevel))
        return false;

    this->keyToStringConverters.insert(keyLevel, keyToStringConverterFunction);
    this->keyToTypeConverters.insert(keyLevel, keyToTypeConverterFunction);
    return true;
}

/*
 * File handling
 */

bool ScStwSettings::writeSettingsToFile() {
    QJsonDocument doc = QJsonDocument::fromVariant(this->settingsCache);

    // overwrite file
    this->settingsFile->reset();
    this->settingsFile->write(doc.toJson(QJsonDocument::Indented));
    this->settingsFile->flush();

    return true;
}

bool ScStwSettings::loadSettingsFromFile() {
    QJsonDocument doc = QJsonDocument::fromJson(this->settingsFile->readAll());

    if(doc.toVariant().type() != QVariant::Map)
        return false;

    this->settingsCache = doc.toVariant().toMap();
    return true;
}
