#ifndef __SQL_EDITOR_FE_H__
#define __SQL_EDITOR_FE_H__

#include "sqlide/sql_editor_be.h"
#include <gtkmm.h>

#include "Scintilla.h"
#include "SciLexer.h"
#define PLAT_GTK 2
#define GTK
#include "ScintillaWidget.h"

class SqlEditorFE : public sigc::trackable
{
  public:
    typedef boost::shared_ptr<SqlEditorFE>  Ref;

    SqlEditorFE();
    Gtk::Widget& widget();
    Gtk::Container& container();
    

    static SqlEditorFE* from(Gtk::Widget *w);

  public:
    Sql_editor::Ref be() { return _be; }
    void be(Sql_editor::Ref be);
    void check_sql(bool sync= false);
    void background_action_cb(sigc::slot<void> slot) { _background_action_cb= slot; }
  
    bool is_dirty() { return _dirty; }
    void set_dirty(bool flag);

    void show_special_chars(bool flag);

    void show_find_panel(bool flag=true);
    void enable_replace_panel(bool flag);
  private:

    bool on_background_action_timer();

    Sql_editor::Ref _be;
    sigc::slot<void> _background_action_cb;
    sigc::connection _background_action_timer_conn;
    int _old_selection_start, _old_selection_end;

    void change_selected_range(int start, int end);
    void change_cursor_pos(int pos);

    bool on_find_key_press(GdkEventKey *key);
    void find_text_changed();

  public:
    std::string current_sql_statement();

  public:
    void set_text(const std::string& text);
    std::string get_text();
    std::string get_selected_text();
    void set_savepoint();
    bool get_modify();
    void scroll_to(const int line, const std::string& msg);
    void set_focus();
  
    void set_font(const std::string &font);

    sigc::signal<void>& signal_text_changed() {return _text_changed_signal;}
    sigc::signal<void>& signal_selection_changed() { return _selection_changed_signal; }

    int reset_sql_check_state();
    int process_sql_statement_border(int begin_lineno, int begin_line_pos, int end_lineno, int end_line_pos);
    int process_sql_error(const int err_tok_line, const int err_tok_line_pos, const int err_tok_len, const std::string &err_msg);

    bool sql_got_errors() const { return _errors_count != 0; }

    void insert_text(const std::string &text);
    void replace_selected_text(const std::string &text);

  public:
    enum FindResult
    {
      FoundMatch,
      NoMoreMatches,
      WrappedAround,
      NoMatches
    };
    void select_all();
    void copy();
    void delete_();
    void paste();
    bool has_selection();
    bool is_editable();
    FindResult find_text(const std::string &text, bool match_case=false, bool match_whole_word=false, bool forward=true);
  
    bool can_undo();
    bool can_redo();
    void undo();
    void redo();

    void toggle_wrap_lines();

  private:
    enum ReplaceType
    {
      Replace,
      ReplaceAll,
      FindReplace
    };
    void notify(SCNotification *notification);
    bool margin_click(int position, int modifiers);
    void fold_changed(int line, int levelNow, int levelPrev);
    void expand(int &line, bool doExpand, bool force=false, int visLevels=0, int level=-1);
    void fold_code(bool expanding);
    void fold_open_all();
    void fold_close_all();
    void do_find(bool forward);
    void do_replace(ReplaceType type);
    void find_icon_press(Gtk::EntryIconPosition pos, const GdkEventButton *ev);
    void add_search_history(const std::string &entry);
    void clear_search_history();

    Gtk::VBox            _container;
    Glib::RefPtr<Gtk::Builder> _find_panel;
    Gtk::Widget         *_widget; // Gtkmm wrapped _editor
    GtkWidget           *_editor;
    ScintillaObject     *_sci;
    Gtk::Menu            _context_menu;

    Gtk::Entry          *_find_entry;
    Gtk::Entry          *_replace_entry;
    Gtk::Label          *_find_status;
    Gtk::Menu           *_search_menu;
    bool                _search_match_whole_word;
    bool                _search_ignore_case;
    bool                _search_wrap_around;

    sptr_t send_editor(unsigned int msg, uptr_t uparam = 0, sptr_t sparam = 0);
    sigc::signal<void>  _text_changed_signal;
    sigc::signal<void>  _selection_changed_signal;

    static void notify_signal(GtkWidget *w, gint wParam, gpointer lParam, SqlEditorFE *editor);
    int _errors_count;
  
    bool on_button_press_event(GdkEventButton *event);
    void activate_menu_action(const std::string &action);
  
    bool _dirty;
};

#endif
