/*
 * File Name: test_main_window.cpp
 */

/*
 * This file is part of uds-plugin-pdf.
 *
 * uds-plugin-pdf 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-pdf 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 <gdk/gdkkeysyms.h>
#include "plugin_type.h"

#include "string_impl.h"
#include "test_main_window.h"

namespace test
{

MainWindow::MainWindow()
: main_window(0)
, source(this)
, gc(0)
, pixmap(0)
, output_dev()
, current_page_data(0)
, search_results()
{
    create();
}

MainWindow::~MainWindow()
{
}

void MainWindow::create()
{
    // check
    if (main_window)
    {
        return;
    }

    // create main window
    main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    gtk_widget_set_size_request(main_window, 1024, 768);

    // install event handlers
    connect_event_handlers(main_window);

    gtk_widget_show(main_window);

    update_window();

    open_document("G:\\std.pdf");
}

void MainWindow::destory()
{
    g_object_unref(main_window);
    main_window = 0;
}

void MainWindow::connect_event_handlers(GtkWidget *widget)
{
    /// Install event handlers by using member functions.
    source.connect_event_handlers(widget);
    
    source.set_event_handler(EventSource::EVENT_EXPOSE,
                             &MainWindow::on_expose);

    source.set_event_handler(EventSource::EVENT_CONFIG,
                             &MainWindow::on_configure);
    
    source.set_event_handler(EventSource::EVENT_BUTTON_PRESS,
                             &MainWindow::on_button_press);

    source.set_event_handler(EventSource::EVENT_BUTTON_RELEASE,
                             &MainWindow::on_button_release);

    source.set_event_handler(EventSource::EVENT_MOTION_NOTIFY,
                             &MainWindow::on_motion_notify);
    
    source.set_event_handler(EventSource::EVENT_KEY_PRESS,
                             &MainWindow::on_key_press);
    
    source.set_event_handler(EventSource::EVENT_KEY_RELEASE,
                             &MainWindow::on_key_release);
    
    source.set_event_handler(EventSource::EVENT_DELETE,
                             &MainWindow::on_delete);

}

gboolean MainWindow::on_expose(GtkWidget *widget, 
                               GdkEvent *event) 
{
    draw();

    return TRUE;
}

void MainWindow::update_output_device()
{

    OutputDeviceContext output_dev_ctx;

    output_dev_ctx.widget = main_window;
    output_dev_ctx.gc = gc;

    // map output device
    output_dev.map(output_dev_ctx);
}

void MainWindow::update_window()
{
    if (pixmap)
    {
        g_object_unref(pixmap);
    }

    if (gc)
    {
        g_object_unref(gc);
    }

    // Construct pixmap
    pixmap = gdk_pixmap_new(main_window->window,
                            main_window->allocation.width,
                            main_window->allocation.height, -1);

    // Construct gc
    gc = gdk_gc_new(pixmap);

    //Update the output device
    update_output_device();
}

gboolean MainWindow::on_configure(GtkWidget *widget, 
                               GdkEvent *event) 
{
    update_window();

    return TRUE;
}

void MainWindow::open_document(const std::string &filename)
{
    //std::string anchor = "pdf:/page:28/char:0";

    document.open(filename);

    current_page = document.get_cur_page_num();

    document.sig_page_ready.add_slot(this, &MainWindow::handle_page_ready);

    document.sig_search_results_ready.add_slot(this
        , &MainWindow::handle_searching_done);
    
    document.get_cur_page_data();
    
}

void MainWindow::search_next(bool forward)
{
    if (search_results.b_searching)
    {
        return;
    }

    std::string dst_str = "from where you found the exception. In this regard, exception handling is completely different from signal handling. Exception objects are ordinary objects that are described in ordinary classes or ordinary fundamental types. Thus, you can use ints, strings, or template classes that";
    PDFSearchCriteria criteria;
    criteria.text = dst_str;
    criteria.forward = forward;
    criteria.match_whole_word = false;
    criteria.case_sensitive = true;

    static unsigned int id = 0;

    search_results.ref_id = id;
    search_results.b_forward = forward;

    if (search_results.cur_pos.empty())
    {
        document.get_anchor_of_page(current_page, search_results.cur_pos);
    }
    
    if (document.search_next(criteria, search_results.cur_pos, id++))
    {
        search_results.b_searching = true;
    }

}

void MainWindow::search_all()
{
}

void MainWindow::handle_searching_done(SearchResult ret, PDFSearchCollection* results
        , unsigned int ref_id)
{
    if (ref_id != search_results.ref_id)
    {
        delete results;
        return;
    }

    // reset the status at first
    search_results.b_searching = false;

    // get the bounding rectangles of the searching results
    if (ret != RES_OK)
    {
        return;
    }

    // remove the previous search results
    delete search_results.data;
    search_results.data = results;

    // get the first search result
    PluginRange *first_result = results->front();

    if (search_results.b_forward)
    {
        search_results.cur_pos = 
            static_cast<StringImpl*>(first_result->end_anchor)->stl_string();
    }
    else
    {
        search_results.cur_pos = 
            static_cast<StringImpl*>(first_result->start_anchor)->stl_string();
    }
    
    
    // get the first page from the searching results
    // update current displaying page if searching next
    document.get_dst_page_data(search_results.cur_pos);
}

void MainWindow::draw_page_bitmap(const PagePtr page)
{
    //SplashColorMode mode = page_data->get_bitmap()->getMode();
    output_dev.clear_background();

    output_dev.set_color_depth(RGB_COLOR_DEPTH);
    
    output_dev.draw_image(page->get_bitmap()->getDataPtr()
        , page->get_bitmap()->getWidth()
        , page->get_bitmap()->getHeight()
        , page->get_bitmap()->getRowSize());

    // this page has been displayed already, unlock it
    page->unlock();
}

void MainWindow::draw_search_results()
{
    //If the search results should be displayed.
    //1. Get the bounding rectangles of current page
    PDFSearchCollection *cur_search_doc = search_results.data;
    if (!cur_search_doc)
    {
        return;
    }

    //PDFSearchPage *search_page = 0;
    //for(int i = 0; i < cur_search_doc->size(); ++i)
    //{
    //    PDFSearchPage* page = cur_search_doc->get(i);
    //    if (page->get_element() == current_page)
    //    {
    //        search_page = page;
    //        break;
    //    }
    //}
    //

    //if (search_page)
    //{
    //    //Draw all of the search rects in this page
    //    for(int i = 0; i < search_page->size(); ++i)
    //    {
    //        PluginRange *result = search_page->get(i);
    //        assert(result);

    //        PDFRectangles rects;
    //        if (document.get_bounding_rectangles(
    //            static_cast<StringImpl*>(result->start_anchor)->stl_string()
    //            , static_cast<StringImpl*>(result->end_anchor)->stl_string()
    //            , rects))
    //        {
    //            for(int i = 0; i < rects.size(); ++i)
    //            {
    //                PluginRectangle *rect = rects.get(i);
    //                GdkRectangle gdk_rect;
    //                gdk_rect.x = rect->x;
    //                gdk_rect.y = rect->y;
    //                gdk_rect.width  = rect->width;
    //                gdk_rect.height = rect->height;
    //                output_dev.draw_highlight_rectangle(gdk_rect);
    //            }
    //        }
    //    }    
    //}
}

void MainWindow::handle_page_ready(PagePtr cur_page, unsigned int ref_id)
{
    current_page = cur_page->get_page_num();

    current_page_data = cur_page;

    draw_page_bitmap(current_page_data);

    draw_search_results();
}

void MainWindow::draw()
{
    if (current_page_data)
    {
        draw_page_bitmap(current_page_data);
    }

    draw_search_results();
}

gboolean MainWindow::on_button_press(GtkWidget *widget, 
                                     GdkEvent *event)
{
    return FALSE;
}

gboolean MainWindow::on_button_release(GtkWidget *widget, 
                                       GdkEvent *event) 
{
    int dst_num = current_page + 1;
    document.get_dst_page_data(dst_num);
    return TRUE;
}

gboolean MainWindow::on_motion_notify(GtkWidget *widget, 
                                      GdkEvent *event)
{
    return FALSE;
}

gboolean MainWindow::on_key_press(GtkWidget *widget,
                                GdkEvent *event)
{
    return FALSE;
}

gboolean MainWindow::on_key_release(GtkWidget *widget,
                                  GdkEvent *event)
{
    guint key_code = ((GdkEventKey*)event)->keyval;

    switch(key_code)
    {
    //Search Next
    case GDK_F1:
        search_next();

        break;
    case GDK_F2:
        search_next(false);

        break;
    default:

        break;
    }

    return TRUE;
}

gboolean MainWindow::on_delete(GtkWidget *widget,
                              GdkEvent *event)
{
    return FALSE;
}

} //namespace uds

