
#include "gtk-local.h"

#include "magnify-tool.h"

#include "gtk-toolbar.h"
#include "gtk-graph.h"
#include <gdk/gdkkeysyms.h>

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

static int _magnify_programmatically = 0;
static GtkWidget *_magnify_window = NULL;
#ifdef GAG_USE_CAIRO
    static cairo_t *_magnify_cr = NULL;
#else
    static GdkGC *_magnify_gc = NULL;
#endif
static G_env *_magnify_genv = NULL;
static gint _magnify_x = 0;
static gint _magnify_y = 0;
static gint _magnify_x_offset = 0;
static gint _magnify_y_offset = 0;
static gint _magnify_width = 400;
static gint _magnify_height = 400;
#define GTV_MAGNIFY_WINDOW_SIZE_INCR 30
static float _magnify_window_size_factor = 1.2;
static float _magnify_zoom_factor = 5.0;
static int _magnify_pin_window = 0;
const int _magnify_grab_pointer = 1; /* enables to release the mouse button */
#undef LIMIT_GRAB_POINTER_TO_WINDOW

static void _magnify_reset_zoom()
{
    _magnify_zoom_factor = 1.0;
    _magnify_x_offset = 0;
    _magnify_y_offset = 0;
}

static void _magnify_zoom()
{
    _magnify_zoom_factor *= 1.5;
    if (_magnify_zoom_factor > 1e10)
        _magnify_zoom_factor = 1e10;
}

static void _magnify_unzoom()
{
    _magnify_zoom_factor /= 1.5;
    if (_magnify_zoom_factor < 1.0)
        _magnify_zoom_factor = 1.0;
}

static void _move_origin( ggtk_env_t *genv, gint _x, gint _y, gint
 _x_root, gint _y_root)
{
    if (_magnify_grab_pointer) {
        gint x;
        gint y;
        gdk_window_get_origin( genv->win_graph, &x, &y);
        _magnify_x = _x_root - x;
        _magnify_y = _y_root - y;
    } else {
        _magnify_x = _x;
        _magnify_y = _y;
    }
}

static void _move_magnify_offset( ggtk_env_t *genv, gint _x, gint _y, gint
 _x_root, gint _y_root)
{
    int ox = _magnify_x;
    int oy = _magnify_y;
    _move_origin( genv, _x, _y, _x_root, _y_root);
    _magnify_x_offset -= _magnify_x - ox;
    _magnify_y_offset -= _magnify_y - oy;
}

static void _magnify_flip_pin_window()
{
    _magnify_pin_window = 1 - _magnify_pin_window;
}

static void _move_magnify_window( ggtk_env_t *genv, gint _x, gint _y, gint
 _x_root, gint _y_root)
{
    _move_origin( genv, _x, _y, _x_root, _y_root);
    if (!_magnify_pin_window) {
        gdk_window_move( _magnify_window->window, _x_root - _magnify_width / 2 +
         _magnify_x_offset, _y_root - _magnify_height / 2 + _magnify_y_offset);
    }
}

static void _draw_magnify_window( G_env *env)
{
    //printf("_draw_magnify_window: %d %d\n", _magnify_x, _magnify_y);
#ifdef GAG_USE_CAIRO
    if (gtv_lens_limits( env, _magnify_genv, _magnify_x, _magnify_y,
     _magnify_zoom_factor))
        // error with too big zoom factor
        _magnify_unzoom();
    gtv_refresh( _magnify_genv, GTV_REFRESH_MODE_UPDATE);
//    cairo_rectangle (_magnify_cr, 0, 0, _magnify_width - 1, _magnify_height - 1);
//    cairo_paint (_magnify_cr);
#else
    GdkRectangle rect = { 0, 0, _magnify_width, _magnify_height};
    gdk_window_begin_paint_rect(_magnify_window->window, &rect);

    if (gtv_lens_limits( env, _magnify_genv, _magnify_x, _magnify_y,
     _magnify_zoom_factor))
        // error with too big zoom factor
        _magnify_unzoom();
    gtv_refresh( _magnify_genv, GTV_REFRESH_MODE_UPDATE);


    gdk_draw_rectangle(_magnify_window->window, _magnify_gc, FALSE,
     0, 0, _magnify_width - 1, _magnify_height - 1);
    gdk_window_end_paint(_magnify_window->window);
#endif
}

static void _destroy_magnify_window( guint32 time)
{
    if (gdk_pointer_is_grabbed()) {
        gdk_pointer_ungrab( time);
    }
    if (_magnify_window != NULL) {
        gtk_widget_destroy( _magnify_window);
    }
    if (_magnify_programmatically) {
        sic_post_widget_created();
    }
}

static gboolean _magnify_expose( GtkWidget *widget, GdkEventExpose *event,
 gpointer data)
{
    if (_magnify_window != NULL)
        _draw_magnify_window( (G_env *)data);
    return FALSE;
}

static void _magnify_destroy( GtkWidget *widget, gpointer data)
{
    if (gdk_pointer_is_grabbed()) {
        gdk_pointer_ungrab( GDK_CURRENT_TIME);
    }
#ifdef GAG_USE_CAIRO
    g_object_unref( _magnify_cr);
#else
    g_object_unref( _magnify_gc);
#endif
    _magnify_window = NULL;
    /* deactivate magnify */
    ggtk_set_pointer_event_handler( NULL, NULL);
}

static void _magnify_update_window_size( ggtk_env_t *genv, gdouble x, gdouble y,
 gdouble x_root, gdouble y_root)
{
    gtk_window_resize( GTK_WINDOW(_magnify_window), _magnify_width,
     _magnify_height);
    _move_magnify_window( genv, x, y, x_root, y_root);
    gtv_on_resize( _magnify_genv, _magnify_width, _magnify_height);
    gtk_widget_queue_draw( _magnify_window);
}

static void _magnify_increase_window_size()
{
    _magnify_width = (gint)(_magnify_width *
     _magnify_window_size_factor);
    _magnify_height = (gint)(_magnify_height *
     _magnify_window_size_factor);
}

static void _magnify_decrease_window_size()
{
    if (_magnify_width > 10 && _magnify_height > 10) {
        _magnify_width = (gint)(_magnify_width /
         _magnify_window_size_factor);
        _magnify_height = (gint)(_magnify_height /
         _magnify_window_size_factor);
    }
}

static void _create_magnify_window( G_env *data)
{
    _magnify_pin_window = 0;
    _magnify_window = gtk_window_new(GTK_WINDOW_POPUP);
    gtk_window_set_decorated( GTK_WINDOW(_magnify_window), FALSE);
    gtk_window_set_position( GTK_WINDOW(_magnify_window),
     GTK_WIN_POS_MOUSE);
    gtk_window_set_default_size( GTK_WINDOW(_magnify_window),
     _magnify_width, _magnify_height);
    g_signal_connect( G_OBJECT(_magnify_window), "expose_event",
     G_CALLBACK(_magnify_expose), data);
    g_signal_connect( G_OBJECT(_magnify_window), "destroy",
     G_CALLBACK (_magnify_destroy), data);
    gtk_widget_show_all( _magnify_window);
#ifdef GAG_USE_CAIRO
    gtk_widget_set_app_paintable(_magnify_window, TRUE);
    gtk_widget_set_double_buffered(_magnify_window, TRUE);
    _magnify_cr = gdk_cairo_create( _magnify_window->window);
#else
    _magnify_gc = gdk_gc_new( _magnify_window->window);
    if (data->win_backg) {
        gtk_widget_modify_bg( _magnify_window, GTK_STATE_NORMAL,
         &ggtk_white);
        gdk_gc_set_rgb_fg_color( _magnify_gc, &ggtk_black);
    } else {
        gtk_widget_modify_bg( _magnify_window, GTK_STATE_NORMAL,
         &ggtk_black);
        gdk_gc_set_rgb_fg_color( _magnify_gc, &ggtk_white);
    }
#endif
    _magnify_genv = ggtk_new_genv_from( data, _magnify_window,
     _magnify_width, _magnify_height, FALSE);
    gtv_lens_register( data, _magnify_genv);
}

static int _magnify_handler( GdkEvent *event, gpointer data, gpointer user_data)
{
    if (event == NULL || event->type == GDK_BUTTON_PRESS) {
        GdkEventButton *e = (GdkEventButton *)event;
        if (_magnify_window != NULL) {
            if (e == NULL ) {
            } else if (e->button == 1) {
                _magnify_decrease_window_size();
                _magnify_update_window_size( (ggtk_env_t *)data, e->x, e->y,
                 e->x_root, e->y_root);
            } else if (e->button == 3) {
                _magnify_increase_window_size();
                _magnify_update_window_size( (ggtk_env_t *)data, e->x, e->y,
                 e->x_root, e->y_root);
            } else if (_magnify_grab_pointer) {
                _destroy_magnify_window( e->time);
            }
        } else if (e == NULL || e->button == 2) {
            GdkWindow *window;
            _create_magnify_window( (G_env *)data);
            if (e != NULL) {
                window = e->window;
                _move_magnify_window( (ggtk_env_t *)data, e->x, e->y, e->x_root, e->y_root);
            } else {
                window = ((ggtk_env_t *)data)->win_graph;
            }
            if (_magnify_grab_pointer) {
                GdkGrabStatus status;
                status = gdk_pointer_grab( window, TRUE,
                 GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK,
#ifndef LIMIT_GRAB_POINTER_TO_WINDOW
                 NULL,
#else
                 window,
#endif
                 NULL, e != NULL ? e->time : GDK_CURRENT_TIME);
                if (status != GDK_GRAB_SUCCESS) {
                    fprintf( stderr, "gdk_pointer_grab error\n");
                } else {
                    if (gdk_keyboard_grab(_magnify_window->window, FALSE, GDK_CURRENT_TIME) !=
                     GDK_GRAB_SUCCESS)
                        fprintf( stderr, "gdk_keyboard_grab error\n");
                }
            }
        }
        return 1;
    } else if (_magnify_window == NULL) {
    } else if (event->type == GDK_BUTTON_RELEASE) {
        if (!_magnify_grab_pointer) {
            _destroy_magnify_window( ((GdkEventButton *)event)->time);
            return 1;
        }
    } else if (event->type == GDK_MOTION_NOTIFY) {
        GdkEventMotion *e = (GdkEventMotion *)event;
        if (e->state & GDK_CONTROL_MASK) {
            _move_magnify_offset( (ggtk_env_t *)data, e->x, e->y, e->x_root, e->y_root);
        } else {
            _move_magnify_window( (ggtk_env_t *)data, e->x, e->y, e->x_root, e->y_root);
        }
        gtk_widget_queue_draw( _magnify_window);
        return 1;
    } else if (event->type == GDK_SCROLL) {
        GdkEventScroll *e = (GdkEventScroll *)event;
        if (e->state & GDK_CONTROL_MASK) {
            if (e->direction == GDK_SCROLL_DOWN || e->direction ==
             GDK_SCROLL_RIGHT) {
                _magnify_decrease_window_size();
                _magnify_update_window_size( (ggtk_env_t *)data, e->x, e->y,
                 e->x_root, e->y_root);
            } else if (e->direction == GDK_SCROLL_UP || e->direction ==
             GDK_SCROLL_LEFT) {
                _magnify_increase_window_size();
                _magnify_update_window_size( (ggtk_env_t *)data, e->x, e->y,
                 e->x_root, e->y_root);
            }
        } else {
            if (e->direction == GDK_SCROLL_DOWN) {
                _magnify_unzoom();
            } else if (e->direction == GDK_SCROLL_UP) {
                _magnify_zoom();
            }
            gtk_widget_queue_draw( _magnify_window);
        }
        return 1;
    } else if (event->type == GDK_KEY_PRESS) {
        GdkEventKey *e = (GdkEventKey *)event;

        if (e->keyval == GDK_E || e->keyval == GDK_e) {
            _destroy_magnify_window( e->time);
        } else if (e->keyval == GDK_Shift_L || e->keyval == GDK_Shift_R) {
            _magnify_flip_pin_window();
        } else {
            if (e->keyval == GDK_0)
                _magnify_reset_zoom();
            else if (e->keyval == GDK_plus)
                _magnify_zoom();
            else if (e->keyval == GDK_minus)
                _magnify_unzoom();
            gtk_widget_queue_draw( _magnify_window);
        }
        return 1;
    }
    return 0;
}

void ggtk_activate_magnify( GdkEventButton *event, gpointer data)
{
    _magnify_programmatically = (event == NULL);
    ggtk_set_pointer_event_handler( _magnify_handler, NULL);
    ggtk_call_pointer_event_handler( (GdkEvent *)event, data);
}

