/** 
 * @defgroup warp warp
 * warp gui routines
 * @{
 */

#include "prefs.h"
#include "main.h"
#include "gui.h"
#include "source.h"
#include "sensor.h"
#include "warp.h"


/************************** GTK-Callbacks **************************/
/**
 * warp-edge coordinate spinbutton changed
 */
gboolean cb_warpX1_changed(GtkWidget * widget)
{
    _VERBOSE2 printf("%s()\n", __func__);
    
    int value = (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));

    if(src_get() && sensor_get() && value > 1) 
    {
        sensor_get()->wrp_x1 = value;
        src_get()->draw_source =
                src_get()->draw_dst = TRUE;
    }
    return TRUE;
}

/**
 * warp-edge coordinate spinbutton changed
 */
gboolean cb_warpY1_changed(GtkWidget * widget)
{
    int value = (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));

    _VERBOSE2 printf("%s()\n", __func__);
    
    if(src_get() && sensor_get() && value > 1) {
        sensor_get()->wrp_y1 = value;
        src_get()->draw_source =
                src_get()->draw_dst = TRUE;
    }
    return TRUE;
}

/**
 * warp-edge coordinate spinbutton changed
 */
gboolean cb_warpX2_changed(GtkWidget * widget)
{
    int value = (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));

    _VERBOSE2 printf("%s()\n", __func__);
    
    if(src_get() && sensor_get() && value > 1) {
        sensor_get()->wrp_x2 = value;
        src_get()->draw_source =
                src_get()->draw_dst = TRUE;
    }
    return TRUE;
}

/**
 * warp-edge coordinate spinbutton changed
 */
gboolean cb_warpY2_changed(GtkWidget * widget)
{
    int value = (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));

    _VERBOSE2 printf("%s()\n", __func__);
    
    if(src_get() && sensor_get() && value > 1) {
        sensor_get()->wrp_y2 = value;
        src_get()->draw_source =
                src_get()->draw_dst = TRUE;
    }
    return TRUE;
}

/**
 * warp-edge coordinate spinbutton changed
 */
gboolean cb_warpX3_changed(GtkWidget * widget)
{
    int value = (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));

    _VERBOSE2 printf("%s()\n", __func__);
    
    if(src_get() && sensor_get() && value > 1) {
        sensor_get()->wrp_x3 = value;
        src_get()->draw_source =
                src_get()->draw_dst = TRUE;
    }
    return TRUE;
}

/**
 * warp-edge coordinate spinbutton changed
 */
gboolean cb_warpY3_changed(GtkWidget * widget)
{
    int value = (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));

    _VERBOSE2 printf("%s()\n", __func__);
    
    if(src_get() && sensor_get() && value > 1) {
        sensor_get()->wrp_y3 = value;
        src_get()->draw_source =
                src_get()->draw_dst = TRUE;
    }
    return TRUE;
}

/**
 * warp-edge coordinate spinbutton changed
 */
gboolean cb_warpX4_changed(GtkWidget * widget)
{
    int value = (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));

    _VERBOSE2 printf("%s()\n", __func__);
    
    if(src_get() && sensor_get() && value > 1) {
        sensor_get()->wrp_x4 = value;
        src_get()->draw_source =
                src_get()->draw_dst = TRUE;
    }
    return TRUE;
}

/**
 * warp-edge coordinate spinbutton changed
 */
gboolean cb_warpY4_changed(GtkWidget * widget)
{
    int value = (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));

    _VERBOSE2 printf("%s()\n", __func__);
    
    if(src_get() && sensor_get() && value > 1) {
        sensor_get()->wrp_y4 = value;
        src_get()->draw_source =
                src_get()->draw_dst = TRUE;
    }
    return TRUE;
}

/************************** Module **************************/
/**
 * scan line, save pixels to buffer and return length 
 */
static int scan_line(unsigned char *src, unsigned char *buffer,
              int src_width, int x1, int y1, int x2, int y2)
{
    int length = 0;
    int x, y;
    int tmp, flag, delta, d_sub, dx, dy, e;
    int step_x, step_y;
    int count;
    
    if(!src || !buffer)
        return 0;
    
    /*
     * default step is 1 
     */
    step_x = step_y = 1;
    /*
     * calc delta 
     */
    dx = x2 - x1;
    dy = y2 - y1;
    if(dx < 0) {
        step_x = -step_x;
        dx = -dx;
    }
    if(dy < 0) {
        step_y = -step_y;
        dy = -dy;
    }

    if(dx > dy) {
        x = x1;
        y = y1;
        e = dx >> 1;
        length = dx;
        d_sub = dx;
        delta = dy;
        flag = TRUE;
    }
    else {
        x = y1;
        y = x1;
        e = dy >> 1;
        length = dy;
        d_sub = dy;
        delta = dx;
        tmp = step_x;
        step_x = step_y;
        step_y = tmp;
        flag = FALSE;
    }

    /*
     * draw loop 
     */
    for(count = 0; count < length; count++) {
        /*
         * save-pixel (x,y)
         */
        if(flag) {
            buffer[count * 3] = src[src_width * 3 * y + x * 3];
            buffer[count * 3 + 1] = src[src_width * 3 * y + x * 3 + 1];
            buffer[count * 3 + 2] = src[src_width * 3 * y + x * 3 + 2];
        }
        else {
            buffer[count * 3] = src[src_width * 3 * x + y * 3];
            buffer[count * 3 + 1] = src[src_width * 3 * x + y * 3 + 1];
            buffer[count * 3 + 2] = src[src_width * 3 * x + y * 3 + 2];
        }

        /*
         * adjust x 
         */
        x += step_x;
        /*
         * adjust error 
         */
        e += delta;
        /*
         * error too large? 
         */
        if(e > d_sub) {
            /*
             * correct error 
             */
            e -= d_sub;
            /*
             * adjust y 
             */
            y += step_y;
        }
    }

    /*
     * amount of pixels in line 
     */
    return length;
}

/**
 * interpolate one line to new length
 */
static void map_line(unsigned char *dst, unsigned char *buffer,
              int buffer_width, int dst_width, int line)
{
    double step, x;
    int count;
    x = 0;
    step = (double) buffer_width / (double) dst_width;
    for(count = 0; count < dst_width; count++) {
        dst[line * 3 * dst_width + count * 3] = buffer[(int) x * 3];
        dst[line * 3 * dst_width + count * 3 + 1] = buffer[(int) x * 3 + 1];
        dst[line * 3 * dst_width + count * 3 + 2] = buffer[(int) x * 3 + 2];
        x += step;
    }
}

/**
 * map 4-point polygon to rectangular dest-buffer 
 */
void warp_image(unsigned char *src, unsigned char *dest,
                int dst_width, int dst_height,
                int x1, int y1, int x2, int y2,
                int x3, int y3, int x4, int y4)
{

    int length;
    double src_x1, src_y1, src_x2, src_y2, line;
    double x_step1, y_step1, x_step2, y_step2;
    unsigned char *linebuffer;
    /*
     * buffer for one line of src-polygon (may not be larger than
     * 2*dst_width) 
     */
    if(!(linebuffer = malloc(2 * dst_width * 3)))
        return;
    src_x1 = x1;
    src_y1 = y1;
    src_x2 = x2;
    src_y2 = y2;
    x_step1 = (double) (x4 - x1) / (double) dst_height;
    y_step1 = (double) (y4 - y1) / (double) dst_height;
    x_step2 = (double) (x3 - x2) / (double) dst_height;
    y_step2 = (double) (y3 - y2) / (double) dst_height;
    if((x_step1 > 0 || y_step1 > 0) && (x_step2 > 0 || y_step2 > 0)) {
        _VERBOSE6 printf("%s(): %d/%d - %d/%d - %d/%d - %d/%d\n",
                         __func__, x1, y1, x2, y2, x3, y3, x4, y4);
        for(line = 0; line < dst_height; line++) {
            length =
                scan_line(src, linebuffer, dst_width, src_x1, src_y1, src_x2,
                          src_y2);
            map_line(dest, linebuffer, length, dst_width, line);
            src_x1 += x_step1;
            src_y1 += y_step1;
            src_x2 += x_step2;
            src_y2 += y_step2;
        }
    }

    free(linebuffer);
}


/**
 * initialize warp-gui
 */
int warp_gui_init()
{
    /*g_signal_connect(G_OBJECT
                     (glade_xml_get_widget
                      (gui_get_xml(), "spinbutton_x1")), "value-changed",
                     G_CALLBACK(cb_warpX1_changed), NULL);
                         
    g_signal_connect(G_OBJECT
                     (glade_xml_get_widget
                      (gui_get_xml(), "spinbutton_y1")), "value-changed",
                     G_CALLBACK(cb_warpY1_changed), NULL);
                     
    g_signal_connect(G_OBJECT
                     (glade_xml_get_widget
                      (gui_get_xml(), "spinbutton_x2")), "value-changed",
                     G_CALLBACK(cb_warpX2_changed), NULL);
                     
    g_signal_connect(G_OBJECT
                     (glade_xml_get_widget
                      (gui_get_xml(), "spinbutton_y2")), "value-changed",
                     G_CALLBACK(cb_warpY2_changed), NULL);
                     
    g_signal_connect(G_OBJECT
                     (glade_xml_get_widget
                      (gui_get_xml(), "spinbutton_x3")), "value-changed",
                     G_CALLBACK(cb_warpX3_changed), NULL);
                     
    g_signal_connect(G_OBJECT
                     (glade_xml_get_widget
                      (gui_get_xml(), "spinbutton_y3")), "value-changed",
                     G_CALLBACK(cb_warpY3_changed), NULL);
                     
    g_signal_connect(G_OBJECT
                     (glade_xml_get_widget
                      (gui_get_xml(), "spinbutton_x4")), "value-changed",
                     G_CALLBACK(cb_warpX4_changed), NULL);
                     
    g_signal_connect(G_OBJECT
                     (glade_xml_get_widget
                      (gui_get_xml(), "spinbutton_y4")), "value-changed",
                     G_CALLBACK(cb_warpY4_changed), NULL);*/
                     
    return 0;
}
 


/**
 * Store warp-values to GUI
 */
void warp_gui_update()
{
    gdouble x1, x2, x3, x4, y1, y2, y3, y4;

    /*
     * default values 
     */
    x1 = 10;
    y1 = 10;
    x2 = 128;
    y2 = 10;
    x3 = 128;
    y3 = 128;
    x4 = 10;
    y4 = 128;

    _VERBOSE2 printf("%s()\n",__func__);

    if(gui_get_xml())
    {
        /*
         * set ranges of warp-coordinate spinbuttons 
         */
        gtk_spin_button_set_range(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_x1")),
                                  1,
                                  (gdouble) src_get()->width -
                                  1);
        gtk_spin_button_set_range(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_x2")), 1,
                                  (gdouble) src_get()->width -
                                  1);
        gtk_spin_button_set_range(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_x3")), 1,
                                  (gdouble) src_get()->width -
                                  1);
        gtk_spin_button_set_range(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_x4")), 1,
                                  (gdouble) src_get()->width -
                                  1);
        gtk_spin_button_set_range(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_y1")), 1,
                                  (gdouble) src_get()->height -
                                  1);
        gtk_spin_button_set_range(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_y2")), 1,
                                  (gdouble) src_get()->height -
                                  1);
        gtk_spin_button_set_range(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_y3")), 1,
                                  (gdouble) src_get()->height -
                                  1);
        gtk_spin_button_set_range(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_y4")), 1,
                                  (gdouble) src_get()->height -
                                  1);
                                  
        /* get current values */
        if(sensor_get()) 
        {
            x1 = (gdouble) sensor_get()->wrp_x1;
            y1 = (gdouble) sensor_get()->wrp_y1;
            x2 = (gdouble) sensor_get()->wrp_x2;
            y2 = (gdouble) sensor_get()->wrp_y2;
            x3 = (gdouble) sensor_get()->wrp_x3;
            y3 = (gdouble) sensor_get()->wrp_y3;
            x4 = (gdouble) sensor_get()->wrp_x4;
            y4 = (gdouble) sensor_get()->wrp_y4;
            _VERBOSE1 printf("%s(): %.0f/%.0f, %.0f/%.0f, %.0f/%.0f, %.0f/%.0f\n",
                                __func__, x1,y1,x2,y2,x3,y3,x4,y4);
        }
    
        gtk_spin_button_set_value(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_x1")), x1);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_x2")), x2);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_x3")), x3);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_x4")), x4);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_y1")), y1);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_y2")), y2);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_y3")), y3);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON
                                  (glade_xml_get_widget
                                   (gui_get_xml(), "spinbutton_y4")), y4);
    }
    else
    {
        _ERROR printf("%s(): NULL glade_xml!\n", __func__);
    }
}


/** 
 * @} 
 */
