/*
 * Copyright (C) 2008 Carlos Garcia Campos  <carlosgc@gnome.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "config.h"

#include <gtk/gtk.h>

#include "layers.h"

enum
{
    LAYERS_TITLE_COLUMN,
    LAYERS_VISIBILITY_COLUMN,
    LAYERS_ENABLE_COLUMN,
    LAYERS_SHOWTOGGLE_COLUMN,
    LAYERS_RB_GROUP_COLUMN,
    LAYERS_LAYER_COLUMN,
    N_COLUMNS
};

typedef struct
{
    PopplerDocument *doc;
    guint page;
    GtkWidget *treeview;
    GtkWidget *darea;

    cairo_surface_t *surface;
} PgdLayersDemo;

static void pgd_layers_free(PgdLayersDemo *demo)
{
    if (!demo)
        return;

    if (demo->doc) {
        g_object_unref(demo->doc);
        demo->doc = NULL;
    }

    if (demo->surface) {
        cairo_surface_destroy(demo->surface);
        demo->surface = NULL;
    }

    g_free(demo);
}

static void build_tree(PopplerDocument *document, GtkTreeModel *model, GtkTreeIter *parent, PopplerLayersIter *iter)
{

    do {
        GtkTreeIter tree_iter;
        PopplerLayersIter *child;
        PopplerLayer *layer;
        gboolean visible;
        gchar *markup;
        gint rb_group = 0;

        layer = poppler_layers_iter_get_layer(iter);
        if (layer) {
            markup = g_markup_escape_text(poppler_layer_get_title(layer), -1);
            visible = poppler_layer_is_visible(layer);
            rb_group = poppler_layer_get_radio_button_group_id(layer);
        } else {
            gchar *title;

            title = poppler_layers_iter_get_title(iter);
            markup = g_markup_escape_text(title, -1);
            g_free(title);

            visible = FALSE;
            layer = NULL;
        }

        gtk_tree_store_append(GTK_TREE_STORE(model), &tree_iter, parent);
        gtk_tree_store_set(GTK_TREE_STORE(model), &tree_iter, LAYERS_TITLE_COLUMN, markup, LAYERS_VISIBILITY_COLUMN, visible, LAYERS_ENABLE_COLUMN, TRUE, /* FIXME */
                           LAYERS_SHOWTOGGLE_COLUMN, (layer != NULL), LAYERS_RB_GROUP_COLUMN, rb_group, LAYERS_LAYER_COLUMN, layer, -1);
        if (layer)
            g_object_unref(layer);
        g_free(markup);

        child = poppler_layers_iter_get_child(iter);
        if (child)
            build_tree(document, model, &tree_iter, child);
        poppler_layers_iter_free(child);
    } while (poppler_layers_iter_next(iter));
}

GtkTreeModel *pgd_layers_create_model(PopplerDocument *document)
{
    GtkTreeModel *model;
    PopplerLayersIter *iter;

    iter = poppler_layers_iter_new(document);
    if (iter) {
        model = GTK_TREE_MODEL(gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_OBJECT));
        build_tree(document, model, NULL, iter);
        poppler_layers_iter_free(iter);
    } else {
        GtkTreeIter tree_iter;
        gchar *markup;

        model = GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_STRING));
        gtk_list_store_append(GTK_LIST_STORE(model), &tree_iter);
        markup = g_strdup_printf("<span size=\"larger\" style=\"italic\">%s</span>", "The document doesn't contain layers");
        gtk_list_store_set(GTK_LIST_STORE(model), &tree_iter, 0, markup, -1);
        g_free(markup);
    }

    return model;
}

static cairo_surface_t *pgd_layers_render_page(PgdLayersDemo *demo)
{
    cairo_t *cr;
    PopplerPage *page;
    gdouble width, height;
    cairo_surface_t *surface = NULL;

    page = poppler_document_get_page(demo->doc, demo->page);
    if (!page)
        return NULL;

    poppler_page_get_size(page, &width, &height);
    gtk_widget_set_size_request(demo->darea, width, height);

    surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
    cr = cairo_create(surface);

    cairo_save(cr);
    cairo_set_source_rgb(cr, 1, 1, 1);
    cairo_rectangle(cr, 0, 0, width, height);
    cairo_fill(cr);
    cairo_restore(cr);

    cairo_save(cr);
    poppler_page_render(page, cr);
    cairo_restore(cr);

    cairo_destroy(cr);
    g_object_unref(page);

    return surface;
}

static gboolean pgd_layers_viewer_drawing_area_draw(GtkWidget *area, cairo_t *cr, PgdLayersDemo *demo)
{
    if (!demo->surface) {
        demo->surface = pgd_layers_render_page(demo);
        if (!demo->surface)
            return FALSE;
    }

    cairo_set_source_surface(cr, demo->surface, 0, 0);
    cairo_paint(cr);

    return TRUE;
}

static gboolean pgd_layers_viewer_redraw(PgdLayersDemo *demo)
{
    cairo_surface_destroy(demo->surface);
    demo->surface = NULL;

    gtk_widget_queue_draw(demo->darea);

    return FALSE;
}

static void pgd_layers_viewer_queue_redraw(PgdLayersDemo *demo)
{
    g_idle_add((GSourceFunc)pgd_layers_viewer_redraw, demo);
}

static void pgd_layers_page_selector_value_changed(GtkSpinButton *spinbutton, PgdLayersDemo *demo)
{
    demo->page = (gint)gtk_spin_button_get_value(spinbutton) - 1;
    pgd_layers_viewer_queue_redraw(demo);
}

static GtkWidget *pgd_layers_create_viewer(PgdLayersDemo *demo)
{
    GtkWidget *vbox, *hbox;
    GtkWidget *label;
    GtkWidget *swindow;
    GtkWidget *page_selector;
    guint n_pages;
    gchar *str;

    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);

    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);

    label = gtk_label_new("Page:");
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
    gtk_widget_show(label);

    n_pages = poppler_document_get_n_pages(demo->doc);
    page_selector = gtk_spin_button_new_with_range(1, n_pages, 1);
    g_signal_connect(G_OBJECT(page_selector), "value-changed", G_CALLBACK(pgd_layers_page_selector_value_changed), (gpointer)demo);
    gtk_box_pack_start(GTK_BOX(hbox), page_selector, FALSE, TRUE, 0);
    gtk_widget_show(page_selector);

    str = g_strdup_printf("of %d", n_pages);
    label = gtk_label_new(str);
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
    gtk_widget_show(label);
    g_free(str);

    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
    gtk_widget_show(hbox);

    demo->darea = gtk_drawing_area_new();
    g_signal_connect(G_OBJECT(demo->darea), "draw", G_CALLBACK(pgd_layers_viewer_drawing_area_draw), (gpointer)demo);

    swindow = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
#if GTK_CHECK_VERSION(3, 7, 8)
    gtk_container_add(GTK_CONTAINER(swindow), demo->darea);
#else
    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swindow), demo->darea);
#endif
    gtk_widget_show(demo->darea);

    gtk_box_pack_start(GTK_BOX(vbox), swindow, TRUE, TRUE, 0);
    gtk_widget_show(swindow);

    return vbox;
}

static gboolean update_kids(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GtkTreeIter *parent)
{
    if (gtk_tree_store_is_ancestor(GTK_TREE_STORE(model), parent, iter)) {
        gboolean visible;

        gtk_tree_model_get(model, parent, LAYERS_VISIBILITY_COLUMN, &visible, -1);
        gtk_tree_store_set(GTK_TREE_STORE(model), iter, LAYERS_ENABLE_COLUMN, visible, -1);
    }

    return FALSE;
}

static gboolean clear_rb_group(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gint *rb_group)
{
    gint group;

    gtk_tree_model_get(model, iter, LAYERS_RB_GROUP_COLUMN, &group, -1);

    if (group == *rb_group) {
        gtk_tree_store_set(GTK_TREE_STORE(model), iter, LAYERS_VISIBILITY_COLUMN, FALSE, -1);
    }

    return FALSE;
}

static void pgd_layers_visibility_changed(GtkCellRendererToggle *cell, gchar *path_str, PgdLayersDemo *demo)
{
    GtkTreeModel *model;
    GtkTreePath *path;
    GtkTreeIter iter;
    gboolean visible;
    PopplerLayer *layer;

    model = gtk_tree_view_get_model(GTK_TREE_VIEW(demo->treeview));

    path = gtk_tree_path_new_from_string(path_str);
    gtk_tree_model_get_iter(model, &iter, path);
    gtk_tree_model_get(model, &iter, LAYERS_VISIBILITY_COLUMN, &visible, LAYERS_LAYER_COLUMN, &layer, -1);

    visible = !visible;
    visible ? poppler_layer_show(layer) : poppler_layer_hide(layer);

    if (visible) {
        gint rb_group;

        rb_group = poppler_layer_get_radio_button_group_id(layer);
        if (rb_group) {
            gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)clear_rb_group, &rb_group);
        }
    }

    gtk_tree_store_set(GTK_TREE_STORE(model), &iter, LAYERS_VISIBILITY_COLUMN, visible, -1);

    if (poppler_layer_is_parent(layer)) {
        gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)update_kids, &iter);
    }

    pgd_layers_viewer_queue_redraw(demo);

    gtk_tree_path_free(path);
    g_object_unref(layer);
}

GtkWidget *pgd_layers_create_widget(PopplerDocument *document)
{
    PgdLayersDemo *demo;
    GtkWidget *swindow;
    GtkWidget *treeview;
    GtkTreeModel *model;
    GtkCellRenderer *renderer;
    GtkWidget *hpaned, *viewer;

    demo = g_new0(PgdLayersDemo, 1);
    demo->doc = g_object_ref(document);

    hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);

    viewer = pgd_layers_create_viewer(demo);

    swindow = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    model = pgd_layers_create_model(document);
    treeview = gtk_tree_view_new_with_model(model);
    demo->treeview = treeview;
    g_object_unref(model);

    renderer = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Layer", renderer, "markup", LAYERS_TITLE_COLUMN, NULL);
    g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
    g_object_set(G_OBJECT(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 0)), "expand", TRUE, NULL);

    if (GTK_IS_TREE_STORE(model)) {
        renderer = gtk_cell_renderer_toggle_new();
        gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 1, "Show/Hide", renderer, "active", LAYERS_VISIBILITY_COLUMN, "activatable", LAYERS_ENABLE_COLUMN, "visible", LAYERS_SHOWTOGGLE_COLUMN, NULL);

        g_signal_connect(renderer, "toggled", G_CALLBACK(pgd_layers_visibility_changed), (gpointer)demo);
        gtk_tree_view_column_set_clickable(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 1), TRUE);
    }

    gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_NONE);

    gtk_container_add(GTK_CONTAINER(swindow), treeview);
    gtk_widget_show(treeview);

    gtk_paned_add1(GTK_PANED(hpaned), swindow);
    gtk_widget_show(swindow);

    gtk_paned_add2(GTK_PANED(hpaned), viewer);
    gtk_widget_show(viewer);

    gtk_paned_set_position(GTK_PANED(hpaned), 150);

    g_object_weak_ref(G_OBJECT(hpaned), (GWeakNotify)pgd_layers_free, (gpointer)demo);

    return hpaned;
}
