#include "config.h"
#include <Elementary.h>

#include "../Ecrire.h"

typedef struct
{
  Evas_Object            *entry_find;
  Evas_Object            *entry_replace;
  Ecrire_Editor          *inst;

  int                    initial_pos;
  int                    prev_find_pos;
  Eina_Bool              forwards;
  Eina_Bool              wrap;
  Evas_Textblock_Cursor *cur_find;
} Entry_Search;

static Eina_Bool
_search_replace(Entry_Search *search, const char *text, Eina_Bool jump_next)
{
   Evas_Textblock_Cursor *end, *start, *mcur;
   Eina_Bool try_next = EINA_FALSE;
   const char *found;
   char *utf8;
   size_t initial_pos;
   Evas_Object *entry;
   const Evas_Object *tb;


   entry = search->inst->entry;
   tb = elm_entry_textblock_get(entry);

   if (!text || !*text)
      return EINA_FALSE;

   mcur = (Evas_Textblock_Cursor *) evas_object_textblock_cursor_get(tb);
   if (!search->cur_find)
     {
        search->cur_find = evas_object_textblock_cursor_new(tb);
     }
   else if (!evas_textblock_cursor_compare(search->cur_find, mcur))
     {
        try_next = EINA_TRUE;
     }

   if (search->forwards)
     {
        evas_textblock_cursor_paragraph_last(search->cur_find);
        start = mcur;
        end = search->cur_find;
     }
   else
     {
        /* Not correct, more adjustments needed. */
        evas_textblock_cursor_paragraph_first(search->cur_find);
        start = search->cur_find;
        end = mcur;
     }

   initial_pos = evas_textblock_cursor_pos_get(start);

   utf8 = evas_textblock_cursor_range_text_get(start, end,
         EVAS_TEXTBLOCK_TEXT_PLAIN);

   if (!utf8)
      return EINA_FALSE;

   if (try_next && jump_next)
     {
        found = strstr(utf8 + 1, text);
        if (!found)
          {
             found = utf8;
          }
     }
   else
     {
        found = strstr(utf8, text);
     }

   if (found)
     {
        size_t pos = 0;
        int idx = 0;
        while ((utf8 + idx) < found)
          {
             pos++;
#if (EINA_VERSION_MAJOR > 1) || (EINA_VERSION_MINOR >= 8)
             eina_unicode_utf8_next_get(utf8, &idx);
#else
             eina_unicode_utf8_get_next(utf8, &idx);
#endif
          }

        if ((search->wrap) && ((pos != search->prev_find_pos) && (search->prev_find_pos == (pos + initial_pos))))
          {
             elm_entry_cursor_pos_set(entry, 0);
             free(utf8);
             return _search_replace(search, text, jump_next);
          }
        else
          {
             elm_entry_select_none(entry);
             evas_textblock_cursor_pos_set(mcur, pos + initial_pos + strlen(text));
             elm_entry_cursor_selection_begin(entry);
             elm_entry_cursor_pos_set(entry, pos + initial_pos);
             elm_entry_cursor_selection_end(entry);
             evas_textblock_cursor_copy(mcur, search->cur_find);
             search->prev_find_pos = pos + initial_pos;
          }
     }
   else if (search->wrap)
     {
        elm_entry_cursor_pos_set(entry, 0);
     }

   free(utf8);

   return !!found;
}

static void
_cb_find_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Entry_Search *search = data;

   _search_replace(search, elm_object_text_get(search->entry_find), EINA_TRUE);
}

static void
_cb_replace_clicked(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Entry_Search *search = data;

   if (_search_replace(search, elm_object_text_get(search->entry_find), EINA_FALSE))
     {
        elm_entry_entry_insert(search->inst->entry, elm_object_text_get(search->entry_replace));
        if (search->cur_find)
          {
             evas_textblock_cursor_free(search->cur_find);
             search->cur_find = NULL;
          }
     }
}

static void
_cb_wrap_changed(void *data EINA_UNUSED, Evas_Object *obj,
                 void *event_info EINA_UNUSED)
{
   Entry_Search *search = data;

   search->wrap = elm_check_state_get(obj);
}

static void
_cb_entry_changed(void *data EINA_UNUSED, Evas_Object *obj,
                  void *event_info EINA_UNUSED)
{
   Entry_Search *search = data;

   elm_entry_cursor_pos_set(search->inst->entry, search->initial_pos);
}

static void
_cb_win_del(void *data EINA_UNUSED, Evas *e, Evas_Object *obj, void *event_info)
{
   Entry_Search *search = data;

   if (search->cur_find)
     evas_textblock_cursor_free(search->cur_find);

   search->inst->search_win = NULL;
   free(search);
}

Evas_Object *
ui_find_dialog_open(Evas_Object *parent, Ecrire_Editor *inst)
{
   Evas_Object *win, *bg, *bx, *pad, *hbx, *chk, *btn;
   Evas_Object *sent, *rent;
   const Evas_Object *tb;
   Evas_Textblock_Cursor *cursor;

   if (inst->search_win) return NULL;

   Entry_Search *search = calloc(1, sizeof(Entry_Search));
   EINA_SAFETY_ON_NULL_RETURN_VAL(search, NULL);

   search->inst = inst;
   search->wrap = 1;
   search->forwards = 1;

   search->inst->search_win = win = elm_win_add(parent, "search", ELM_WIN_TOOLBAR);
   elm_win_autodel_set(win, EINA_TRUE);
   elm_win_title_set(win, _("Search"));
   evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, _cb_win_del, search);

   bg = elm_bg_add(win);
   elm_win_resize_object_add(win, bg);
   evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_show(bg);

   bx = elm_box_add(win);
   elm_win_resize_object_add(win, bx);
   evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_show(bx);

   search->entry_find = sent = elm_entry_add(win);
   elm_object_part_text_set(sent, "guide", "Search");
   elm_entry_scrollable_set(sent, EINA_TRUE);
   elm_entry_single_line_set(sent, EINA_TRUE);
   evas_object_size_hint_align_set(sent, EVAS_HINT_FILL, 0.0);
   evas_object_size_hint_weight_set(sent, EVAS_HINT_EXPAND, 0.0);
   elm_box_pack_end(bx, sent);
   evas_object_show(sent);
   evas_object_smart_callback_add(sent, "changed", _cb_entry_changed, search);

   search->entry_replace = rent = elm_entry_add(win);
   elm_object_part_text_set(rent, "guide", "Replace");
   elm_entry_scrollable_set(rent, EINA_TRUE);
   elm_entry_single_line_set(rent, EINA_TRUE);
   evas_object_size_hint_align_set(rent, EVAS_HINT_FILL, 0.0);
   evas_object_size_hint_weight_set(rent, EVAS_HINT_EXPAND, 0.0);
   elm_box_pack_end(bx, rent);
   evas_object_show(rent);
   evas_object_smart_callback_add(rent, "changed", _cb_entry_changed, search);

   hbx = elm_box_add(win);
   elm_box_homogeneous_set(hbx, EINA_TRUE);
   elm_box_horizontal_set(hbx, EINA_TRUE);
   evas_object_size_hint_align_set(hbx, 0.5, EVAS_HINT_FILL);
   evas_object_size_hint_weight_set(hbx, EVAS_HINT_EXPAND, 0.0);
   evas_object_show(hbx);
   elm_box_pack_end(bx, hbx);

   pad = elm_frame_add(win);
   elm_object_style_set(pad, "pad_small");
   evas_object_size_hint_align_set(pad, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_size_hint_weight_set(pad, EVAS_HINT_EXPAND, 0.0);
   evas_object_show(pad);

   btn = elm_button_add(win);
   elm_object_text_set(btn, _("Find"));
   evas_object_size_hint_align_set(btn, 1.0, 0.0);
   evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND, 0.0);
   evas_object_show(btn);
   elm_object_content_set(pad, btn);
   elm_box_pack_end(hbx, pad);
   evas_object_smart_callback_add(btn, "clicked", _cb_find_clicked, search);

   pad = elm_frame_add(win);
   elm_object_style_set(pad, "pad_small");
   evas_object_size_hint_align_set(pad, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_size_hint_weight_set(pad, EVAS_HINT_EXPAND, 0.0);
   evas_object_show(pad);

   btn = elm_button_add(win);
   elm_object_text_set(btn, _("Replace"));
   evas_object_size_hint_align_set(btn, 1.0, 0.0);
   evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND, 0.0);
   evas_object_show(btn);
   elm_object_content_set(pad, btn);
   elm_box_pack_end(hbx, pad);
   evas_object_smart_callback_add(btn, "clicked", _cb_replace_clicked, search);

   chk = elm_check_add(bx);
   evas_object_size_hint_weight_set(chk, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_size_hint_align_set(chk, EVAS_HINT_FILL, EVAS_HINT_FILL);
   elm_object_text_set(chk, _("Wrap?"));
   elm_check_state_set(chk, search->wrap);
   elm_box_pack_end(hbx, chk);
   evas_object_show(chk);
   evas_object_smart_callback_add(chk, "changed", _cb_wrap_changed, search);

   tb = elm_entry_textblock_get(inst->entry);
   cursor = (Evas_Textblock_Cursor *) evas_object_textblock_cursor_get(tb);
   search->initial_pos = evas_textblock_cursor_pos_get(cursor);

   /* Forcing it to be the min height. */
   evas_object_resize(win, 250, 1);
   evas_object_show(win);

   return win;
}
