#include <Tactility/app/wificonnect/WifiConnect.h>

#include <Tactility/app/AppContext.h>
#include <Tactility/service/loader/Loader.h>
#include <Tactility/service/wifi/Wifi.h>

#include <lvgl/lvgl.h>

namespace tt::app::wificonnect {

constexpr auto* TAG = "WifiConnect";
constexpr auto* WIFI_CONNECT_PARAM_SSID = "ssid"; // String
constexpr auto* WIFI_CONNECT_PARAM_PASSWORD = "password"; // String

extern const AppManifest manifest;

static void onConnect(const service::wifi::settings::WifiApSettings& ap_settings, bool remember, void* parameter) {
    auto* wifi = static_cast<WifiConnect*>(parameter);
    wifi->getState().setApSettings(ap_settings);
    wifi->getState().setConnecting(true);
    service::wifi::connect(ap_settings, remember);
}

void WifiConnect::onWifiEvent(service::wifi::WifiEvent event) {
    State& state = getState();
    if (event.type == WIFI_EVENT_TYPE_STATION_CONNECTION_RESULT) {
        if (event.connection_error == WIFI_STATION_CONNECTION_ERROR_NONE) {
            if (state.isConnecting()) {
                state.setConnecting(false);
                stop(manifest.appId);
            }
        } else {
            if (state.isConnecting()) {
                state.setConnecting(false);
                state.setConnectionError(true);
                requestViewUpdate();
            }
        }
    }
    requestViewUpdate();
}

WifiConnect::WifiConnect() {
    wifiSubscription = service::wifi::getPubsub()->subscribe([this](auto event)  {
        onWifiEvent(event);
    });

    bindings = (Bindings) {
        .onConnectSsid = onConnect,
        .onConnectSsidContext = this,
    };
}

WifiConnect::~WifiConnect() {
    service::wifi::getPubsub()->unsubscribe(wifiSubscription);
}

void WifiConnect::lock() {
    mutex.lock();
}

void WifiConnect::unlock() {
    mutex.unlock();
}

void WifiConnect::requestViewUpdate() {
    lock();
    if (viewEnabled) {
        lvgl_lock();
        view.update();
        lvgl_unlock();
    }
    unlock();
}

void WifiConnect::onShow(AppContext& app, lv_obj_t* parent) {
    lock();
    viewEnabled = true;
    view.init(app, parent);
    view.update();
    unlock();
}

void WifiConnect::onHide(AppContext& app) {
    // No need to lock view, as this is called from within Gui's LVGL context
    lock();
    viewEnabled = false;
    unlock();
}

extern const AppManifest manifest = {
    .appId = "WifiConnect",
    .appName = "Wi-Fi Connect",
    .appIcon = LV_SYMBOL_WIFI,
    .appCategory = Category::System,
    .appFlags = AppManifest::Flags::Hidden,
    .createApp = create<WifiConnect>
};

LaunchId start(const std::string& ssid, const std::string& password) {
    auto parameters = std::make_shared<Bundle>();
    parameters->putString(WIFI_CONNECT_PARAM_SSID, ssid);
    parameters->putString(WIFI_CONNECT_PARAM_PASSWORD, password);
    return app::start(manifest.appId, parameters);
}

bool optSsidParameter(const std::shared_ptr<const Bundle>& bundle, std::string& ssid) {
    return bundle->optString(WIFI_CONNECT_PARAM_SSID, ssid);
}

bool optPasswordParameter(const std::shared_ptr<const Bundle>& bundle, std::string& password) {
    return bundle->optString(WIFI_CONNECT_PARAM_PASSWORD, password);
}

} // namespace
