/* 
 * Copyright (c) 2009, 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 _WB_LIVE_SCHEMA_TREE_H_
#define _WB_LIVE_SCHEMA_TREE_H_

#include "grt/tree_model.h"
#include "grtpp.h"
#include "workbench/wb_backend_public_interface.h"
#include "base/string_utilities.h"
#include <glib/gpattern.h>

namespace wb
{
  class MYSQLWBBACKEND_PUBLIC_FUNC LiveSchemaTree : public bec::TreeModel
  {
  public:
    static const short COLUMN_DATA = 0x01;
    static const short TRIGGER_DATA = 0x02;
    static const short INDEX_DATA = 0x04;
    static const short FK_DATA = 0x08;

    enum ObjectType
    {
      Schema,
      Table,
      View,
      Routine,
      Any
    };

    // One entry in a list that describes a single object.
    struct ChangeRecord
    {
      ObjectType type;
      std::string schema;
      std::string name;
      std::string detail;
    };

    struct MYSQLWBBACKEND_PUBLIC_FUNC ColumnNode
    {
      ColumnNode() : is_pk(false), is_fk(false), is_id(false) {}
      std::string name;
      std::string type;
      std::string default_value;
      bool is_pk;
      bool is_fk;
      bool is_id;
    };

    struct MYSQLWBBACKEND_PUBLIC_FUNC FKColumnNode
    {
      std::string name;
      std::string referenced_column;
    };

    struct MYSQLWBBACKEND_PUBLIC_FUNC FKNode
    {
      std::string name;
      unsigned char update_rule;
      unsigned char delete_rule;
      std::string referenced_table;
      std::vector<FKColumnNode> columns;
    };

    struct MYSQLWBBACKEND_PUBLIC_FUNC IndexColumnNode
    {
      std::string name;
      bool nullable;
    };

    struct MYSQLWBBACKEND_PUBLIC_FUNC IndexNode
    {
      std::string name;
      std::vector<IndexColumnNode> columns;
      bool unique;
      unsigned char type;
    };

    struct MYSQLWBBACKEND_PUBLIC_FUNC TriggerNode
    {
      std::string name;
      unsigned char event_manipulation;
      unsigned char timing;
    };

    
    struct MYSQLWBBACKEND_PUBLIC_FUNC ObjectNode
    {
      ObjectNode();
      std::string name;
      std::string details;
      bool fetched;
      bool fetching;
      bool expanded;
      virtual int get_loaded_mask() { return 0; }
      virtual bool operator <(const ObjectNode &node) const { return name < node.name; }
      virtual void copy(const ObjectNode &node, bool full);
      virtual ObjectNode& operator=(const ObjectNode &node);
    };

    struct MYSQLWBBACKEND_PUBLIC_FUNC ViewNode: public ObjectNode
    {
      std::vector<ColumnNode> columns;
      bool columns_load_error;
      bool columns_loaded;
      bool columns_expanded;
      ViewNode();
      virtual int get_loaded_mask() { return columns_loaded ? COLUMN_DATA : 0; }
      virtual void copy(const ObjectNode &node, bool full);
      virtual ObjectNode& operator=(const ObjectNode &node);
      virtual ViewNode& operator=(const ViewNode &node);
    };
    
    struct MYSQLWBBACKEND_PUBLIC_FUNC TableNode: public ViewNode
    {
      std::vector<TriggerNode> triggers;
      std::vector<IndexNode> indexes;
      std::vector<FKNode> foreign_keys;

      bool triggers_loaded;
      bool indexes_loaded;
      bool foreign_keys_loaded;

      bool triggers_expanded;
      bool indexes_expanded;
      bool foreign_keys_expanded;

      TableNode();
      virtual int get_loaded_mask();
      virtual void copy(const ObjectNode &node, bool full);
      virtual ObjectNode& operator=(const ObjectNode &node);
      virtual TableNode& operator=(const TableNode &node);
      IndexNode *get_index(const std::string &name);
      FKNode *get_foreign_key(const std::string &name);
    };

    struct MYSQLWBBACKEND_PUBLIC_FUNC SchemaNode
    {
      std::string name;
      std::vector<TableNode> tables;
      std::vector<ViewNode> views;
      std::vector<ObjectNode> routines;
      bool fetched;
      bool fetching;
      bool expanded;
      bool tables_expanded;
      bool views_expanded;
      bool routines_expanded;

      SchemaNode();
      bool operator == (const SchemaNode &s) const { return name == s.name; }
      void add_node(ObjectType type, std::string name);
      void delete_node(std::string name, ObjectType type);
      void sort_nodes(ObjectType type);
      bool filter_data(ObjectType object_type, GPatternSpec* schema_pattern, GPatternSpec* object_pattern, SchemaNode& target) const;
    };


    typedef boost::function<void (SchemaNode)> SchemaContentArrivedSlot;
    typedef boost::function<void (SchemaNode, ObjectNode*, ObjectType)> ObjectDetailsArrivedSlot;
    struct FetchDelegate
    {
      virtual std::list<std::string> fetch_schema_list() = 0;
      virtual bool fetch_schema_contents(bec::NodeId, const std::string &, bool include_column_data, const SchemaContentArrivedSlot&) = 0;
      virtual bool fetch_object_details(ObjectType, const std::string&, const std::string&, short, const ObjectDetailsArrivedSlot&) = 0;
    };
    
    struct Delegate
    {
      virtual void tree_refresh() = 0;
      virtual void tree_activate_objects(const std::string&, const std::vector<ChangeRecord>&) = 0;
      virtual void tree_alter_objects(const std::vector<ChangeRecord>&) = 0;
      virtual void tree_create_object(ObjectType, const std::string&, const std::string&) = 0;
      virtual void tree_drop_objects(const std::vector<ChangeRecord>&) = 0;
    };
    
    
    typedef boost::signals2::signal<int (const std::string&)> SqlEditorTextInsertSignal;
    typedef boost::function<bec::MenuItemList (std::string, std::string, std::string)> ObjectPluginMenuItemsSlot;
    typedef boost::function<bool (std::string, std::string, std::string, std::string)> CallObjectPluginMenuItemsSlot;

    SqlEditorTextInsertSignal sql_editor_text_insert_signal;
    ObjectPluginMenuItemsSlot object_plugin_items_slot;
    CallObjectPluginMenuItemsSlot call_object_plugin_items_slot;

  protected:
    boost::weak_ptr<FetchDelegate> _fetch_delegate;
    boost::weak_ptr<Delegate> _delegate;
    grt::GRT* _grt;
    std::vector<SchemaNode> _schemata;
    std::string _active_schema;
    int _schemata_vector_serial;
    bool _rebuilding;
    bool _case_sensitive_identifiers;

    void schema_contents_arrived(const SchemaNode &node, int index, int serial);
    void object_details_arrived(const SchemaNode &schema_node, const ObjectNode *obj_node, ObjectType obj_type, int schema_index, int obj_index, int serial);
    void load_table_details(const bec::NodeId &index_node, int fetch_mask);
    
    bool identifiers_equal(const std::string &a, const std::string &b);

    bool do_expand_node(const bec::NodeId &node, bool dont_fetch);
  public:
    LiveSchemaTree(grt::GRT* grtm);
    ~LiveSchemaTree() { clean_filter(); }
    void set_delegate(boost::shared_ptr<Delegate> delegate);
    void set_fetch_delegate(boost::shared_ptr<FetchDelegate> delegate);
    std::string get_filter_wildcard(const std::string& filter);
    bool filter_data(ObjectType object_type, const std::string& filter, LiveSchemaTree& target);
    bool filter_data(LiveSchemaTree& target);

    ObjectNode * get_object_node_by_index(const bec::NodeId &index_node, ObjectType *object_type= NULL);
    ObjectNode * get_object_node_by_type_and_name(const std::string &schema_name, ObjectType obj_type, std::string name, bec::NodeId *index_ret = 0);
    ObjectNode * get_object_node_by_type_and_name(SchemaNode *schema_node, ObjectType obj_type, std::string name, bec::NodeId *index_ret = 0);
    ObjectNode * get_object_node_by_type_and_index(SchemaNode *schema_node, ObjectType obj_type, size_t index);
    void insert_node(SchemaNode *schema_node, ObjectType obj_type, ObjectNode *obj_node);
    static unsigned char internalize_token(const std::string& token);
    static std::string externalize_token(unsigned char c);

    void set_active_schema(const std::string &schema);
    int get_index_of_schema(const std::string &schema_name) const;
    
    void set_case_sensitive_identifiers(bool flag);

    virtual bool is_expandable(const bec::NodeId &node);
    virtual bool is_expanded(const bec::NodeId &node);
    virtual bool expand_node(const bec::NodeId &node);
    virtual void collapse_node(const bec::NodeId &node);

    virtual void refresh();
    virtual void refresh_ex(bool hard_refresh);
    virtual void refresh_ex(const std::list<std::string> &schema_list, bool hard_refresh);
    virtual int count_children(const bec::NodeId &node);
    virtual bec::NodeId get_child(const bec::NodeId &node, int index);
    
    void update_live_object_state(ObjectType type, const std::string &schema_name, const std::string &old_obj_name, const std::string &new_obj_name);

    virtual std::string get_field_description(const bec::NodeId &node, int column);
    virtual bool get_field(const bec::NodeId &node, int column, std::string &value);
#if !defined(__APPLE__)
    virtual bool get_field(const bec::NodeId &node, int column, int &value);
#endif
    virtual bec::IconId get_field_icon(const bec::NodeId &node, int column, bec::IconSize size);
    
    bec::NodeId get_node_for_schema(const std::string &name);
    bec::NodeId get_node_for_object(const std::string &schema_name, ObjectType type, const std::string& name);
    
    virtual bool activate_node(const bec::NodeId &node);
    
    virtual bool activate_popup_item_for_nodes(const std::string &name, const std::vector<bec::NodeId> &orig_nodes);
    virtual bec::MenuItemList get_popup_items_for_nodes(const std::vector<bec::NodeId> &nodes);

    bool is_schema_contents_enabled() const;
    void is_schema_contents_enabled(bool value);

    void set_base(LiveSchemaTree *base) { _base = base; }
    void set_filter(std::string filter, ObjectType type);
    void clean_filter();

    virtual bool is_highlighted(const bec::NodeId& node);
    
    
    const std::vector<SchemaNode> &get_schemata() { return _schemata; }
  private:
    bool _is_schema_contents_enabled;
    bool _auto_fetch_columns;

    LiveSchemaTree *_base;
    std::string _filter;
    ObjectType _filter_type;
    GPatternSpec* _schema_pattern;
    GPatternSpec* _object_pattern;

    static const char* _schema_tokens[15];
  };
};

#endif
