#include <glib.h>
#include <stdlib.h>
#include <gio/gio.h>
#include <cloudprovidersproviderexporter.h>
#include <cloudprovidersaccountexporter.h>
/* for CLoudProviderStatus enum */
#include <cloudprovidersaccount.h>


#define TIMEOUT 800
#define COUNT_PLACEHOLDER_ACCOUNTS 3
#define TEST_CLOUD_PROVIDERS_BUS_NAME "org.freedesktop.CloudProviders.ServerExample"
#define TEST_CLOUD_PROVIDERS_OBJECT_PATH "/org/freedesktop/CloudProviders/ServerExample"

#define CLOUD_PROVIDERS_TYPE_TEST_SERVER (cloud_providers_test_server_get_type())
G_DECLARE_FINAL_TYPE (CloudProvidersTestServer, cloud_providers_test_server, CLOUD_PROVIDERS, TEST_SERVER, GObject);

struct _CloudProvidersTestServerClass
{
  GObjectClass parent_class;
};

struct _CloudProvidersTestServer
{
  GObject parent_instance;

  GHashTable *accounts;
  gchar *name;
  GIcon *icon;
  gchar *path;
  guint timeout_handler;
  GDBusConnection *connection;
  CloudProvidersProviderExporter *exporter;
};

G_DEFINE_TYPE (CloudProvidersTestServer, cloud_providers_test_server, G_TYPE_OBJECT);

static CloudProvidersTestServer*
cloud_providers_test_server_new (void)
{
  CloudProvidersTestServer *self;

  self = g_object_new (CLOUD_PROVIDERS_TYPE_TEST_SERVER, NULL);

  return self;
}

static void
test_cloud_provider_finalize (GObject *object)
{
  CloudProvidersTestServer *self = CLOUD_PROVIDERS_TEST_SERVER (object);

  g_hash_table_unref (self->accounts);
  g_free (self->name);
  g_free (self->path);
  g_clear_object (&self->icon);

  G_OBJECT_CLASS (cloud_providers_test_server_parent_class)->finalize (object);
}

static void
cloud_providers_test_server_init (CloudProvidersTestServer *self)
{
  GFile *icon_file;
  gchar *current_dir;
  gchar *uri;

  current_dir = g_get_current_dir ();

  self->accounts = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
  self->name = "MyCloud";
  self->path = g_strdup (current_dir);
  uri = g_build_filename (current_dir, "icon.svg", NULL);
  icon_file = g_file_new_for_uri (uri);
  self->icon = g_file_icon_new (icon_file);

  g_object_unref (icon_file);
  g_free (uri);
  g_free (current_dir);
}

static void
cloud_providers_test_server_class_init (CloudProvidersTestServerClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);

  gobject_class->finalize = test_cloud_provider_finalize;
}

/* ---------------------------------------------------------------------------------------------------- */

static void
activate_action (GSimpleAction *action,
                 GVariant      *parameter,
                 gpointer       user_data)
{
  g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
}

static void
activate_toggle (GSimpleAction *action,
                 GVariant      *parameter,
                 gpointer       user_data)
{
  GVariant *old_state, *new_state;

  old_state = g_action_get_state (G_ACTION (action));
  new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));

  g_print ("Toggle action %s activated, state changes from %d to %d\n",
           g_action_get_name (G_ACTION (action)),
           g_variant_get_boolean (old_state),
           g_variant_get_boolean (new_state));

  g_simple_action_set_state (action, new_state);
  g_variant_unref (old_state);
}

static void
activate_radio (GSimpleAction *action,
                GVariant      *parameter,
                gpointer       user_data)
{
  GVariant *old_state, *new_state;

  old_state = g_action_get_state (G_ACTION (action));
  new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));

  g_print ("Radio action %s activated, state changes from %s to %s\n",
           g_action_get_name (G_ACTION (action)),
           g_variant_get_string (old_state, NULL),
           g_variant_get_string (new_state, NULL));

  g_simple_action_set_state (action, new_state);
  g_variant_unref (old_state);
}

static GActionEntry actions[] = {
  { "website",  activate_action, NULL, NULL, NULL },
  { "photos",  activate_action, NULL, NULL, NULL },
  { "notes",   activate_action, NULL, NULL, NULL },
  { "allow-sync",  activate_toggle, NULL, "true", NULL },
  { "buy",  activate_radio,  "s",  NULL, NULL },
};

struct menu {
  GMenu *mainMenu;
  GMenuItem *website;
  GMenuItem *photos;
  GMenuItem *notes;
  GMenuItem *allowSync;
  GMenuItem *buy;
};

static GMenuModel *
get_model (void)
{
  GMenu *section;
  struct menu *m;
  GMenuItem *item;
  GMenu *submenu;

  m = g_new0(struct menu, 1);
  m->mainMenu = g_menu_new();

  section = g_menu_new();
  m->website = g_menu_item_new("MyCloud website", "cloudprovider.website");
  g_menu_append_item(section, m->website);
  m->photos = g_menu_item_new("MyCloud photos", "cloudprovider.photos");
  g_menu_append_item(section, m->photos);
  m->notes = g_menu_item_new("MyCloud notes", "cloudprovider.notes");
  g_menu_append_item(section, m->notes);
  g_menu_append_section(m->mainMenu, NULL, G_MENU_MODEL(section));

  section = g_menu_new();
  m->allowSync = g_menu_item_new("Allow Synchronization", "cloudprovider.allow-sync");
  g_menu_append_item(section, m->allowSync);

  submenu = g_menu_new();
  item = g_menu_item_new("5GB", "5");
  g_menu_append_item(submenu, item);
  item = g_menu_item_new("10GB", "10");
  g_menu_append_item(submenu, item);
  item = g_menu_item_new("50GB", "50");
  g_menu_append_item(submenu, item);
  item = g_menu_item_new_submenu("Buy storage", G_MENU_MODEL(submenu));
  g_menu_append_item(section, item);
  g_menu_append_section(m->mainMenu, NULL, G_MENU_MODEL(section));

  return G_MENU_MODEL(m->mainMenu);
}

static GActionGroup *
get_action_group (void)
{
  GSimpleActionGroup *group;

  group = g_simple_action_group_new ();

  g_action_map_add_action_entries (G_ACTION_MAP (group),
                                   actions,
                                   G_N_ELEMENTS (actions), NULL);

  return G_ACTION_GROUP (group);
}

static gboolean
change_random_cloud_provider_state (gpointer user_data)
{
  CloudProvidersTestServer *self = (CloudProvidersTestServer *)user_data;
  CloudProvidersAccountExporter *account;
  GRand *rand;
  gint new_status;
  gint account_id;

  rand = g_rand_new ();
  account_id = g_rand_int_range (rand, 0, COUNT_PLACEHOLDER_ACCOUNTS);
  new_status = g_rand_int_range (rand,
                                 CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE,
                                 CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR + 1);

  g_print ("Change status of %03d to %d\n", account_id, new_status);
  account = g_hash_table_lookup (self->accounts, GINT_TO_POINTER (account_id));
  cloud_providers_account_exporter_set_status (account, new_status);

  return TRUE;
}

static gchar *
get_status_details (CloudProvidersAccountStatus status)
{
    gchar *description = "";
    switch (status) {
      case CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE:
        description = "Details: Sync idle";
        break;
      case CLOUD_PROVIDERS_ACCOUNT_STATUS_SYNCING:
        description = "Details: Syncing";
        break;
      case CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR:
        description = "Details: Error";
        break;
      case CLOUD_PROVIDERS_ACCOUNT_STATUS_INVALID:
        description = "Details: Sync status details not set";
        break;
    }
    return description;
}

static gboolean
add_accounts (CloudProvidersTestServer *self)
{
  guint n;

  // export multiple accounts as DBus objects to the bus
  for (n = 0; n < COUNT_PLACEHOLDER_ACCOUNTS; n++)
    {
      g_autoptr (CloudProvidersAccountExporter) account = NULL;
      g_autofree gchar *account_object_name = NULL;
      g_autofree gchar *account_name = NULL;

      account_object_name = g_strdup_printf ("MyAccount%d", n);
      account_name = g_strdup_printf ("MyAccount %d", n);
      g_debug ("Adding account %s", account_name);
      account = cloud_providers_account_exporter_new (self->exporter,
                                                      account_object_name);

      cloud_providers_account_exporter_set_name (account, account_name);
      cloud_providers_account_exporter_set_icon (account, self->icon);
      cloud_providers_account_exporter_set_path (account, self->path);
      cloud_providers_account_exporter_set_status (account,
                                                   CLOUD_PROVIDERS_ACCOUNT_STATUS_INVALID);
      cloud_providers_account_exporter_set_status_details (account,
                                                           get_status_details (CLOUD_PROVIDERS_ACCOUNT_STATUS_INVALID));
      cloud_providers_account_exporter_set_menu_model (account, get_model ());
      cloud_providers_account_exporter_set_action_group (account, get_action_group ());
      g_hash_table_insert (self->accounts, GINT_TO_POINTER (n), account);
    }

    return G_SOURCE_REMOVE;
}

static void
on_bus_acquired (GDBusConnection *connection,
                 const gchar     *name,
                 gpointer         user_data)
{
    CloudProvidersTestServer *self = CLOUD_PROVIDERS_TEST_SERVER (user_data);

    g_debug ("Bus adquired: %s\n", name);

    g_debug ("Registering cloud provider server 'MyCloud'\n");

    self->connection = connection;
    self->exporter = cloud_providers_provider_exporter_new(self->connection,
                                                           TEST_CLOUD_PROVIDERS_BUS_NAME,
                                                           TEST_CLOUD_PROVIDERS_OBJECT_PATH);
    cloud_providers_provider_exporter_set_name (self->exporter, "My cloud");
    add_accounts (self);

}

static void
on_name_acquired (GDBusConnection *connection,
                  const gchar     *name,
                  gpointer         user_data)
{
  CloudProvidersTestServer *self = (CloudProvidersTestServer *)user_data;
  self->timeout_handler = g_timeout_add (TIMEOUT,
                                         (GSourceFunc) change_random_cloud_provider_state,
                                         self);
  g_debug ("Server test name adquired");
  change_random_cloud_provider_state (self);
}

static void
on_name_lost (GDBusConnection *connection,
              const gchar     *name,
              gpointer         user_data)
{
    g_critical ("Name lost: %s\n", name);
}

int
main (int argc, char *argv[])
{
  GMainLoop *loop;
  CloudProvidersTestServer *test_cloud_provider;
  guint owner_id;

  test_cloud_provider = cloud_providers_test_server_new ();

  owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
                             TEST_CLOUD_PROVIDERS_BUS_NAME,
                             G_BUS_NAME_OWNER_FLAGS_NONE,
                             on_bus_acquired,
                             on_name_acquired,
                             on_name_lost,
                             test_cloud_provider,
                             NULL);

  loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (loop);

    g_debug("going oooooout/n");
  g_bus_unown_name (owner_id);
  g_object_unref (test_cloud_provider);

  return 0;
}

