#include "gtk/gtk.h"
#include <stdbool.h>
#include "rotation.h"

struct sensor_cb_data {
        sensor_cb rotation_update;
};

static GDBusProxy *iio_proxy;
static bool rotation_valid = false;
static int rotation;
struct sensor_cb_data cb_data;

static int
rotation_string_to_degree(const gchar* val)
{
        int rot = -1;
        if (!g_ascii_strcasecmp(val, "normal"))
                rot = 0;
        if (!g_ascii_strcasecmp(val, "right-up"))
                rot = 270;
        if (!g_ascii_strcasecmp(val, "left-up"))
                rot = 90;
        if (!g_ascii_strcasecmp(val, "bottom-up"))
                rot = 180;
        return rot;
}

static void
rotation_properties_changed (GDBusProxy *proxy,
                             GVariant   *changed_properties,
                             GStrv       invalidated_properties,
                             gpointer    user_data)
{
        GVariant *v;
        GVariantDict dict;
        const gchar *val;
        struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;

        g_variant_dict_init (&dict, changed_properties);

        if (g_variant_dict_contains (&dict, "HasAccelerometer")) {
                v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAccelerometer");
                rotation_valid = g_variant_get_boolean (v);
                g_debug ("accelerometer changed: %d\n", rotation_valid);
                g_variant_unref (v);
        }
        if (g_variant_dict_contains (&dict, "AccelerometerOrientation")) {
                v = g_dbus_proxy_get_cached_property (iio_proxy, "AccelerometerOrientation");
                val = g_variant_get_string (v, NULL);
                rotation = rotation_string_to_degree(val);
                g_variant_unref (v);
        }
        g_variant_dict_clear (&dict);

        g_debug ("sensor rotation changed: %d %d\n",
                 rotation, rotation_valid);
        cb_data->rotation_update(rotation, rotation_valid);
}

static void
rotation_get_initial_values (struct sensor_cb_data *cb_data)
{
        GVariant *v;
        const gchar *val;

        v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAccelerometer");
        rotation_valid = g_variant_get_boolean (v);
        if (rotation_valid) {
                g_variant_unref (v);
                v = g_dbus_proxy_get_cached_property (iio_proxy, "AccelerometerOrientation");
                val = g_variant_get_string (v, NULL);
                rotation = rotation_string_to_degree(val);
                g_debug ("rotation: %d\n", rotation);
        } else
                g_debug ("no accelerometer\n");
        g_variant_unref (v);
        cb_data->rotation_update(rotation, rotation_valid);
}

static void
rotation_accelerometer_claimed(GDBusProxy   *proxy,
                               GAsyncResult *res,
                               gpointer      user_data)
{
        struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;

        g_autoptr(GError) error = NULL;
        g_autoptr(GVariant) result = g_dbus_proxy_call_finish(proxy, res, &error);

        if (!result) {
                if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
                        g_warning ("Failed to claim accelerometer: %s",
                                   error->message);
                return;
        }
        rotation_get_initial_values (cb_data);
}

static void
rotation_iio_proxy_init (GObject      *src,
                         GAsyncResult *res,
                         gpointer      user_data)
{
        struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;
        g_autoptr(GError) err = NULL;

        iio_proxy = g_dbus_proxy_new_finish(res, &err);
        if (!iio_proxy || err) {
                g_warning ("Failed to connect to sensor proxy service %s\n",
                           err->message);
                return;
        }

        g_signal_connect (G_OBJECT (iio_proxy), "g-properties-changed",
                          G_CALLBACK (rotation_properties_changed),
                          cb_data);

        g_dbus_proxy_call (iio_proxy,
                           "ClaimAccelerometer",
                           NULL,
                           G_DBUS_CALL_FLAGS_NONE,
                           -1,
                           NULL,
                           (GAsyncReadyCallback)rotation_accelerometer_claimed,
                           cb_data);
}

static void
rotation_iio_proxy_appeared_cb (GDBusConnection *connection,
                                const gchar     *name,
                                const gchar     *name_owner,
                                gpointer         user_data)
{
        g_debug ("iio-sensor-proxy appeared\n");
        g_dbus_proxy_new (connection,
                          G_DBUS_PROXY_FLAGS_NONE,
                          NULL,
                          "net.hadess.SensorProxy",
                          "/net/hadess/SensorProxy",
                          "net.hadess.SensorProxy",
                          NULL,
                          (GAsyncReadyCallback)rotation_iio_proxy_init,
                          user_data);
}

static void
rotation_iio_proxy_vanished_cb (GDBusConnection *connection,
                                const gchar     *name,
                                gpointer         user_data)
{
        struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;

        if (iio_proxy) {
                g_clear_object (&iio_proxy);
                g_debug ("iio-sensor-proxy vanished\n");

                rotation_valid = false;
                rotation = 0;
                cb_data->rotation_update(rotation, rotation_valid);
        }
}

void
mp_device_rotation_init(sensor_cb cb)
{
        g_assert(cb != NULL);
        cb_data.rotation_update = cb;
        g_bus_watch_name (G_BUS_TYPE_SYSTEM,
                          "net.hadess.SensorProxy",
                          G_BUS_NAME_WATCHER_FLAGS_NONE,
                          rotation_iio_proxy_appeared_cb,
                          rotation_iio_proxy_vanished_cb,
                          &cb_data,
                          NULL);
}

void
mp_device_rotation_clean(void)
{
        g_object_unref(iio_proxy);
}
