/*
 * File Name: render_test.cpp
 */

/*
 * This file is part of uds-plugin-plaintext.
 *
 * uds-plugin-plaintext 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 of the License, or
 * (at your option) any later version.
 *
 * uds-plugin-plaintext 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, see <http://www.gnu.org/licenses/>.
 */

/**
 * Copyright (C) 2008 iRex Technologies B.V.
 * All rights reserved.
 */

#include <stdio.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "text_config.h"
#include "text_model.h"
#include "text_view.h"

using namespace text;

unsigned char* bmp = NULL;
GdkPixbuf *pix_buf = NULL;
GtkWidget *top_level_window = NULL;
GdkGC*     xor_gc  = NULL;

int page_index  = 0;
int total_pages = 0;
int cur_width   = DEFAULT_SURFACE_WIDTH;
int cur_height  = DEFAULT_SURFACE_HEIGHT;

// Store the search result.
std::vector<Range> result_ranges;
std::vector<BoundingRectangle>  result_rects;

void fill_pix_buf()
{
    if (pix_buf != NULL)
    {
        g_object_unref(pix_buf);
        pix_buf = NULL;
    }

    pix_buf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
        FALSE,
        8,
        cur_width,
        cur_height);

    guchar *pixels = gdk_pixbuf_get_pixels(pix_buf);
    int rowstride = gdk_pixbuf_get_rowstride(pix_buf);
    int channels = gdk_pixbuf_get_n_channels(pix_buf);

    guchar *p = NULL;
    for (int y=0; y<cur_height; y++)
    {
        for (int x=0; x<cur_width; x++)
        {
            p = pixels + y*rowstride + x*channels;
            p[0] = p[1] = p[2] = bmp[y*cur_width+x];
        }
    }
}

static void destroy(GtkWidget *widget, gpointer data)
{
    gtk_main_quit ();
}

static gboolean widget_event_handler(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
    guint key_code;
    GdkEventConfigure *cfg_event = NULL;
    GdkEventButton *button_event = NULL;
    TextView  *view = (TextView *)user_data;
    const TextModel *model = view->get_model();
    const char *str = NULL;
    std::string tmp;
    ViewPosition view_pos;
    Position doc_pos;
    GtkWidget *dialog;

    switch (event->type)
    {
        case GDK_KEY_PRESS:
            key_code = ((GdkEventKey*)event)->keyval;
            if (key_code == GDK_Page_Up)
            {
                if (page_index == 0)
                {
                    return FALSE;
                }
                --page_index;
            }
            else if (key_code == GDK_Page_Down)
            {
                if (page_index == total_pages-1)
                {
                    return FALSE;
                }
                ++page_index;
            }
            else if (key_code == GDK_equal)
            {
                // Increase font size
                view->set_font_size(view->get_font_size()+1);
                total_pages = view->get_page_count();
                page_index = 0;
            }
            else if (key_code == GDK_minus)
            {
                // Decrease font size
                view->set_font_size(view->get_font_size()-1);
                total_pages = view->get_page_count();
                page_index = 0;
            }

            // Render specified page
            memset(bmp, 0xFF, cur_width * cur_height);
            view->render(bmp, page_index);
            fill_pix_buf();
            gdk_window_invalidate_rect(top_level_window->window, NULL, TRUE);
            break;

        case GDK_CONFIGURE:
            cfg_event = reinterpret_cast<GdkEventConfigure *>(event);
            if (cfg_event->width != cur_width || cfg_event->height != cur_height)
            {
                cur_width  = cfg_event->width;
                cur_height = cfg_event->height;
                view->set_display_size(cur_width, cur_height);
                total_pages = view->get_page_count();

                // Render page 0
                delete[] bmp;
                bmp = new unsigned char[cur_width * cur_height];
                memset(bmp, 0xFF, cur_width * cur_height);
                view->render(bmp, page_index = 0);
                fill_pix_buf();
                gdk_window_invalidate_rect(top_level_window->window, NULL, TRUE);
            }
            break;

        case GDK_BUTTON_PRESS:
            // Record the position inside the widget.
            button_event = reinterpret_cast<GdkEventButton *>(event);
            view_pos.page_number = page_index;
            view_pos.x = static_cast<int>(button_event->x);
            view_pos.y = static_cast<int>(button_event->y);
            if (view->map_view_pos_to_doc_pos(view_pos, doc_pos))
            {
                str = model->get_paragraph(doc_pos.paragraph)->c_str() + doc_pos.offset;
                tmp = std::string(str, g_utf8_next_char(str)-str);
            }
            else
            {
                tmp = "Nothing found!";
            }

            dialog = gtk_message_dialog_new(GTK_WINDOW(top_level_window),
                                            GTK_DIALOG_MODAL,
                                            GTK_MESSAGE_INFO,
                                            GTK_BUTTONS_OK,
                                            "%s", tmp.c_str());
            gtk_dialog_run(GTK_DIALOG(dialog));
            gtk_widget_destroy(dialog);
            break;

        default:
            break;
    }
    return TRUE;
}

static gboolean my_expose(GtkWidget *da, GdkEventExpose *event, gpointer data)
{
    GdkGC *gc = gdk_gc_new(da->window);
    GdkPixbuf** pix_buf = (GdkPixbuf**)data;
    gdk_draw_pixbuf(da->window,
        gc,
        *pix_buf,
        0,
        0,
        0,
        0,
        -1,
        -1,
        GDK_RGB_DITHER_NORMAL,
        0,
        0);

    for (unsigned int i=0; i<result_rects.size(); i++)
    {
        if (result_rects[i].page_number == page_index)
        {
            gdk_draw_rectangle(da->window,
                xor_gc,
                TRUE,
                result_rects[i].rect.x,
                result_rects[i].rect.y,
                result_rects[i].rect.width,
                result_rects[i].rect.height);
        }
    }

    return TRUE;
}

int main(int argc, char* argv[])
{
    gtk_init(&argc, &argv);

    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s <file_name>.\n", argv[0]);
        return -1;
    }

    // Render the page 0
    TextModel model(argv[1]);
    model.open();
    model.read_text();
    // Dump model
    // model.dump();

    // Test search all function.
    // Set search criteria.
    char search_text[] = "¹ùÐ¥Ìì";
    Position from(0, 0);
    bool case_sensitive = true;
    bool match_whole_word = false;

    // Convert the search text to UTF-8 encoded string.
#ifdef WIN32
    const char* in_p = search_text;
#else
    char* in_p = search_text;
#endif
    size_t in_bytes_left = strlen(search_text);
    char out_buf[1024];
    char *out_p = out_buf;
    size_t out_bytes_left = sizeof(out_buf);

    iconv_t conv = iconv_open("UTF-8", "GB2312");
    iconv(conv, &in_p, &in_bytes_left, &out_p, &out_bytes_left);
    iconv_close(conv);

    *out_p = 0;
    model.search_all(result_ranges, out_buf, case_sensitive, match_whole_word);
    // Test search all done.

    g_thread_init(NULL);
    TextView view(&model);
    view.initialize();
    view.paginate(NULL);
    total_pages = view.get_page_count();

    for (unsigned int i=0; i<result_ranges.size(); i++)
    {
        view.get_bounding_rectangles(result_rects, result_ranges[i]);
    }

    bmp = new unsigned char[cur_width * cur_height];
    memset(bmp, 0xFF, cur_width * cur_height);
    view.render(bmp, page_index);
    fill_pix_buf();

    // GTK stuff
    top_level_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_size_request(top_level_window, cur_width, cur_height);

    gtk_widget_set_events (top_level_window,
                           GDK_EXPOSURE_MASK | GDK_BUTTON_MOTION_MASK |
                           GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
                           GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
    g_signal_connect(G_OBJECT(top_level_window), "destroy", G_CALLBACK(destroy), NULL);
    g_signal_connect(G_OBJECT(top_level_window), "expose_event", G_CALLBACK(my_expose), &pix_buf);
    gtk_signal_connect(GTK_OBJECT(top_level_window), "key_press_event", GTK_SIGNAL_FUNC(widget_event_handler), &view);
    gtk_signal_connect(GTK_OBJECT(top_level_window), "button_press_event", GTK_SIGNAL_FUNC(widget_event_handler), &view);
    gtk_signal_connect(GTK_OBJECT(top_level_window), "configure_event", GTK_SIGNAL_FUNC(widget_event_handler), &view);

    // Show all widget
    gtk_widget_show_all(top_level_window);

    // Create xor gc.
    xor_gc = gdk_gc_new(top_level_window->window);
    gdk_gc_set_function(xor_gc, GDK_XOR);
    GdkColor color = {0xffffffff, 0xffff, 0xffff, 0xffff};
    gdk_gc_set_foreground(xor_gc, &color);

    gtk_main();

    g_object_unref(pix_buf);
    return 0;
}

