/*
 * Copyright 2020-2022 UBports foundation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; version 3.
 *
 * 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Marius Gripsgard <marius@ubports.com>
 */

#include "dbusAdaptor.h"
#include "vibrator.h"
#include "leds.h"
#include "utils.h"

#include "hfdadaptor.h"

#include <deviceinfo.h>

#include <climits>
#include <memory>
#include <iostream>

#include <QCoreApplication>
#include <QDBusError>

class DbusAdaptorService : public DbusAdaptor, protected QDBusContext {
    Q_OBJECT
public:
    DbusAdaptorService(std::shared_ptr<hfd::Vibrator> vibrator, std::shared_ptr<hfd::Leds> leds)
        : DbusAdaptor()
        , m_vibrator(vibrator)
        , m_leds(leds)
    {}

    void setVibrateDurationExtraMs(uint vibrateDurationExtraMs) {
        std::cout << "Setting vibrateDurationExtraMs as " << vibrateDurationExtraMs << std::endl;
        m_vibrateDurationExtraMs = vibrateDurationExtraMs;
    };

public Q_SLOTS:
    void vibrate() override { m_vibrator->vibrate(); };
    void vibrate(int durationMs) override {
        if (!verifyDBusInt("durationMs", durationMs, 1, 1000 * 60))
            return;
        m_vibrator->vibrate(durationMs + m_vibrateDurationExtraMs);
    };
    void rumble(int durationMs, int repeat) override {
        if (!verifyDBusInt("durationMs", durationMs, 1, 1000))
            return;
        if (!verifyDBusInt("repeat", repeat, 1, 60))
            return;
        m_vibrator->rumble(durationMs + m_vibrateDurationExtraMs, repeat);
    };

    void setState(int state) override { m_leds->setState(hfd::utils::toState(state)); };
    void setColor(unsigned int color) override { m_leds->setColor(color); }
    void setOnMs(int ms) override {
        if (!verifyDBusInt("onMs", ms))
            return;
        m_leds->setOnMs(ms);
    };
    void setOffMs(int ms) override {
        if (!verifyDBusInt("offMs", ms, 0))
            return;
        m_leds->setOffMs(ms);
    };

protected:

    bool verifyDBusInt(const std::string &name, int val, int min = 1, int max = INT_MAX) {
        if (val >= min && val <= max)
            return true;

        this->sendErrorReply(QDBusError::InvalidArgs, QString::fromStdString(std::string(name) + ": parameter out of range"));
        return false;
    }

private:
    std::shared_ptr<hfd::Vibrator> m_vibrator;
    std::shared_ptr<hfd::Leds> m_leds;
    uint m_vibrateDurationExtraMs;
};

#include "service.moc"


int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    // Environment variables to switch between implementations
    const char* vibrator_impl_env = getenv("HFD_VIBRATOR_IMPL");
    const char* leds_impl_env = getenv("HFD_LEDS_IMPL");

    std::cout << "Starting vibrator impl" << std::endl;
    std::shared_ptr<hfd::Vibrator> vibrator;
    if (vibrator_impl_env)
        vibrator = hfd::Vibrator::create(vibrator_impl_env);
    else
        vibrator = hfd::Vibrator::create();

    std::cout << "Starting leds impl" << std::endl;
    std::shared_ptr<hfd::Leds> leds;
    if (leds_impl_env)
        leds = hfd::Leds::create(leds_impl_env);
    else
        leds = hfd::Leds::create();

    std::cout << "done" << std::endl;
    auto dbusAdaptor = new DbusAdaptorService(vibrator, leds);
    std::unique_ptr<DeviceInfo> devInfo = std::make_unique<DeviceInfo>();
    dbusAdaptor->setVibrateDurationExtraMs(static_cast<unsigned int>(std::stoul(devInfo->get("VibrateDurationExtraMs", "0"))));

    new VibratorAdaptor(dbusAdaptor);
    new LedsAdaptor(dbusAdaptor);
    QDBusConnection connection = QDBusConnection::systemBus();
    connection.registerObject("/com/lomiri/hfd", dbusAdaptor);
    connection.registerService("com.lomiri.hfd");

    if (connection.lastError().isValid()) {
        std::cout << "Not connected to DBus!!!" << std::endl;
        qWarning() << connection.lastError();
        return 1;
    }

    std::cout << "Started dbus conn" << std::endl;

    return app.exec();

}
