
#include "gtk-local.h"

#include "gtk-toolbar.h"
#include "gtk-hsvcontrol.h"

#include "magnify-tool.h"
#include "gtk-graph.h"
#include "gtv/event-stack.h"
#include <stdio.h>
#include <string.h>
#ifdef WIN32
#include <gdk/gdkwin32.h>
#endif /* WIN32 */

#undef GGTK_USE_MENU_BAR

GdkColor ggtk_black = {0, 0, 0, 0};
GdkColor ggtk_white = {0, 0xffff, 0xffff, 0xffff};

static void post_command( const char *args, ...)
{
    va_list l;

    va_start( l, args);
    if (sic_post_command_va( args, l) == -1)
        gdk_display_beep( gdk_display_get_default());
    va_end( l);
}

static void click_item( GtkWidget *w, void *data)
{
}

static void set_pen_number( GtkWidget *widget, void *data)
{
    post_command( "GREG1\\PENCIL %d", (size_t)data);
}

static void set_pen_color( GtkWidget *widget, void *data)
{
    post_command( "GREG1\\PENCIL /COLOUR %d", (size_t)data);
}

static void set_pen_dash( GtkWidget *widget, void *data)
{
    post_command( "GREG1\\PENCIL /DASHED %d", (size_t)data);
}

static void set_pen_weight( GtkWidget *widget, void *data)
{
    post_command( "GREG1\\PENCIL /WEIGHT %d", (size_t)data);
}

static void set_marker_nsides( GtkWidget *widget, void *data)
{
    post_command( "GREG1\\SET MARKER %d * * *", (size_t)data);
}

static void set_marker_mstyle( GtkWidget *widget, void *data)
{
    post_command( "GREG1\\SET MARKER * %d * *", (size_t)data);
}

static void set_marker_size( GtkWidget *widget, void *data)
{
    post_command( "GREG1\\SET MARKER * * %f *", (size_t)data * 0.2);
}

static void set_marker_orientation( GtkWidget *widget, void *data)
{
    post_command( "GREG1\\SET MARKER * * * %d", (size_t)data * 15);
}


static void send_command(
#ifndef GGTK_USE_MENU_BAR
GtkWidget *widget,
#endif
void *data)
{
    post_command( (char *)data);
}

static void ggtk_hardcopy(
#ifndef GGTK_USE_MENU_BAR
GtkWidget *widget,
#endif
G_env *genv)
{
    char dir_name[1024];
    gtv_get_dir_name( genv, dir_name, sizeof(dir_name));
    post_command( "GTVL\\HARDCOPY /PRINT /DIRECTORY %s", dir_name);
}

static void ggtk_clear(
#ifndef GGTK_USE_MENU_BAR
GtkWidget *widget,
#endif
G_env *genv)
{
    char dir_name[1024];
    gtv_get_dir_name( genv, dir_name, sizeof(dir_name));
    post_command( "GTVL\\CLEAR DIRECTORY %s", dir_name);
}

// S.Guilloteau:  Add the CONTINUE button, very convenient in any script with PAUSE
static void ggtk_continue(
#ifndef GGTK_USE_MENU_BAR
GtkWidget *widget,
#endif
G_env *genv)
{
    post_command( "SIC\\CONTINUE");
}


static void ggtk_edit_lut(
#ifndef GGTK_USE_MENU_BAR
GtkWidget *widget,
#endif
G_env *genv)
{
    run_hsv_control( genv);
}

/*****************************************************************************/

static gboolean expose_event_callback( GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
    ggtk_env_t *genv = (ggtk_env_t *)data;
    //env = (G_env *)g_object_get_data( drawing_area, "ENV");
    //printf( "enter expose_event_callback\n");
#if 1
    if (genv->win_graph) {
#ifndef NO_CALL_TO_PUSH_REFRESH_WITH_NULL_GENV_DURING_ZOOM
        if (genv->post_refresh_handler != NULL) {
            // direct drawing
            gtv_refresh( (G_env *)data, GTV_REFRESH_MODE_REWIND);
            ggtk_call_post_refresh_handler( genv);
        } else
#endif
        {
            gtv_push_refresh( ((G_env *)data)->adr_dir,
             GTV_REFRESH_MODE_REFRESH, (G_env *)data);
        }
    } else {
        printf( "FAIL expose_event_callback\n");
    }
#else
    gdk_draw_line( widget->window,
        widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
        0, 0, widget->allocation.width, widget->allocation.height);
    gdk_display_flush( gdk_display_get_default());
#endif
    //printf( "leave expose_event_callback\n");
    return FALSE; // don't continue propagation
}

static gboolean configure_event_callback( GtkWidget *widget, GdkEventConfigure *event, gpointer data)
{
    //printf( "enter configure_event_callback( %d %d %d %d %lx)\n", event->x, event->y,
    //    event->width, event->height, (size_t)data);
    gtv_on_resize((G_env *)data, event->width, event->height);
    //printf( "leave configure_event_callback\n");
    return TRUE; // don't continue propagation
}

/*****************************************************************************/

static _event_handler_t _pointer_event_handler;
static gpointer _pointer_user_data;

void ggtk_set_pointer_event_handler( _event_handler_t h, gpointer user_data)
{
    _pointer_event_handler = h;
    _pointer_user_data = user_data;
}

int ggtk_call_pointer_event_handler( GdkEvent *event, gpointer event_data)
{
    if (_pointer_event_handler == NULL)
        return 0;

    return _pointer_event_handler( event, event_data, _pointer_user_data);
}

/*****************************************************************************/

static gboolean button_press_callback( GtkWidget *widget, GdkEventButton *event, gpointer data)
{
    //printf("button_press_callback: %d\n", event->button);
    if (ggtk_call_pointer_event_handler( (GdkEvent *)event, data))
        return TRUE; // don't continue propagation

    if (event->button == 2)
        ggtk_activate_magnify( event, data);
    return TRUE; // don't continue propagation
}

static gboolean button_release_callback( GtkWidget *widget, GdkEventButton *event, gpointer data)
{
    //printf("button_release_callback: %d\n", event->button);
    ggtk_call_pointer_event_handler( (GdkEvent *)event, data);
    return TRUE; // don't continue propagation
}

static gboolean motion_notify_callback( GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
    //printf("motion_notify_callback\n");
    ggtk_call_pointer_event_handler( (GdkEvent *)event, data);
    return TRUE; // don't continue propagation
}

static gboolean scroll_notify_callback( GtkWidget *widget, GdkEventScroll *event, gpointer data)
{
    //printf("scroll_notify_callback: %d %f %f\n", event->type, event->x, event->y);
    if (ggtk_call_pointer_event_handler( (GdkEvent *)event, data))
        return TRUE; // don't continue propagation

    return TRUE; // don't continue propagation
}

static gboolean key_press_callback( GtkWidget *widget, GdkEventKey *event, gpointer data)
{
    //printf("key_press_callback\n");
    ggtk_call_pointer_event_handler( (GdkEvent *)event, data);
    return TRUE; // don't continue propagation
}

static gboolean noop_focus_callback( GtkWidget *widget, GdkEventFocus *event, gpointer data)
{
    // disable default behaviour which fires expose event 
    return TRUE; // don't continue propagation
}

static gboolean delete_event_callback( GtkWidget *widget, GdkEvent *event, gpointer data)
{
    /* return FALSE for emitting "destroy" */
    return FALSE;
}

static int _gtk_nb_windows = 0;

static void destroy_event_callback( GtkWidget *widget, gpointer data)
{
    //printf( "gtk-toolbar: destroy_event_callback: %d\n", _gtk_nb_windows);
    _gtk_nb_windows--;
#if 0
    if (!_gtk_nb_windows)
        gtk_main_quit();
#endif
    gtv_on_destroy_genv( (G_env *)data);
}

#ifndef GGTK_USE_MENU_BAR
static void _on_tool_item_clicked( GtkWidget *toggle)
{
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
}

static void _find_toggle_callback( GtkWidget *widget, gpointer data)
{
    if (GTK_IS_TOGGLE_BUTTON(widget)) {
        *(GtkWidget**)data = widget;
    } else if (GTK_IS_CONTAINER(widget)) {
        gtk_container_forall(GTK_CONTAINER(widget),  _find_toggle_callback,
        data);
    }
}
#endif

GtkWidget *ggtk_menu_new( const char* text, GtkWidget *parent, gboolean parent_is_menu_bar)
{
    GtkWidget *menu_pane;
    GtkWidget *item;

    menu_pane = gtk_menu_new( );
#ifndef GGTK_USE_MENU_BAR
    if (parent_is_menu_bar) {
        GtkWidget *toggle;

        item =  (GtkWidget *)gtk_menu_tool_button_new( NULL, text);
        gtk_container_forall(GTK_CONTAINER(item),  _find_toggle_callback,
         &toggle);
        g_signal_connect_swapped( G_OBJECT(item), "clicked",
         G_CALLBACK(_on_tool_item_clicked), toggle);
        gtk_menu_tool_button_set_menu( GTK_MENU_TOOL_BUTTON(item), menu_pane);
        gtk_toolbar_insert( GTK_TOOLBAR(parent), GTK_TOOL_ITEM(item), -1);
    } else {
#endif
    item = gtk_menu_item_new_with_label( text);
    gtk_menu_item_set_submenu( GTK_MENU_ITEM(item), menu_pane);
#ifndef GGTK_USE_MENU_BAR
    gtk_widget_show(item);
#endif
    if (parent_is_menu_bar) {
        gtk_menu_bar_append( GTK_MENU_BAR(parent), item);
    }
    else {
        gtk_menu_shell_append( GTK_MENU_SHELL(parent), item);
    }
#ifndef GGTK_USE_MENU_BAR
    }
#endif

    return menu_pane;
}

GtkWidget *ggtk_menu_item_on_menu_bar_new( const char* text, GtkWidget *menu_bar, GCallback click_item, gpointer data)
{
#ifndef GGTK_USE_MENU_BAR
    GtkToolItem *item;

    item =  gtk_tool_button_new( NULL, text);
    gtk_toolbar_insert( GTK_TOOLBAR(menu_bar), item, -1);
    g_signal_connect( G_OBJECT(item), "clicked", click_item, data);

    return (GtkWidget *)item;
#else
    GtkWidget *item;

    item = gtk_menu_item_new_with_label( text);
    gtk_menu_bar_append( GTK_MENU_BAR(menu_bar), item);
    g_signal_connect( G_OBJECT(item), "activate", click_item, data);

    return item;
#endif
}

#if 0
{
    GtkWidget *toolbar;
    GtkToolItem *toolitem;
    GtkWidget *toggle;

    toolbar = gtk_toolbar_new();
    toolitem =  gtk_tool_button_new( NULL, "button");
    gtk_toolbar_insert( GTK_TOOLBAR(toolbar), toolitem, -1);
    toolitem =  gtk_menu_tool_button_new( NULL, "menu");
    gtk_toolbar_insert( GTK_TOOLBAR(toolbar), toolitem, -1);
    gtk_menu_tool_button_set_menu( toolitem, menu);
    gtk_box_pack_end (GTK_BOX (vbox), toolbar, TRUE, TRUE, 2);
    gtk_widget_show_all (toolbar);
    gtk_container_forall(GTK_CONTAINER(toolitem),  _find_toggle_callback,
    &toggle);
    g_signal_connect_swapped( G_OBJECT(toolitem), "clicked",
     G_CALLBACK(_on_tool_item_clicked), toggle);
}
#endif

GtkWidget *ggtk_menu_item_new( const char* text, GtkWidget *parent, GCallback click_item, gpointer data)
{
    GtkWidget *item;

    item = gtk_menu_item_new_with_label( text);
    gtk_menu_shell_append( GTK_MENU_SHELL(parent), item);
    g_signal_connect( G_OBJECT(item), "activate", click_item, (gpointer)data);
#ifndef GGTK_USE_MENU_BAR
    gtk_widget_show(item);
#endif

    return item;
}

GtkWidget *ggtk_image_menu_item_new( const char* text, GdkPixmap *pixmap, GtkWidget *parent, GCallback click_item, gpointer data)
{
    GtkWidget *item;
    GtkWidget *image;

    item = gtk_image_menu_item_new_with_label( text);
    image = gtk_image_new_from_pixmap( pixmap, NULL);
    gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(item), image);

    gtk_menu_shell_append( GTK_MENU_SHELL(parent), item);
    g_signal_connect( G_OBJECT(item), "activate", click_item, (gpointer)data);
#ifndef GGTK_USE_MENU_BAR
    gtk_widget_show(item);
#endif

    return item;
}

static void _fill_menu_bar(GtkWidget *menu_bar, GdkDrawable *drawable, ggtk_env_t *env)
{
    GtkWidget *menu_pane;
    GtkWidget *menu_pane2;
    GtkWidget *item;
    int i;
    char dum[32];

    menu_pane = ggtk_menu_new( "Pencil", menu_bar, 1);

    menu_pane2 = ggtk_menu_new( "Number", menu_pane, 0);
    for (i = 0; i <= 15; i++) {
        sprintf( dum, "%d", i);
        item = ggtk_menu_item_new( dum, menu_pane2, G_CALLBACK( set_pen_number), (gpointer)(size_t)i);
    }

    menu_pane2 = ggtk_menu_new( "Dash", menu_pane, 0);
    for (i = 1; i <= 7; i++) {
        GdkPixmap *pixmap;
        GdkGC *gc;

        static gint8 dashed[][4] = {
            {1, 0, 0, 0}, {10, 10, 0, 0}, {2, 6, 0, 0}, {10, 10, 2, 10},
            {22, 18, 0, 0}, {20, 8, 1, 8}, {20, 8, 8, 8}
        };
        static int ldashed[7] = { 1, 2, 2, 4, 2, 4, 4 };

        pixmap = gdk_pixmap_new( drawable, 64, 32, -1);
        gc = gdk_gc_new( pixmap);
        gdk_gc_set_rgb_fg_color( gc, &ggtk_white);
        gdk_draw_rectangle( pixmap, gc, TRUE, 0, 0, 64, 32);
        gdk_gc_set_rgb_fg_color( gc, &ggtk_black);
        gdk_gc_set_dashes( gc, 0, dashed[i - 1], ldashed[i - 1]);
        gdk_gc_set_line_attributes( gc, 5, i == 1 ? GDK_LINE_SOLID : GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND);
        gdk_draw_line( pixmap, gc, 0, 16, 64, 16);

        sprintf( dum, "%d", i);
        ggtk_image_menu_item_new( dum, pixmap, menu_pane2, G_CALLBACK( set_pen_dash), (gpointer)(size_t)i);
        g_object_unref( gc);
        g_object_unref( pixmap);
    }

    menu_pane2 = ggtk_menu_new( "Weight", menu_pane, 0);
    for (i = 1; i <= 5; i++) {
        GdkPixmap *pixmap;
        GdkGC *gc;

        pixmap = gdk_pixmap_new( drawable, 64, 32, -1);
        gc = gdk_gc_new( pixmap);
        gdk_gc_set_rgb_fg_color( gc, &ggtk_white);
        gdk_draw_rectangle( pixmap, gc, TRUE, 0, 0, 64, 32);
        gdk_gc_set_rgb_fg_color( gc, &ggtk_black);
        gdk_gc_set_line_attributes( gc, i, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
        gdk_draw_line( pixmap, gc, 0, 16, 64, 16);

        sprintf( dum, "%d", i);
        ggtk_image_menu_item_new( dum, pixmap, menu_pane2, G_CALLBACK( set_pen_weight), (gpointer)(size_t)i);
        g_object_unref( gc);
        g_object_unref( pixmap);
    }

    menu_pane = ggtk_menu_new( "Marker", menu_bar, 1);
    menu_pane2 = ggtk_menu_new( "Nsides", menu_pane, 0);
    for (i = 1; i <= 10; i++) {
        sprintf( dum, "%d", i);
        item = ggtk_menu_item_new( dum, menu_pane2, G_CALLBACK( set_marker_nsides), (gpointer)(size_t)i);
    }

    menu_pane2 = ggtk_menu_new( "Mstyle", menu_pane, 0);
    static char *mstyle_list[] = {"Convex polygon","Vertex connected","Starred polygon","Filled convex polygon","Filled starred polygon"};
    for (i = 0; i <= 4; i++) {
        item = ggtk_menu_item_new( mstyle_list[i], menu_pane2, G_CALLBACK( set_marker_mstyle), (gpointer)(size_t)i);
    }

    menu_pane2 = ggtk_menu_new( "Size", menu_pane, 0);
    for (i = 1; i <= 10; i++) {
        sprintf( dum, "%0.1f cm", i*0.2);
        item = ggtk_menu_item_new( dum, menu_pane2, G_CALLBACK( set_marker_size), (gpointer)(size_t)i);
    }

    menu_pane2 = ggtk_menu_new( "Orientation", menu_pane, 0);
    for (i = 0; i <= 12; i++) {
        sprintf( dum, "%d degrees", i*15);
        item = ggtk_menu_item_new( dum, menu_pane2, G_CALLBACK( set_marker_orientation), (gpointer)(size_t)i);
    }

    ggtk_menu_item_on_menu_bar_new( "Hardcopy", menu_bar, G_CALLBACK(
     ggtk_hardcopy), (gpointer)env);
    ggtk_menu_item_on_menu_bar_new( "Draw", menu_bar, G_CALLBACK(
     send_command), (gpointer)"DRAW");
    ggtk_menu_item_on_menu_bar_new( "Clear", menu_bar, G_CALLBACK(
     ggtk_clear), (gpointer)env);
    ggtk_menu_item_on_menu_bar_new( "Edit Lut", menu_bar, G_CALLBACK(
     ggtk_edit_lut), (gpointer)env);
     // S.Guilloteau
    ggtk_menu_item_on_menu_bar_new( "Continue", menu_bar, G_CALLBACK(
     ggtk_continue), (gpointer)env);
}

void ggtk_attach_window_genv(ggtk_env_t *env, GtkWidget *window, GtkWidget
 *drawing_area, gboolean connect_expose)
{
    _gtk_nb_windows++;
    //printf( "gtk-toolbar: ggtk_attach_window_genv: %d\n", _gtk_nb_windows);
    env->main_widget = window;
    env->drawing_area = drawing_area;
    env->win_graph = drawing_area->window;
    //env->gc = drawing_area->style->fg_gc[GTK_STATE_NORMAL];
#ifdef GAG_USE_CAIRO
    env->cr = gdk_cairo_create( drawing_area->window);
#else
    env->gc = gdk_gc_new( drawing_area->window);
#endif

    //g_object_set_data( G_OBJECT(drawing_area), "ENV", gargs->env);
    if (connect_expose) {
        g_signal_connect( G_OBJECT(drawing_area), "expose_event",
         G_CALLBACK(expose_event_callback), env);
    }
    g_signal_connect( G_OBJECT(drawing_area), "configure_event",
     G_CALLBACK(configure_event_callback), env);
    g_signal_connect( G_OBJECT(drawing_area), "button_press_event",
     G_CALLBACK(button_press_callback), env);
    g_signal_connect( G_OBJECT(drawing_area), "button_release_event",
     G_CALLBACK(button_release_callback), env);
    g_signal_connect( G_OBJECT(drawing_area), "motion_notify_event",
     G_CALLBACK(motion_notify_callback), env);
    g_signal_connect( G_OBJECT(drawing_area), "scroll_event",
     G_CALLBACK(scroll_notify_callback), env);
    g_signal_connect( G_OBJECT(drawing_area), "key_press_event",
     G_CALLBACK(key_press_callback), env);
    g_signal_connect( G_OBJECT(drawing_area), "key_release_event",
     G_CALLBACK(key_press_callback), env);
    g_signal_connect( G_OBJECT(drawing_area), "focus_in_event",
     G_CALLBACK(noop_focus_callback), env);
    g_signal_connect( G_OBJECT(drawing_area), "focus_out_event",
     G_CALLBACK(noop_focus_callback), env);

    g_signal_connect( G_OBJECT(window), "delete_event",
     G_CALLBACK(delete_event_callback), env);
    g_signal_connect( G_OBJECT (window), "destroy",
     G_CALLBACK (destroy_event_callback), env);
}

void ggtk_create_drawing_area(gtv_toolbar_args_t *args)
{
    ggtk_toolbar_args_t *gargs = (ggtk_toolbar_args_t *)args;
    GtkWidget *window;
    GtkWidget *main_box;
    GtkWidget *menu_bar;
    GtkWidget *drawing_area;

    /* Create a new window */
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL);
    gtk_window_set_focus_on_map( GTK_WINDOW(window), FALSE);
#if 0 && !defined(darwin)
    /* menu don't work on mac with this setting */
    gtk_window_set_accept_focus( GTK_WINDOW(window), FALSE);
#endif

    /* Set the window title */
    gtk_window_set_title( GTK_WINDOW(window), gargs->window_name);

    main_box = gtk_vbox_new( FALSE, 0);
    gtk_container_add( GTK_CONTAINER(window), main_box);

#ifndef GGTK_USE_MENU_BAR
    menu_bar = gtk_toolbar_new( );
    gtk_toolbar_set_style( GTK_TOOLBAR(menu_bar), GTK_TOOLBAR_TEXT);
#else
    menu_bar = gtk_menu_bar_new( );
#endif
    if (menu_bar != NULL)
        gtk_box_pack_start( GTK_BOX(main_box), menu_bar, FALSE, FALSE, 2);

    /* Sometimes GtkImage is a useful alternative to a drawing area. You can
     * put a GdkPixmap in the GtkImage and draw to the GdkPixmap, calling
     * gtk_widget_queue_draw() on the GtkImage when you want to refresh to the
     * screen. */
    drawing_area = gtk_drawing_area_new();
    if (gargs->env->herit.win_backg)
        gtk_widget_modify_bg( drawing_area, GTK_STATE_NORMAL, &ggtk_white);
    else
        gtk_widget_modify_bg( drawing_area, GTK_STATE_NORMAL, &ggtk_black);
    //gtk_widget_set_size_request( drawing_area, args->win_width, args->win_height);
    gtk_window_set_default_size( GTK_WINDOW(window), args->win_width,
     args->win_height);
    gtk_box_pack_start( GTK_BOX(main_box), drawing_area, TRUE, TRUE, 0);
    //gtk_widget_set_double_buffered(drawing_area, FALSE);
    gtk_widget_set_events( drawing_area,
        //GDK_BUTTON1_MOTION_MASK // only button 1
        GDK_BUTTON_MOTION_MASK
        //| GDK_POINTER_MOTION_MASK
        | GDK_BUTTON_PRESS_MASK
        | GDK_BUTTON_RELEASE_MASK
        | GDK_KEY_PRESS_MASK
        | GDK_KEY_RELEASE_MASK
        );
    GTK_WIDGET_SET_FLAGS( drawing_area, GTK_CAN_FOCUS);
    //gtk_widget_set_can_focus( drawing_area, TRUE);

    // make window of widget available for pixmaps
    gtk_widget_show_all( window);
    _fill_menu_bar(menu_bar, GDK_DRAWABLE(GTK_WIDGET(window)->window),
     gargs->env);
#ifndef GGTK_USE_MENU_BAR
    //gtk_widget_show_all( menu_bar);
#endif

    ggtk_attach_window_genv(gargs->env, window, drawing_area, TRUE);

    gtk_widget_show_all( window);
    sic_post_widget_created();

    //while (gtk_events_pending())
	//    gtk_main_iteration();
    //gdk_window_process_updates( drawing_area->window, TRUE);
}

int run_gtk_main_loop( void *data)
{
    static int active = 0;

    void ggtk_init();
    ggtk_init();

    //gdk_display_flush( gdk_display_get_default());
    //sic_post_widget_created();

    if (active)
        return 1;

    active = 1;
    gdk_threads_enter( );
    gtk_main( );
    gdk_threads_leave( );
    active = 0;

    return 0;
}

static void send_lut_to_main_task( )
{
}

void ggtk_env_change_win( ggtk_env_t *genv, GdkWindow *window)
{
#ifdef GAG_USE_CAIRO
    cairo_destroy( genv->cr);
#else
#endif
    genv->win_graph = window;
#ifdef GAG_USE_CAIRO
    genv->cr = gdk_cairo_create( window);
#else
    //env->gc = gdk_gc_new( genv->win_graph);
#endif
}

