#include "SearchBar.h"

#include <config.h>

#include "control/Control.h"

#include "i18n.h"

SearchBar::SearchBar(Control* control): control(control) {
    MainWindow* win = control->getWindow();

    GtkWidget* close = win->get("buttonCloseSearch");
    g_signal_connect(close, "clicked", G_CALLBACK(buttonCloseSearchClicked), this);

    GtkWidget* next = win->get("btSearchForward");
    GtkWidget* previous = win->get("btSearchBack");
    g_signal_connect(next, "clicked", G_CALLBACK(+[](GtkButton* button, SearchBar* self) { self->searchNext(); }),
                     this);
    g_signal_connect(previous, "clicked",
                     G_CALLBACK(+[](GtkButton* button, SearchBar* self) { self->searchPrevious(); }), this);

    // TODO(fabian): When keybindings are implemented, handle previous search keybinding properly
    GtkWidget* searchTextField = win->get("searchTextField");
    g_signal_connect(searchTextField, "search-changed", G_CALLBACK(searchTextChangedCallback), this);
    // Enable next/previous search when pressing Enter / Shift+Enter
    g_signal_connect(searchTextField, "key-press-event",
                     G_CALLBACK(+[](GtkWidget* entry, GdkEventKey* event, SearchBar* self) {
                         if (event->keyval == GDK_KEY_Return) {
                             if (event->state & GDK_SHIFT_MASK)
                                 self->searchPrevious();
                             else
                                 self->searchNext();
                             // Grab focus again since searching will take away focus
                             gtk_widget_grab_focus(entry);
                             return true;
                         }
                         return false;
                     }),
                     this);

    cssTextFild = gtk_css_provider_new();
    gtk_style_context_add_provider(gtk_widget_get_style_context(win->get("searchTextField")),
                                   GTK_STYLE_PROVIDER(cssTextFild), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}

SearchBar::~SearchBar() { this->control = nullptr; }

auto SearchBar::searchTextonCurrentPage(const char* text, int* occures, double* top) -> bool {
    int p = control->getCurrentPageNo();

    return control->searchTextOnPage(text, p, occures, top);
}

void SearchBar::search(const char* text) {
    MainWindow* win = control->getWindow();
    GtkWidget* lbSearchState = win->get("lbSearchState");

    bool found = true;
    int occures = 0;

    if (*text != 0) {
        found = searchTextonCurrentPage(text, &occures, nullptr);
        if (found) {
            if (occures == 1) {
                gtk_label_set_text(GTK_LABEL(lbSearchState), _("Text found on this page"));
            } else {
                char* msg = g_strdup_printf(_("Text %i times found on this page"), occures);
                gtk_label_set_text(GTK_LABEL(lbSearchState), msg);
                g_free(msg);
            }
        } else {
            gtk_label_set_text(GTK_LABEL(lbSearchState), _("Text not found"));
        }
    } else {
        searchTextonCurrentPage("", nullptr, nullptr);
        gtk_label_set_text(GTK_LABEL(lbSearchState), "");
    }

    if (found) {
        gtk_css_provider_load_from_data(cssTextFild, "GtkSearchEntry {}", -1, nullptr);
    } else {
        gtk_css_provider_load_from_data(cssTextFild, "GtkSearchEntry { color: #ff0000; }", -1, nullptr);
    }
}

void SearchBar::searchTextChangedCallback(GtkEntry* entry, SearchBar* searchBar) {
    const char* text = gtk_entry_get_text(entry);
    searchBar->search(text);
}

void SearchBar::buttonCloseSearchClicked(GtkButton* button, SearchBar* searchBar) { searchBar->showSearchBar(false); }

void SearchBar::searchNext() {
    int page = control->getCurrentPageNo();
    int count = control->getDocument()->getPageCount();
    if (count < 2) {
        // Nothing to do
        return;
    }

    MainWindow* win = control->getWindow();
    int x = page + 1;
    GtkWidget* searchTextField = win->get("searchTextField");
    const char* text = gtk_entry_get_text(GTK_ENTRY(searchTextField));
    GtkWidget* lbSearchState = win->get("lbSearchState");
    if (*text == 0) {
        return;
    }

    if (x >= count) {
        x = 0;
    }

    double top = 0;
    int occures = 0;

    while (x != page) {

        bool found = control->searchTextOnPage(text, x, &occures, &top);
        if (found) {
            control->getScrollHandler()->scrollToPage(x, top);
            gtk_label_set_text(GTK_LABEL(lbSearchState),
                               (occures == 1 ? FC(_F("Text found once on page {1}") % (x + 1)) :
                                               FC(_F("Text found {1} times on page {2}") % occures % (x + 1))));
            return;
        }

        x++;
        if (x >= count) {
            x = 0;
        }
    }

    gtk_label_set_text(GTK_LABEL(lbSearchState), _("Text not found, searched on all pages"));
}

void SearchBar::searchPrevious() {
    int page = control->getCurrentPageNo();
    int count = control->getDocument()->getPageCount();
    if (count < 2) {
        // Nothing to do
        return;
    }

    MainWindow* win = control->getWindow();
    int x = page - 1;
    GtkWidget* searchTextField = win->get("searchTextField");
    const char* text = gtk_entry_get_text(GTK_ENTRY(searchTextField));
    GtkWidget* lbSearchState = win->get("lbSearchState");
    if (*text == 0) {
        return;
    }

    if (x < 0) {
        x = count - 1;
    }

    double top = 0;
    int occures = 0;

    while (x != page) {

        bool found = control->searchTextOnPage(text, x, &occures, &top);
        if (found) {
            control->getScrollHandler()->scrollToPage(x, top);
            gtk_label_set_text(GTK_LABEL(lbSearchState),
                               (occures == 1 ? FC(_F("Text found once on page {1}") % (x + 1)) :
                                               FC(_F("Text found {1} times on page {2}") % occures % (x + 1))));
            return;
        }

        x--;
        if (x < 0) {
            x = count - 1;
        }
    }

    gtk_label_set_text(GTK_LABEL(lbSearchState), _("Text not found, searched on all pages"));
}

void SearchBar::showSearchBar(bool show) {
    MainWindow* win = control->getWindow();
    GtkWidget* searchBar = win->get("searchBar");

    if (show) {
        GtkWidget* searchTextField = win->get("searchTextField");
        gtk_widget_grab_focus(searchTextField);
        gtk_widget_show_all(searchBar);
    } else {
        gtk_widget_hide(searchBar);
        for (int i = control->getDocument()->getPageCount() - 1; i >= 0; i--) {
            control->searchTextOnPage("", i, nullptr, nullptr);
        }
    }
}
