/* 
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * This program 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; version 2 of the
 * License.
 * 
 * This program 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#ifndef _ADVANCED_SIDEBAR_H_
#define _ADVANCED_SIDEBAR_H_

#include "mforms/task_sidebar.h"
#include "mforms/drawbox.h"
#include "mforms/grttreeview.h"
#include "mforms/menu.h"
#include "mforms/textentry.h"
#include "mforms/label.h"

#include "sqlide/wb_live_schema_tree.h"

/**
 * Implementation of a sidebar in iTunes look and feel that can optionally show the content of a 
 * database.
 */

namespace wb {

  class AdvancedSidebar;

  /**
    * The SidebarEntry class is a lean wrapper for an icon/label combination which can be used
    * to trigger an action. It needs a container to be useful (here the class SidebarEntry).
    */
  class SidebarEntry
  {
  private:
    std::string _title;
    cairo_surface_t* _icon;
    std::string _command;
    bool _is_link;
  public:
    SidebarEntry(const std::string& title, const std::string& icon, const std::string& command, bool as_link);
    ~SidebarEntry();

    void paint(cairo_t* cr, MySQL::Geometry::Rect bounds, bool hot, bool active, const MySQL::Drawing::Color& selection_color);
    bool contains(double x, double y);

    std::string title() { return _title; };
    std::string command() { return _command; };
    bool is_link() { return _is_link; };

  };

  class SidebarSection : public mforms::DrawBox
  {
  private:
    std::string _title;
    std::vector<SidebarEntry*> _entries;
    int _layout_width;
    int _layout_height;
    bool _expand_text_visible;
    int _expand_text_width;
    bool _expanded;
    bool _expand_text_active;
    bool _expandable;
    bool _refresh_hot;
    bool _refresh_down;
    cairo_surface_t* _refresh_icon; // TODO: make this managed by the icon manager.

    SidebarEntry* _selected_entry;
    SidebarEntry* _hot_entry;
    boost::signals2::signal<void (SidebarSection*)> _expanded_changed;

    AdvancedSidebar* _owner;
  protected:
    void set_selected(SidebarEntry* entry);
    void layout(cairo_t* cr);
    SidebarEntry* entry_from_point(double x, double y);
    int find_entry(const std::string& title);

  public:
    SidebarSection(AdvancedSidebar* owner, const std::string& title, bool expandable, bool refreshable);
    ~SidebarSection();

    int add_entry(const std::string& title, const std::string& icon, const std::string& command,
      bool as_link);
    void remove_entry(const std::string& entry);

    void clear();
    bool select(int index);
    bool select(const std::string& title);
    //void set_selection_color(const MySQL::Drawing::Color color) {};

    SidebarEntry* selected() { return _selected_entry; };
    bool expanded() { return _expanded; };
    void toggle_expand();

    std::string title() { return _title; };

    virtual void repaint(cairo_t* cr, int areax, int areay, int areaw, int areah);
    virtual void mouse_leave();
    virtual void mouse_move(int x, int y);
    virtual void mouse_down(int button, int x, int y);
    virtual void mouse_click(int button, int x, int y);
    virtual void mouse_up(int button, int x, int y);
    virtual void get_layout_size(int* w, int* h);

    void clear_selection();

    boost::signals2::signal<void (SidebarSection*)>* expanded_changed() { return &_expanded_changed; }

  };

  class AdvancedSidebar : public mforms::TaskSidebar
  {
  private:
    //db_mgmt_ConnectionRef _connection;
    typedef boost::signals2::signal<void (const std::string&)> SearchBoxChangedSignal;
    std::vector<SidebarSection*> _sections;
    SidebarSection* _schema_tree_heading;
    mforms::GRTTreeView _schema_tree;
    mforms::Menu _tree_context_menu;
    mforms::Box _schema_search_box;
    mforms::TextEntry _schema_search_text;
    mforms::Label _schema_search_warning;
    wb::LiveSchemaTree* _schema_model;
    wb::LiveSchemaTree* _base_model;
    wb::LiveSchemaTree* _filtered_schema_model;
    boost::signals2::scoped_connection _activate_conn;
    boost::signals2::scoped_connection _changed_conn;
    SearchBoxChangedSignal _search_box_changed_signal;
    bool _is_model_owner;                   // True if the sidebar is the owner of the model.

    mforms::Box _schema_box; // Container for schema section and tree so we can hide it as a block.

    AdvancedSidebar(); // Create the sidebar via its mforms alter ego TaskSidebar::create()
    MySQL::Drawing::Color _selection_color;
  protected:
    static mforms::TaskSidebar* create_instance();

    int find_section(const std::string& title);
    void setup_schema_tree();
    void on_show_menu();
    void on_expand_changed(SidebarSection* section);
    void handle_menu_command(const std::string& command);
    void on_search_text_changed();
  public:
    ~AdvancedSidebar();

    virtual mforms::GRTTreeView *get_schema_tree() { return &_schema_tree; }
    virtual mforms::TextEntry *get_filter_entry() { return &_schema_search_text; }
    void refresh_clicked(SidebarSection* section);
    virtual void refresh_model();
    virtual void set_schema_model(wb::LiveSchemaTree* model);
    virtual void set_filtered_schema_model(wb::LiveSchemaTree* model) {_filtered_schema_model = model;}

    virtual int add_section(const std::string& title);
    virtual void remove_section(const std::string& title);
    virtual int add_section_entry(const std::string& section, const std::string& title, const std::string& icon, const std::string& command, bool as_link);
    virtual void remove_section_entry(const std::string& section, const std::string& entry);
    virtual void set_collapse_states(const std::string& data);
    virtual std::string get_collapse_states();

    virtual void clear_sections();
    virtual void clear_section(const std::string& section);
    virtual void set_selection_color(const std::string& color);
    virtual void set_selection_color (const mforms::SystemColor color);
    virtual boost::signals2::signal<void (const std::string&)>* signal_filter_changed() { return &_search_box_changed_signal; }
    const MySQL::Drawing::Color& selection_color() const
    {
      return _selection_color;
    }

    virtual void expand_schema(int schema_index);
    virtual void restore_expanded_nodes();
    bool expand_node(const bec::NodeId &node, int depth_limit);

    virtual int select_entry(const std::string& section, const std::string& title);
    virtual void clear_selection();

  private:
    static bool __init;
    static bool init_factory_method();
  };

} // namespace wb

#endif // _ADVANCED_SIDEBAR_H_
