
#include "gtk-local.h"

#include "gtk-hsvcontrol.h"

#include "gtk-graph.h"
#include "message-c.h"
#include "gcore/rgb_hsv.h"
#include "gsys/sic_util.h"
#include "gtv/xsub.h"
#include "gtv/event-stack.h"
#include <stdlib.h>
#include <math.h>
#ifndef WIN32
#include <dirent.h>
#endif
#include <gtk/gtk.h>

#define _GGTK_USE_DIALOG

typedef enum {
    HSV_HUE, HSV_SAT, HSV_VAL, HSV_LUT
} hsv_drawing_cell_id;
#define _HSV_DRAWING_WIDTH 128
#define _HSV_DRAWING_HEIGHT 128
#define _HSV_LUT_HEIGHT 32
#define MAX_INTENSITY 0xFFFF           /* according to X... */

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

typedef struct {
    int size;
    GdkPoint *hue_points;
    GdkPoint *sat_points;
    GdkPoint *val_points;
    float *hue;
    float *sat;
    float *val;
    float *red;
    float *green;
    float *blue;
    float lowbound;
    float highbound;
} _hsv_lut;

typedef struct {
    GtkWidget *main_window;
    GtkWidget *table;
    GtkWidget *hue_drawing_area;
    GtkWidget *lut_drawing_area;
    _hsv_lut *lut;
    G_env *genv;
} _hsv_context;

static int _scale(int value, int src_dim, int dest_dim)
{
    return (int)((float)value / (src_dim - 1) * (dest_dim - 1) + 0.5);
}

static void _hsv_redraw(_hsv_context *context)
{
    gtk_widget_queue_draw( context->main_window);
}

static void _hsv_redraw_lut(_hsv_context *context)
{
    gtk_widget_queue_draw( context->lut_drawing_area);
}

static void _hsv_redraw_hue(_hsv_context *context)
{
    gtk_widget_queue_draw( context->hue_drawing_area);
}

static void _hsv_on_change_bounds( _hsv_context *context)
{
    _hsv_lut *lut = context->lut;
    int i;
    float l;

    l = lut->size - 1.;
    for (i = 0; i < lut->size; i++) {
        lut->hue[i] = lut->lowbound + i / l * (lut->highbound - lut->lowbound);
        lut->hue[i] = fmodf(lut->hue[i], 360.);
        lut->hue_points[i].y = (l - 1.) * (1. - lut->hue[i] / 360.);
        hsv_to_rgb( &lut->hue[i], &lut->sat[i], &lut->val[i], &lut->red[i],
         &lut->green[i], &lut->blue[i]);
    }
    if (context->genv != NULL && context->genv->update_gtv_lut) {
        gtv_lut_fromeditor(lut->red, lut->green, lut->blue, lut->size);
    }
}

static void _hsv_lut_alloc(_hsv_lut *lut, int size)
{
    lut->size = size;
    lut->hue_points = calloc(size, sizeof(GdkPoint));
    lut->sat_points = calloc(size, sizeof(GdkPoint));
    lut->val_points = calloc(size, sizeof(GdkPoint));
    lut->hue   = calloc(size, sizeof(float));
    lut->sat   = calloc(size, sizeof(float));
    lut->val   = calloc(size, sizeof(float));
    lut->red   = calloc(size, sizeof(float));
    lut->green = calloc(size, sizeof(float));
    lut->blue  = calloc(size, sizeof(float));
    lut->lowbound = 0;
    lut->highbound = 360.;
}

static void _hsv_update_rgb(hsv_drawing_cell_id drawing_cell, _hsv_context
 *context, float coef, float offset)
{
    _hsv_lut *lut = context->lut;
    int i;
    float f;
    float l;

    l = lut->size - 1.;
    for (i = 0; i < lut->size; i++) {
        f = coef * i / l + offset;

        if (f < 0.)
            f = 0.;
        else if (f > 1.)
            f = 1.;

        switch (drawing_cell) {
        case HSV_HUE:
            lut->hue_points[i].y = (l - 1.) * (1. - f);
            lut->hue[i] = lut->lowbound + f * (lut->highbound - lut->lowbound);
            break;
        case HSV_SAT:
            lut->sat_points[i].y = (l - 1.) * (1. - f);
            lut->sat[i] = f;
            break;
        case HSV_VAL:
            lut->val_points[i].y = (l - 1.) * (1. - f);
            lut->val[i] = f;
            break;
        }
        hsv_to_rgb( &lut->hue[i], &lut->sat[i], &lut->val[i], &lut->red[i],
         &lut->green[i], &lut->blue[i]);
    }
    if (context->genv != NULL && context->genv->update_gtv_lut) {
        gtv_lut_fromeditor(lut->red, lut->green, lut->blue, lut->size);
    }
}

static void _hsv_update_from_rgb(_hsv_lut *lut)
{
    int i;
    float l;

    l = lut->size - 1.;
    for (i = 0; i < lut->size; i++) {
        rgb_to_hsv( &lut->red[i], &lut->green[i], &lut->blue[i], &lut->hue[i],
         &lut->sat[i], &lut->val[i]);
        lut->hue_points[i].y = (l - 1.) * (1. - lut->hue[i] / 360.);
        lut->sat_points[i].y = (l - 1.) * (1. - lut->sat[i]);
        lut->val_points[i].y = (l - 1.) * (1. - lut->val[i]);
    }
}

int _load_default_colormap(_hsv_lut *lut)
{
    int i;
    GdkColor *default_colormap;
    int default_colormap_size;

    default_colormap_size = ggtk_default_colormap_size();
    if (lut == NULL) {
        return default_colormap_size;
    }
    default_colormap = ggtk_default_colormap();
    for (i = 0; i < lut->size; i++) {
        int j = _scale(i, lut->size, default_colormap_size);
        lut->red[i] = (float)default_colormap[j].red / MAX_INTENSITY;
        lut->green[i] = (float)default_colormap[j].green / MAX_INTENSITY;
        lut->blue[i] = (float)default_colormap[j].blue / MAX_INTENSITY;
    }
    return lut->size;
}

static void _hsv_lut_init(_hsv_lut *lut)
{
    int i;
#ifndef _RESET_ON_INIT
    _load_default_colormap(lut);
    _hsv_update_from_rgb(lut);
#else
    float l;
    float t;

    l = lut->size - 1.;
#endif
    for (i = 0; i < lut->size; i++) {
        lut->hue_points[i].x = i;
        lut->sat_points[i].x = i;
        lut->val_points[i].x = i;

#ifndef _RESET_ON_INIT
    }
#else
        t = i / l;

        //hp[i] = t;
        lut->hue_points[i].y = (l - 1.) * (1. - t);
        lut->sat_points[i].y = 0;
        lut->val_points[i].y = 0;

        lut->hue[i] = 360. * t;
        lut->val[i] = 1.;
        lut->sat[i] = 1.;
    }
    for (i = 0; i < lut->size; i++) {
        hsv_to_rgb( &lut->hue[i], &lut->sat[i], &lut->val[i], &lut->red[i],
         &lut->green[i], &lut->blue[i]);
    }
#endif
}

static void _hsv_draw_wedge( GtkWidget *widget, _hsv_context *context)
{
    _hsv_lut *lut = context->lut;
    GdkWindow *window;
    int width;
    int height;
    int i, j, k;
#ifdef GAG_USE_CAIRO
    cairo_t *cr;
    cairo_surface_t *s;
    cairo_format_t format;
    int stride;
    unsigned char *cbuf;
#else
    GdkGC *gc;
    GdkVisual *visual;
    guchar *buf;
    guchar *pos;
#endif
    window = widget->window;
    width = widget->allocation.width;
    height = widget->allocation.height;
 
#ifdef GAG_USE_CAIRO
    cr = gdk_cairo_create(widget->window);
    format = CAIRO_FORMAT_RGB24;
    stride = cairo_format_stride_for_width(format, width);
    if (stride <= 0) {
        ggtk_c_message(seve_e,"GTK","Invalid stride");
        return;
    }
    cbuf = malloc(stride * height);
    k = 0;
    for (j = 0; j < height; j++) {
        for (i = 0; i < width; i++) {
            k = _scale(i, width, lut->size);
            cbuf[j*stride+i*4] = (unsigned char)((gushort)(lut->blue[k] * MAX_INTENSITY) >> 8);
            cbuf[j*stride+i*4+1] = (unsigned char)((gushort)(lut->green[k] * MAX_INTENSITY) >> 8);
            cbuf[j*stride+i*4+2] = (unsigned char)((gushort)(lut->red[k] * MAX_INTENSITY) >> 8);
            cbuf[j*stride+i*4+3] = 123; // alpha channel. unused with CAIRO_FORMAT_RGB24.
        }
    }
    s = cairo_image_surface_create_for_data( cbuf, format, width, height, stride);
    cairo_set_source_surface(cr, s, 0, 0);
    cairo_paint(cr);
    free(cbuf);
    cairo_destroy(cr);
#else
    gc = gdk_gc_new( window);
    visual = gdk_drawable_get_visual( window);
    pos = buf = malloc(width * height * 3 * sizeof(gushort));
    for (j = 0; j < height; j++) {
    for (i = 0; i < width; i++) {
        k = _scale(i, width, lut->size);
        gushort red = (gushort)(lut->red[k] * MAX_INTENSITY)
         >> (16 - visual->red_prec);
        gushort green = (gushort)(lut->green[k] * MAX_INTENSITY)
         >> (16 - visual->green_prec);
        gushort blue = (gushort)(lut->blue[k] * MAX_INTENSITY)
         >> (16 - visual->blue_prec);
            *pos++ = red;
            *pos++ = green;
            *pos++ = blue;
        }
    }
    gdk_draw_rgb_image( window, gc, 0, 0, width, height, GDK_RGB_DITHER_NONE,
     buf, width * 3);
    g_object_unref(gc);
    free( buf);
#endif
}

static void _draw_lines(GtkWidget *widget, GdkPoint *points, int size)
{
    GdkPoint new;
    GdkPoint last;
    int i;
    int width;
    int height;

#ifdef GAG_USE_CAIRO
    cairo_t *cr = gdk_cairo_create(widget->window);
    cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
    cairo_move_to(cr, 0.0, 0.0);
#else
    GdkGC *gc = gdk_gc_new( widget->window);
    gdk_gc_set_rgb_fg_color( gc, &ggtk_white);
#endif
    width = widget->allocation.width;
    height = widget->allocation.height;
    new.x = 0;
    new.y = 0;
    for (i = 0; i < size; i++) {
        last = new;
        new.x = _scale(points[i].x, size, width);
        new.y = _scale(points[i].y, size, height);
        if (i)
#ifdef GAG_USE_CAIRO
            cairo_line_to(cr, new.x, new.y);
    }
    cairo_stroke(cr);
    cairo_destroy(cr);
#else
            gdk_draw_line(widget->window, gc, last.x, last.y, new.x, new.y);
    }
    g_object_unref(gc);
#endif
}

static gboolean _hsv_expose_event_callback( GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
    _hsv_context *context = (_hsv_context *)g_object_get_data(G_OBJECT(widget),
     "CONTEXT");
    hsv_drawing_cell_id id = (hsv_drawing_cell_id)data;

    switch (id) {
    case HSV_HUE:
        _draw_lines(widget, context->lut->hue_points, context->lut->size);
        break;
    case HSV_SAT:
        _draw_lines(widget, context->lut->sat_points, context->lut->size);
        break;
    case HSV_VAL:
        _draw_lines(widget, context->lut->val_points, context->lut->size);
        break;
    case HSV_LUT:
        _hsv_draw_wedge(widget, context);
        break;
    }
    return FALSE; // don't continue propagation
}

static void _hsv_color_input(GtkWidget *widget, int cell_id, int button, int x, int y)
{
    static int reverse = 0;
    _hsv_context *context = (_hsv_context *)g_object_get_data(G_OBJECT(widget),
     "CONTEXT");
    float i;
    float coef, offset;
    int width;
    int height;

    width = widget->allocation.width;
    height = widget->allocation.height;
    if (button) {
        if (button == 1)
            reverse = 0;
        else
            reverse = 1;
    }

    if (x < 0 || y < 0 || x > width - 1 || y > height - 1)
        return;

    i = (float)x / width;

    if (!reverse) {
        coef = i / (1. - i);
        offset = (coef + 1.) * (float)(height - 1 - y) / (height - 1) - coef;
    } else {
        coef = i / (i - 1.);
        offset = (1. - coef) * (float)(height - 1 - y) / (height - 1);
    }

    _hsv_update_rgb( cell_id, context, coef, offset);
    _hsv_redraw_lut(context);
}

static gboolean _hsv_button_press_callback( GtkWidget *widget, GdkEventButton *event, gpointer data)
{
    hsv_drawing_cell_id id = (hsv_drawing_cell_id)data;

    //printf("button_press_callback: %d\n", event->button);
    _hsv_color_input(widget, id, event->button, event->x, event->y);
    gtk_widget_queue_draw( widget);

    return TRUE; // don't continue propagation
}

static gboolean _hsv_motion_notify_callback( GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
    hsv_drawing_cell_id id = (hsv_drawing_cell_id)data;

    //printf("motion_notify_callback\n");
    _hsv_color_input(widget, id, 0, event->x, event->y);
    gtk_widget_queue_draw( widget);

    return TRUE; // don't continue propagation
}

static GtkWidget *_hsv_create_drawing_area(hsv_drawing_cell_id id, _hsv_context
 *context, int col_start, int col_end, int row, int width, int height, gboolean
 vexpand)
{
    GtkWidget *drawing_area;

    drawing_area = gtk_drawing_area_new();
    gtk_widget_modify_bg( drawing_area, GTK_STATE_NORMAL, &ggtk_black);
    gtk_widget_set_size_request( drawing_area, width, height);
    gtk_table_attach( GTK_TABLE(context->table), drawing_area, col_start,
     col_end + 1, row, row + 1, GTK_EXPAND | GTK_FILL, vexpand ? GTK_EXPAND |
     GTK_FILL : GTK_FILL, 2, 2);
    gtk_widget_add_events( drawing_area,
        GDK_BUTTON1_MOTION_MASK // button 1
        | GDK_BUTTON2_MOTION_MASK // button 2
        //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_can_focus( drawing_area, TRUE);
    GTK_WIDGET_SET_FLAGS( drawing_area, GTK_CAN_FOCUS);
    g_signal_connect( G_OBJECT(drawing_area), "expose_event",
     G_CALLBACK(_hsv_expose_event_callback), (void *)id);
    g_signal_connect( G_OBJECT(drawing_area), "button_press_event",
     G_CALLBACK(_hsv_button_press_callback), (void *)id);
    g_signal_connect( G_OBJECT(drawing_area), "motion_notify_event",
     G_CALLBACK(_hsv_motion_notify_callback), (void *)id);
    g_object_set_data(G_OBJECT(drawing_area), "CONTEXT", context);

    return drawing_area;
}

static GtkWidget *_hsv_create_square_drawing_area(hsv_drawing_cell_id id,
 _hsv_context *context, int col, int row)
{
    return _hsv_create_drawing_area(id, context, col, col, row,
     _HSV_DRAWING_WIDTH, _HSV_DRAWING_HEIGHT, TRUE);
}

static void _hsv_slider_changed_value( GtkRange *range, void *data)
{
    float v = gtk_range_get_value( range);
    _hsv_context *context = (_hsv_context *)g_object_get_data(G_OBJECT(range),
     "CONTEXT");

    switch ((size_t)data) {
    case 1:
        context->lut->lowbound = v;
        break;
    case 2:
        context->lut->highbound = v;
        break;
    }
    _hsv_on_change_bounds(context);
    _hsv_redraw_lut(context);
    _hsv_redraw_hue(context);
}

static GtkWidget *_hsv_slider_create(_hsv_context *context, const char *text,
 long id, int value)
{
    GtkWidget *hbox;
    GtkWidget *w;

    hbox = gtk_hbox_new( FALSE, 0);

    w = gtk_label_new( text);
    gtk_box_pack_start( GTK_BOX(hbox), w, FALSE, FALSE, 10);

    w = gtk_hscale_new_with_range( 0, 1800, 1);
    g_object_set_data(G_OBJECT(w), "CONTEXT", context);
    gtk_range_set_value( GTK_RANGE(w), value);
    g_signal_connect( GTK_OBJECT(w), "value-changed",
     G_CALLBACK(_hsv_slider_changed_value), (void *)(size_t)id);
    gtk_box_pack_start( GTK_BOX(hbox), w, TRUE, TRUE, 0);

    return hbox;
}

enum {
    LIST_ITEM = 0,
    PATH,
    N_COLUMNS
};

static void
_init_list (GtkWidget *list)
{

    GtkCellRenderer         *renderer;
    GtkTreeViewColumn       *column;
    GtkListStore            *store;

    /* create and append the single column */

    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes ("Existing luts",
            renderer, "text", LIST_ITEM, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);

    /* create the model and add to tree view */

    store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);

    gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (store));

    /* free reference to the store */

    g_object_unref (store);
}

static void
_add_to_list (GtkWidget *list, const gchar *str, const gchar *path)
{
    GtkListStore            *store;
    GtkTreeIter             iter;

    store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list)));

    gtk_list_store_append (store, &iter);
    gtk_list_store_set (store, &iter, LIST_ITEM, str, PATH, path, -1);
}

static void
_remove_from_list_by_value (GtkWidget *list, const gchar *str)
{
    GtkTreeModel            *model;
    GtkTreeIter             iter;
    gboolean                r;
    gchar                   *value;

    model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));

    /*
    iterate model looking for str and then remove it... only removes the first
    one found.
    */

    if (gtk_tree_model_get_iter_first (model, &iter) == FALSE) return;
    for (r = gtk_tree_model_get_iter_first (model, &iter);
         r == TRUE;
         r = gtk_tree_model_iter_next (model, &iter))
    {
        gtk_tree_model_get (model, &iter, LIST_ITEM, &value,  -1);

        if (g_ascii_strcasecmp(value, str) == 0)
        {
            gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
            break;
        }

        /* free the value once the compare is done */
        if (value) g_free (value);
    }
}

static void
_remove_from_list (GtkWidget *list, gint index)
{
    GtkTreeModel            *model;
    GtkTreeIter             iter;
    gint                    i;

    model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));

    /* iterate model removing by and index (0-based) */

    if (gtk_tree_model_get_iter_first (model, &iter) == FALSE) return;
    for (i=0; i < index; i++)
    {
        /* just do nothing if index is out of range */
        if (gtk_tree_model_iter_next (model, &iter) == FALSE) return;
    }

    gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
}

static void _hsv_get_luts(GtkWidget *list)
{
    char *path;
#ifndef WIN32
    DIR *dirp;
    struct dirent *dp;
#else
    struct _finddata_t lut_file;
    long hFile;
#endif

    /* add predefined lut names */
    _add_to_list(list, "color", NULL);
    _add_to_list(list, "black", NULL);
    _add_to_list(list, "white", NULL);
    _add_to_list(list, "red", NULL);
    _add_to_list(list, "green", NULL);
    _add_to_list(list, "blue", NULL);
    _add_to_list(list, "yellow", NULL);
    _add_to_list(list, "cyan", NULL);
    _add_to_list(list, "magenta", NULL);
    _add_to_list(list, "null", NULL);

    /* add lut file name from gag_lut: */
    path = sic_s_get_logical_path( "gag_lut:");
#ifndef WIN32
    dirp = opendir( path);
    if (dirp != NULL) {
        while ((dp = readdir(dirp)) != NULL) {
            if (dp->d_type == DT_REG) {
                _add_to_list(list, dp->d_name, "gag_lut:");
            }
        }
        (void)closedir(dirp);
    }
#else
    strcat( path, "*.*");
    if( (hFile = _findfirst(path, &lut_file)) != -1L ) {
        _add_to_list(list, lut_file.name, "gag_lut:");
        while(_findnext(hFile, &lut_file) == 0)
            _add_to_list(list, lut_file.name, "gag_lut:");
        _findclose(hFile);
    }
#endif
}

static void _save_lut( _hsv_context *context, const char *filename)
{
    FILE *lut_id;
    int i;

    lut_id = fopen( filename, "w");

    if (lut_id != NULL) {
        for (i = 0; i < context->lut->size; i++)
            fprintf( lut_id, "%f %f %f\n", context->lut->red[i],
             context->lut->green[i], context->lut->blue[i]);

        fclose( lut_id);
    }
}

static void _save_to_file(_hsv_context * context)
{
    GtkWidget *dialog;
    GtkFileFilter* filter;
    GError *error;

    dialog = gtk_file_chooser_dialog_new( "Save file",
     GTK_WINDOW(context->main_window),
     GTK_FILE_CHOOSER_ACTION_SAVE,
     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
     GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
     NULL);

    filter = gtk_file_filter_new();
    gtk_file_filter_set_name( filter, "All files");
    gtk_file_filter_add_pattern( filter, "*");
    gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter);
    filter = gtk_file_filter_new();
    gtk_file_filter_set_name( filter, "Lut file");
    gtk_file_filter_add_pattern( filter, "*.lut");
    gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter);
    gtk_file_chooser_set_filter( GTK_FILE_CHOOSER(dialog), filter);
#if GTK_CHECK_VERSION(2,8,0)
    gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog),
     TRUE);
#endif

    gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER(dialog),
     sic_s_get_logical_path( "gag_lut:"), &error);

    if (gtk_dialog_run( GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
        char filename[SIC_MAX_PATH_LENGTH];
        GPatternSpec *spec;

        strcpy(filename, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
        spec = g_pattern_spec_new("*.*");
        if (!g_pattern_match_string(spec, filename)) {
            strcat( filename, ".lut");
        }
        _save_lut(context, filename);
    }
    gtk_widget_destroy( dialog);
}

#if 0
static void _load_from_file(char *filename)
{
    char *path;
    int i;
    FILE *lut_fp;
    char lutname[SIC_MAX_PATH_LENGTH];

    path = sic_s_get_logical_path( "gag_lut:");
    sprintf( lutname, "%s/%s", path, filename);
    lut_fp = fopen( lutname, "r");

    if (lut_fp != NULL) {
        for (i = 0; i < context->lut->size; i++) {
            if (fscanf( lut_fp, "%f %f %f", &context->lut->red[i],
             &context->lut->green[i], &context->lut->blue[i]) != 3) {
                ggtk_c_message(seve_w, "HSVCONTROL",
                 "Unable to read values in %s", lutname);
                return;
            }
        }
        fclose( lut_fp);

        _hsv_update_from_rgb(context->lut);

        _hsv_redraw(context);
    }
}
#endif

static void _post_silent_command( const char *args, ...)
{
    va_list l;
    char command[MAXBUF];

    va_start( l, args);
    vsprintf( command, args, l);
    if (sic_post_command_text_from( command, SIC_SYNCHRONOUS_MODE) == -1) {
        ggtk_c_message(seve_w, "HSVCONTROL", "Unable to post command %s",
         command);
    }
    va_end( l);
}

static void _hsv_update_after_lut(void *_context)
{
    _hsv_context *context = (_hsv_context *)_context;
    _load_default_colormap( context->lut);
    _hsv_update_from_rgb( context->lut);
    _hsv_redraw( context);
}

static void _hsv_list_cursor_changed(GtkTreeView *tree_view, gpointer user_data)
{
    _hsv_context *context = (_hsv_context *)user_data;
    GtkTreeSelection *sel;
    GtkTreeModel *model;
    GtkTreeIter iter;

    sel = gtk_tree_view_get_selection( tree_view);
    if (gtk_tree_selection_get_selected( sel, &model, &iter)) {
        char filename[SIC_MAX_PATH_LENGTH];
        char *str;

        gtk_tree_model_get( model, &iter, PATH, &str, -1);
        if (str != NULL)
            strcpy( filename, str);
        else
            filename[0] = '\0';
        gtk_tree_model_get( model, &iter, LIST_ITEM, &str, -1);
        strcat( filename, str);
        _post_silent_command( "GTVL\\LUT \"%s\"", filename);
        gtv_push_callback( _hsv_update_after_lut, context); // execute when event stack is empty
    }
}

static void _on_response(GtkDialog *dialog, gint response_id, gpointer
 user_data)
{
    if (response_id < 0) {
        gtk_widget_destroy(GTK_WIDGET(dialog));
        return;
    }
    if (response_id == 1) {
        _save_to_file((_hsv_context *)user_data);
    }
}

void create_hsv_control( G_env *genv)
{
    _hsv_context *context;
    int size;
    GtkWidget *main_window;
    GtkWidget *main_box;
    GtkWidget *table;
    GtkWidget *w;
    GtkWidget *sat_drawing_area;
    GtkWidget *val_drawing_area;
    GtkWidget *sw;
    GtkWidget *list;

    /* Create a context */
    context = malloc(sizeof(_hsv_context));
    context->genv = genv;
#ifdef _GGTK_USE_DEFAULT_SIZE
    size = _load_default_colormap(NULL);
#else
    size = 2048;
#endif
    context->lut = malloc(sizeof(_hsv_lut));
    _hsv_lut_alloc(context->lut, size);
    _hsv_lut_init(context->lut);

    /* Create a new window */
#ifdef _GGTK_USE_DIALOG
    main_window = gtk_dialog_new();
#else
    main_window = gtk_window_new( GTK_WINDOW_TOPLEVEL);
#endif
    context->main_window = main_window;
    g_object_set_data(G_OBJECT(context->main_window), "CONTEXT", context);

    /* Set the window title */
    gtk_window_set_title( GTK_WINDOW(main_window), "HSV Control");

    /* Sets the border width of the window. */
    gtk_container_set_border_width( GTK_CONTAINER(main_window), 1);

#ifdef _GGTK_USE_DIALOG
#if GTK_CHECK_VERSION(2,14,0)
    main_box = gtk_dialog_get_content_area(GTK_DIALOG(main_window));
#else
    main_box = gtk_bin_get_child(GTK_BIN(main_window));
#endif
#else
    main_box = gtk_vbox_new( FALSE, 0);
    gtk_container_add( GTK_CONTAINER(main_window), main_box);
#endif

    table = gtk_table_new( 3, 3, FALSE);
    context->table = table;
    //gtk_container_add( GTK_CONTAINER(main_box), table);
    gtk_box_pack_start( GTK_BOX(main_box), table, TRUE, TRUE, 0);

    w = gtk_label_new( "Hue");
    gtk_table_attach( GTK_TABLE(table), w, 0, 1, 0, 1,
        GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
    w = gtk_label_new( "Saturation");
    gtk_table_attach( GTK_TABLE(table), w, 1, 2, 0, 1,
        GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
    w = gtk_label_new( "Value");
    gtk_table_attach( GTK_TABLE(table), w, 2, 3, 0, 1,
        GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);

    context->hue_drawing_area = _hsv_create_square_drawing_area(HSV_HUE,
     context, 0, 1);
    sat_drawing_area = _hsv_create_square_drawing_area(HSV_SAT, context, 1, 1);
    val_drawing_area = _hsv_create_square_drawing_area(HSV_VAL, context, 2, 1);
    context->lut_drawing_area = _hsv_create_drawing_area(HSV_LUT, context, 0, 2,
     2, _HSV_LUT_HEIGHT, _HSV_LUT_HEIGHT, FALSE);

    w = _hsv_slider_create(context, "low bound", 1, 0);
    //gtk_container_add( GTK_CONTAINER(main_box), w);
    gtk_box_pack_start( GTK_BOX(main_box), w, FALSE, FALSE, 0);

    w = _hsv_slider_create(context, "high bound", 2, 360);
    //gtk_container_add( GTK_CONTAINER(main_box), w);
    gtk_box_pack_start( GTK_BOX(main_box), w, FALSE, FALSE, 0);

    w = gtk_hseparator_new();
    gtk_box_pack_start( GTK_BOX(main_box), w, FALSE, FALSE, 0);

    w = gtk_label_new( "Select existing luts");
    //gtk_container_add( GTK_CONTAINER(main_box), w);
    gtk_box_pack_start( GTK_BOX(main_box), w, FALSE, FALSE, 0);

    sw = gtk_scrolled_window_new(NULL, NULL);
    list = gtk_tree_view_new();

    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    list = gtk_tree_view_new();
    g_signal_connect( G_OBJECT(list), "cursor-changed",
     G_CALLBACK(_hsv_list_cursor_changed), context);

    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
    _init_list(list);
    _hsv_get_luts(list);
    gtk_container_add( GTK_CONTAINER(sw), list);
    //gtk_container_add( GTK_CONTAINER(main_box), sw);
    gtk_box_pack_start( GTK_BOX(main_box), sw, TRUE, TRUE, 0);

#ifdef _GGTK_USE_DIALOG
    gtk_dialog_add_button(GTK_DIALOG(main_window), GTK_STOCK_CLOSE,
     GTK_RESPONSE_CLOSE);
    gtk_dialog_add_button(GTK_DIALOG(main_window), GTK_STOCK_SAVE_AS, 1);
    g_signal_connect(main_window, "response", G_CALLBACK(_on_response),
     context);
#else
    {
    GtkWidget *hbox;

    hbox = gtk_hbox_new( FALSE, 5);
    w = gtk_button_new_with_label("Save as");
    g_signal_connect( G_OBJECT(w), "clicked", G_CALLBACK(_save_to_file),
     (void *)context);
    gtk_box_pack_end( GTK_BOX(hbox), w, FALSE, FALSE, 5);
    w = gtk_button_new_with_label("Close");
    g_signal_connect_swapped( G_OBJECT(w), "clicked",
     G_CALLBACK(gtk_widget_destroy), main_window);
    gtk_box_pack_end( GTK_BOX(hbox), w, FALSE, FALSE, 5);
    gtk_box_pack_start( GTK_BOX(main_box), hbox, FALSE, FALSE, 5);
    }
#endif

    gtk_widget_show_all( main_window);
}

static int _on_create_hsv_control( void *data)
{
    create_hsv_control((G_env *)data);
    // do not call again
    return FALSE;
}

int run_hsv_control( G_env *genv)
{
    // g_idle_add not needed because already in gtk thread
    //g_idle_add(_on_create_hsv_control, (gpointer)genv);
    create_hsv_control( genv);
    return 0;
}

