#include <gtk/gtk.h>

enum
{
  COL_NUM_TEXT = 0,
  COL_NUM_VALUE,
  NUM_NUM_COLS
};

typedef struct
{
  GtkCellRendererText *cell;
  GtkTreeView *view;
  gchar *path;
} CELL_INFO_OF_EDITBOX;
CELL_INFO_OF_EDITBOX g_cellinfo_editbox = {NULL, NULL, NULL};

static void
cb_cell_time_edited(GtkCellRendererText *cell,
                    gchar               *path_string,
                    gchar               *new_text,
                    gpointer             user_data)
{
  printf("edited\n");
  if(!new_text)
    return;

  GtkTreeModel *model;
  GtkTreeModel *filter;
  GtkTreeView *tree_view = GTK_TREE_VIEW(user_data);
  gint new_time = atoi(new_text);
  gint col = 0;

  filter = gtk_tree_view_get_model(tree_view);
  model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));

  GtkTreeIter  iter;
  GtkTreePath *path = gtk_tree_path_new_from_string(path_string);

  gtk_tree_model_get_iter(model, &iter, path);

  gchar* tmp = g_strdup_printf("%d", new_time);
  gtk_list_store_set(GTK_LIST_STORE(model),
                     &iter, col, tmp, -1);

  g_free(tmp);
  gtk_tree_path_free (path);
}

static gboolean
cb_cell_time_focus_out(GtkWidget     *widget,
                       GdkEventFocus *event,
                       gpointer       user_data)
{
  printf("focus-out\n");

  CELL_INFO_OF_EDITBOX *pInfo = (CELL_INFO_OF_EDITBOX *)user_data;
  if (pInfo == NULL || pInfo->cell == NULL)
    return FALSE;

  gchar *str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);

  cb_cell_time_edited(pInfo->cell,
                      pInfo->path,
                      str,
                      pInfo->view);

  pInfo->cell = NULL;
  pInfo->view = NULL;
  if (pInfo->path)
    g_free(pInfo->path);
  pInfo->path = NULL;

  g_free(str);

  return FALSE;
}

static void
cb_cell_time_editing_started(GtkCellRendererText *cell,
                             GtkCellEditable     *editable,
                             gchar               *path,
                             gpointer             user_data)
{
  printf("editing-started\n");
  if (!GTK_IS_ENTRY(editable))
    return;

  g_cellinfo_editbox.cell = cell;
  g_cellinfo_editbox.view = user_data;

  if (g_cellinfo_editbox.path)
    g_free(g_cellinfo_editbox.path);

  g_cellinfo_editbox.path = g_strdup(path);

  g_signal_connect(editable,
                   "focus-out-event",
                   G_CALLBACK(cb_cell_time_focus_out),
                   &g_cellinfo_editbox);
}

static void
cb_cell_time_editing_canceled(GtkCellRendererText *cell, gpointer user_data)
{
  printf("editing-canceled\n");
  g_cellinfo_editbox.cell = NULL;
  g_cellinfo_editbox.view = NULL;

  if (g_cellinfo_editbox.path)
    g_free(g_cellinfo_editbox.path);

  g_cellinfo_editbox.path = NULL;
}


static gboolean
filter_visible_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
  return TRUE;
}

static GtkTreeModel*
create_fade_model(void)
{
    GtkListStore *model;
    GtkTreeIter iter;
    model = gtk_list_store_new (NUM_NUM_COLS, G_TYPE_STRING, G_TYPE_INT);

    gchar* tmp;

    gtk_list_store_append (model, &iter);
    tmp = g_strdup("no effect");
    gtk_list_store_set (model, &iter,
                        COL_NUM_TEXT, tmp,
                        COL_NUM_VALUE, 0,
                        -1);
    g_free(tmp);

    gtk_list_store_append (model, &iter);
    tmp = g_strdup("fade-in");
    gtk_list_store_set (model, &iter,
                        COL_NUM_TEXT, tmp,
                        COL_NUM_VALUE, 1,
                        -1);
    g_free(tmp);

    gtk_list_store_append (model, &iter);
    tmp = g_strdup("fade-out");
    gtk_list_store_set (model, &iter,
                        COL_NUM_TEXT, tmp,
                        COL_NUM_VALUE, 2,
                        -1);
    g_free(tmp);

    gtk_list_store_append (model, &iter);
    tmp = g_strdup("cross-fade");
    gtk_list_store_set (model, &iter,
                        COL_NUM_TEXT, tmp,
                        COL_NUM_VALUE, 3,
                        -1);
    g_free(tmp);

    return GTK_TREE_MODEL(model);
}

static void
fade_edited(const gchar *path_string,
            const gchar *new_text,
            gpointer     user_data)
{
    printf("fade_edited <%s>\n", new_text);
    GtkTreeView *tree_view = GTK_TREE_VIEW(user_data);

    GtkTreeModel *model;
    GtkTreeModel *filter;

    filter = gtk_tree_view_get_model(tree_view);
    model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));

    GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
    GtkTreeIter iter;

    gtk_tree_model_get_iter(model, &iter, path);

    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, new_text, -1);
    gtk_tree_path_free(path);
}

static void
cb_combo_cell_fade_changed(GtkCellRendererCombo *cell,
                           const gchar         *path_string,
                           GtkTreeIter          *new_iter,
                           gpointer             user_data)
{
    printf("cb_combo_cell_fade_changed\n");
    if (!new_iter)
        return;

    GtkTreeModel* model = NULL;
    gchar* new_text = NULL;

    g_object_get(G_OBJECT(cell), "model", &model, NULL);
    gtk_tree_model_get(model, new_iter, COL_NUM_TEXT, &new_text, -1);

    if (new_text) {
        fade_edited(path_string, new_text, user_data);
        g_free(new_text);
    }
}

static void
cb_combo_cell_fade_edited (GtkCellRendererText *cell,
                           const gchar         *path_string,
                           const gchar         *new_text,
                           gpointer             user_data)
{
    printf("cb_combo_cell_fade_edited\n");
    if (!new_text)
        return;

    fade_edited(path_string, new_text, user_data);
}

static GtkWidget *
create_view_and_model(void)
{
  GtkTreeViewColumn *col;
  GtkCellRenderer   *renderer;
  GtkWidget         *view;
  GtkTreeModel      *model;
  GtkListStore      *store;
  GtkTreeIter        iter1;
  GtkTreeIter        iter2;
  GtkTreeIter        iter3;
  GtkTreeModel      *filter;
  GtkTreeModel      *fade_model;

  view = gtk_tree_view_new();
  renderer = gtk_cell_renderer_text_new();

  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
                                              -1,
                                              "time(s)",
                                              renderer,
                                              "text", 0,
                                              NULL);
  g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
  g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(cb_cell_time_edited), view);
  g_signal_connect(G_OBJECT(renderer), "editing-started", G_CALLBACK(cb_cell_time_editing_started), view);
  g_signal_connect(G_OBJECT(renderer), "editing-canceled", G_CALLBACK(cb_cell_time_editing_canceled), view);
  g_object_set_data(G_OBJECT(renderer), "column_num", GUINT_TO_POINTER(0));

  fade_model = create_fade_model();

  renderer = gtk_cell_renderer_combo_new();
  g_object_set(G_OBJECT(renderer), "model", fade_model, NULL);
  g_object_set(G_OBJECT(renderer), "text-column", COL_NUM_TEXT, NULL);
  g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
  g_object_set(G_OBJECT(renderer), "has-entry", FALSE, NULL);
  g_object_set_data(G_OBJECT(renderer), "column_num", GUINT_TO_POINTER(1));
  /* It works though don't connect changed signal */
  g_signal_connect(G_OBJECT(renderer), "changed",
                   G_CALLBACK(cb_combo_cell_fade_changed), view);
  /* Cannot edit if don't connect both changed and edited signal */
  g_signal_connect(G_OBJECT(renderer), "edited",
                   G_CALLBACK(cb_combo_cell_fade_edited), view);
  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view),
                                              -1,
                                              "fade-effect",
                                              renderer,
                                              "text", 1,
                                              NULL);
  g_object_unref(fade_model);

  store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
  gtk_list_store_append(store, &iter1);
  gtk_list_store_set(store, &iter1,
                     0, "10",
                     1, "no effect no effect",
                     -1);
  gtk_list_store_append(store, &iter2);
  gtk_list_store_set(store, &iter2,
                     0, "20",
                     1, "fade-out fade-out",
                     -1);
  gtk_list_store_append(store, &iter3);
  gtk_list_store_set(store, &iter3,
                     0, "30",
                     1, "fade-in fade-in fade-in",
                     -1);

  filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(store), NULL);
  gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter),
                                         filter_visible_func, NULL, NULL);
  gtk_tree_view_set_model(GTK_TREE_VIEW(view), filter);
  col = gtk_tree_view_get_column(GTK_TREE_VIEW(view), 1);

  return view;
}

#if GTK_CHECK_VERSION(3, 8, 2)
static void
load_css(gchar *path)
{
  GtkCssProvider *provider;
  GdkDisplay *display;
  GdkScreen *screen;

  if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
    g_warning("gtk.css doesn't exist: %s", path);
    return;
  }
  provider = gtk_css_provider_new();
  //display = gdk_display_get_default();
  display = gdk_display_open(":0");
  screen = gdk_display_get_default_screen(display);

  gtk_style_context_add_provider_for_screen(screen,
                                            GTK_STYLE_PROVIDER (provider),
                                            GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);

  gtk_css_provider_load_from_path(GTK_CSS_PROVIDER(provider), path, NULL);

  g_object_unref(provider);
}
#else
static void
load_gtkrc(gchar *path)
{
  if (g_file_test(path, G_FILE_TEST_EXISTS)) {
    gtk_rc_add_default_file(path);
    g_message("Load gtkrc: %s", path);
  } else {
    g_warning("gtkrc doesn't exist: %s", path);
  }
}
#endif

int
main (int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *view;

#if GTK_CHECK_VERSION(3, 8, 2)
  load_css("/tmp/b/theme/gtk.css");
#else
  load_gtkrc("/tmp/b/theme/gtkrc");
#endif

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  g_signal_connect(window, "delete_event", gtk_main_quit, NULL); /* dirty */

  view = create_view_and_model();

  gtk_container_add(GTK_CONTAINER(window), view);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

