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

#include "stdafx.h"

#include "wb_live_schema_tree.h"
#include "grtdb/charset_utils.h"
#include "grt/icon_manager.h"

#include "base/string_utilities.h"
#include "base/sqlstring.h"
#include "base/log.h"

using namespace wb;
using namespace bec;
using namespace base;
using namespace grt;

DEFAULT_LOG_DOMAIN("SqlEditorSchemaTree");

const std::string LiveSchemaTree::SCHEMA_TAG = "_SCHEMA_";
const std::string LiveSchemaTree::TABLES_TAG = "_TABLES_";
const std::string LiveSchemaTree::VIEWS_TAG = "_VIEWS_";
const std::string LiveSchemaTree::ROUTINES_TAG = "_ROUTINES_";
const std::string LiveSchemaTree::TABLE_TAG = "_TABLE_";
const std::string LiveSchemaTree::VIEW_TAG = "_VIEW_";
const std::string LiveSchemaTree::ROUTINE_TAG = "_ROUTINE_";
const std::string LiveSchemaTree::COLUMNS_TAG = "_COLUMNS_";
const std::string LiveSchemaTree::INDEXES_TAG = "_INDEXES_";
const std::string LiveSchemaTree::TRIGGERS_TAG = "_TRIGGERS_";
const std::string LiveSchemaTree::FOREIGN_KEYS_TAG = "_FOREIGN_KEYS_";
const std::string LiveSchemaTree::COLUMN_TAG = "_COLUMN_";
const std::string LiveSchemaTree::INDEX_TAG = "_INDEX_";
const std::string LiveSchemaTree::TRIGGER_TAG = "_TRIGGER_";
const std::string LiveSchemaTree::FOREIGN_KEY_TAG = "_FOREIGN_KEY_";

const std::string LiveSchemaTree::FETCHING_CAPTION = _("fetching...");
const std::string LiveSchemaTree::TABLES_CAPTION = _("Tables");
const std::string LiveSchemaTree::VIEWS_CAPTION = _("Views");
const std::string LiveSchemaTree::ROUTINES_CAPTION = _("Routines");

const std::string LiveSchemaTree::COLUMNS_CAPTION = _("Columns");
const std::string LiveSchemaTree::INDEXES_CAPTION = _("Indexes");
const std::string LiveSchemaTree::TRIGGERS_CAPTION = _("Triggers");
const std::string LiveSchemaTree::FOREIGN_KEYS_CAPTION = _("Foreign Keys");

const int LiveSchemaTree::TABLES_NODE_INDEX = 0;
const int LiveSchemaTree::VIEWS_NODE_INDEX = 1;
const int LiveSchemaTree::ROUTINES_NODE_INDEX = 2;
const int LiveSchemaTree::TABLE_COLUMNS_NODE_INDEX = 0;
const int LiveSchemaTree::TABLE_INDEXES_NODE_INDEX = 1;
const int LiveSchemaTree::TABLE_FOREIGN_KEYS_NODE_INDEX = 2;
const int LiveSchemaTree::TABLE_TRIGGERS_NODE_INDEX = 3;

const char* LiveSchemaTree::_schema_tokens[15] = {"",                                                           // Empty item to use 0 as not found
                                                  "CASCADE", "SET NULL", "SET DEFAULT", "RESTRICT", "NO ACTION", // The update/delete rules on foreign keys
                                                  "BTREE", "FULLTEXT", "HASH", "RTREE",                          // The index types
                                                  "INSERT", "UPDATE", "DELETE",                                  // Trigger events
                                                  "BEFORE", "AFTER"};                                            // Trigger timing

LiveSchemaTree::LSTData::LSTData()
  :details("") 
{
};

void LiveSchemaTree::LSTData::copy(LSTData* other)
{
  this->details = other->details;
}

std::string LiveSchemaTree::LSTData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val("");

  if (full)
  {
    std::string owner_name = node->get_string(0);

    ret_val = strfmt(_("<b>%s:</b> <font color='#148814'><b>%s</b></font><br><br>"), get_object_name().c_str(), owner_name.c_str());
  }
  else
    ret_val = details;

  return ret_val;
}

void LiveSchemaTree::ColumnData::copy(LSTData* other)
{
  LSTData::copy(other);

  ColumnData* pother = dynamic_cast<ColumnData*>(other);

  if (pother)
  {
    this->type = pother->type;
    this->default_value = pother->default_value;
    this->is_id = pother->is_id;
    this->is_pk = pother->is_pk;
    this->is_fk = pother->is_fk;
  }
}

std::string LiveSchemaTree::ColumnData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val("");

  if (full)
  {
    ret_val = LSTData::get_details(full, node);
    ret_val += _("<b>Definition:</b><table style=\"border: none; border-collapse: collapse;\">");
    ret_val += details;
    ret_val += "</table><br><br>";
  }
  else
    ret_val = details;
  
  return ret_val;
}

void LiveSchemaTree::FKData::copy(LSTData* other)
{
  LSTData::copy(other);

  FKData* pother = dynamic_cast<FKData*>(other);

  if (pother)
  {
    this->referenced_table = pother->referenced_table;
    this->update_rule = pother->update_rule;
    this->delete_rule = pother->delete_rule;
  }
}

std::string LiveSchemaTree::FKData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val("");

  if (full)
  {
    ret_val = LSTData::get_details(full, node);
    ret_val += _("<b>Definition:</b><br>");
    ret_val += details;
  }
  else
    ret_val = details;
  
  return ret_val;
}

void LiveSchemaTree::IndexData::copy(LSTData* other)
{
  LSTData::copy(other);

  IndexData* pother = dynamic_cast<IndexData*>(other);

  if (pother)
  {
    this->type = pother->type;
    this->unique = pother->unique;
  }
}

void LiveSchemaTree::TriggerData::copy(LSTData* other)
{
  LSTData::copy(other);

  TriggerData* pother = dynamic_cast<TriggerData*>(other);

  if (pother)
  {
    this->event_manipulation = pother->event_manipulation;
    this->timing = pother->timing;
  }
}

void LiveSchemaTree::ObjectData::copy(LSTData* other)
{
  LSTData::copy(other);

  ObjectData* pother = dynamic_cast<ObjectData*>(other);

  if (pother)
  {
    this->fetched = pother->fetched;
    this->fetching = pother->fetching;
  }
}

void LiveSchemaTree::ViewData::copy(LSTData* other)
{
  ObjectData::copy(other);

  ViewData* pother = dynamic_cast<ViewData*>(other);

  if (pother)
  {
    this->_loaded_mask = pother->_loaded_mask;
    this->_loading_mask = pother->_loading_mask;
    this->columns_load_error = pother->columns_load_error;
  }
}

std::string LiveSchemaTree::ViewData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val;
  
  if (full)
    ret_val = LSTData::get_details(full, node);

  if (_loaded_mask & COLUMN_DATA)
  {
    int count = (get_type() == Table) ? node->get_child(TABLE_COLUMNS_NODE_INDEX)->count() : node->count();
    if (count)
    {
      ret_val += _("<b>Columns:</b><table style=\"border: none; border-collapse: collapse;\">");

      for(int index = 0; index < count; index++)
      {
        ColumnData* pdata;
        if (get_type() == Table)
          pdata = dynamic_cast<ColumnData*>(node->get_child(TABLE_COLUMNS_NODE_INDEX)->get_child(index)->get_data());
        else
          pdata = dynamic_cast<ColumnData*>(node->get_child(index)->get_data());

        ret_val += pdata->get_details(false, node);
      }

      ret_val += "</table><br><br>";
    }
  }

  // Appends the error message if needed
  if (columns_load_error)
    ret_val += details;

  return ret_val;
}

std::string LiveSchemaTree::TableData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val = ViewData::get_details(full, node);

  if (_loaded_mask & FK_DATA)
  {
    int count = node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->count();
    if (count)
    {
      ret_val += _("<div><b>Related Tables:</b></div>");

      for(int index = 0; index < count; index++)
      {
        FKData* pdata = dynamic_cast<FKData*>(node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->get_child(index)->get_data());

        ret_val += pdata->get_details(false, node);
      }
    }
  }

  
  return ret_val;
}

void LiveSchemaTree::SchemaData::copy(LSTData* other)
{
  LSTData::copy(other);

  SchemaData* pother = dynamic_cast<SchemaData*>(other);

  if (pother)
  {
    this->fetched = pother->fetched;
    this->fetching = pother->fetching;
  }
}

//--------------------------------------------------------------------------------------------------

short LiveSchemaTree::ViewData::get_loaded_mask()
{
  return _loaded_mask;
}

//--------------------------------------------------------------------------------------------------

void LiveSchemaTree::ViewData::set_unloaded_data(short mask)
{
  // Gets the data that is loaded and needs to be marked as unloaded
  short filter = _loaded_mask & mask;

  _loaded_mask -= filter;
}

void LiveSchemaTree::ViewData::set_loaded_data(short mask)
{
  // Gets the data that is being marked as loaded and was loading
  short filter = mask & _loading_mask;
  _loading_mask -= filter;

  // Now marks the data as loaded
  _loaded_mask |= mask;
}

short LiveSchemaTree::ViewData::get_loading_mask()
{
  return _loading_mask;
}

void LiveSchemaTree::ViewData::set_loading_mask(short mask)
{
  _loading_mask = mask;
}

bool LiveSchemaTree::ViewData::is_data_loaded(short mask)
{
  return (_loaded_mask & mask) == mask;
}

//--------------------------------------------------------------------------------------------------

LiveSchemaTree::LiveSchemaTree(grt::GRT* grt)
: _grt(grt), _model_view(NULL), _case_sensitive_identifiers(false), 
_is_schema_contents_enabled(true), _enabled_events(false), _base(0), _filter_type(Any), _schema_pattern(0), _object_pattern(0)
{
  fill_node_icons();
  
  compare_func = boost::bind(&LiveSchemaTree::compare_identifiers, this, _1, _2);
  
  // Setup the schema node collection skeleton
  mforms::TreeNodeCollectionSkeleton schema_nodes(_icon_paths[Schema]);
  
  mforms::TreeNodeSkeleton tables(TABLES_CAPTION, _icon_paths[TableCollection], TABLES_TAG);
  schema_nodes.children.push_back(tables);

  mforms::TreeNodeSkeleton views(VIEWS_CAPTION, _icon_paths[ViewCollection], VIEWS_TAG);
  schema_nodes.children.push_back(views);

  mforms::TreeNodeSkeleton routines(ROUTINES_CAPTION, _icon_paths[RoutineCollection], ROUTINES_TAG);
  schema_nodes.children.push_back(routines);
  
  _node_collections[Schema] = schema_nodes;
  
  // Setup the table node collection skeleton
  mforms::TreeNodeCollectionSkeleton table_nodes(_icon_paths[Table]);

  mforms::TreeNodeSkeleton columns(COLUMNS_CAPTION, _icon_paths[ColumnCollection], COLUMNS_TAG);
  table_nodes.children.push_back(columns);

  mforms::TreeNodeSkeleton indexes(INDEXES_CAPTION, _icon_paths[IndexCollection], INDEXES_TAG);
  table_nodes.children.push_back(indexes);

  mforms::TreeNodeSkeleton foreign_keys(FOREIGN_KEYS_CAPTION, _icon_paths[ForeignKeyCollection], FOREIGN_KEYS_TAG);
  mforms::TreeNodeSkeleton fetching_fk(FETCHING_CAPTION, _icon_paths[ForeignKey], "");
  foreign_keys.children.push_back(fetching_fk);
  table_nodes.children.push_back(foreign_keys);

  mforms::TreeNodeSkeleton triggers(TRIGGERS_CAPTION, _icon_paths[TriggerCollection], TRIGGERS_TAG);
  mforms::TreeNodeSkeleton fetching_trigger(FETCHING_CAPTION, _icon_paths[Trigger], "");
  triggers.children.push_back(fetching_trigger);
  table_nodes.children.push_back(triggers);
  
  _node_collections[Table] = table_nodes;

  // Setup the view node collection skeleton
  mforms::TreeNodeCollectionSkeleton view_nodes(_icon_paths[View]);
  mforms::TreeNodeSkeleton fetching_view(FETCHING_CAPTION, _icon_paths[View], "");
  view_nodes.children.push_back(fetching_view);
  _node_collections[View] = view_nodes;
  
  mforms::TreeNodeCollectionSkeleton procedure_nodes(_icon_paths[Procedure]);
  _node_collections[Procedure] = procedure_nodes;
  
  mforms::TreeNodeCollectionSkeleton function_nodes(_icon_paths[Function]);
  _node_collections[Function] = function_nodes;
  

  mforms::TreeNodeCollectionSkeleton table_column_nodes(_icon_paths[TableColumn]);
  _node_collections[TableColumn] = table_column_nodes;

  mforms::TreeNodeCollectionSkeleton view_column_nodes(_icon_paths[ViewColumn]);
  _node_collections[ViewColumn] = view_column_nodes;


  mforms::TreeNodeCollectionSkeleton index_nodes(_icon_paths[Index]);
  _node_collections[Index] = index_nodes;


  mforms::TreeNodeCollectionSkeleton trigger_nodes(_icon_paths[Trigger]);
  _node_collections[Trigger] = trigger_nodes;


  mforms::TreeNodeCollectionSkeleton fk_nodes(_icon_paths[ForeignKey]);
  _node_collections[ForeignKey] = fk_nodes;
}

LiveSchemaTree::~LiveSchemaTree()
 { 
   clean_filter();
}

void LiveSchemaTree::set_fetch_delegate(boost::shared_ptr<FetchDelegate> delegate)
{
  _fetch_delegate= delegate;
}

void LiveSchemaTree::set_delegate(boost::shared_ptr<Delegate> delegate)
{
  _delegate= delegate;
}

unsigned char LiveSchemaTree::internalize_token(const std::string& token)
{
  unsigned char found_index = 0;
  for(unsigned char index =1; !found_index && (index < (sizeof(_schema_tokens)/sizeof(char*))); index++)
  {
    if (token == _schema_tokens[index])
      found_index = index;
  }

  return found_index;
}

std::string LiveSchemaTree::externalize_token(unsigned char c)
{
  return (c > 0 && c < (sizeof(_schema_tokens)/sizeof(char*))) ? _schema_tokens[c] : "";
}

void LiveSchemaTree::set_case_sensitive_identifiers(bool flag)
{
  _case_sensitive_identifiers = flag;
}

bool LiveSchemaTree::identifiers_equal(const std::string &a, const std::string &b)
{
  if (_case_sensitive_identifiers)
    return a == b;
  return g_strcasecmp(a.c_str(), b.c_str()) == 0;
}

void LiveSchemaTree::setup_node(mforms::TreeNodeRef node, ObjectType type, mforms::TreeNodeData* pdata, bool ignore_null_data)
{
  switch(type)
  {
  case Schema:
    node->set_data(pdata ? pdata : new SchemaData());
    break;
  case Table:
    node->set_data(pdata ? pdata : new TableData());
    break;
  case View:
    node->set_data(pdata ? pdata : new ViewData());
    break;
  case Procedure:
    node->set_data(pdata ? pdata : new ProcedureData());
    break;
  case Function:
    node->set_data(pdata ? pdata : new FunctionData());
    break;
  case ViewColumn:
    if (pdata || !ignore_null_data)
      node->set_data(pdata ? pdata : new ColumnData(type));
    break;
  case TableColumn:
    node->set_data(pdata ? pdata : new ColumnData(type));
    break;
  case Index:
    node->set_data(pdata ? pdata : new IndexData());
    break;
  case Trigger:
    if (pdata || !ignore_null_data)
      node->set_data(pdata ? pdata : new TriggerData());
    break;
  case ForeignKey:
    if (pdata || !ignore_null_data)
      node->set_data(pdata ? pdata : new FKData());
    break;
  default:
    break;
  }
}

void LiveSchemaTree::fill_node_icons()
{
  _icon_paths[Schema] = get_node_icon_path(Schema);
  _icon_paths[TableCollection] = get_node_icon_path(TableCollection);
  _icon_paths[ViewCollection] = get_node_icon_path(ViewCollection);
  _icon_paths[RoutineCollection] = get_node_icon_path(RoutineCollection);
  _icon_paths[Table] = get_node_icon_path(Table);
  _icon_paths[View] = get_node_icon_path(View);
  _icon_paths[Procedure] = get_node_icon_path(Procedure);
  _icon_paths[Function] = get_node_icon_path(Function);
  _icon_paths[ColumnCollection] = get_node_icon_path(ColumnCollection);
  _icon_paths[IndexCollection] = get_node_icon_path(IndexCollection);
  _icon_paths[ForeignKeyCollection] = get_node_icon_path(ForeignKeyCollection);
  _icon_paths[TriggerCollection] = get_node_icon_path(TriggerCollection);
  _icon_paths[ViewColumn] = get_node_icon_path(ViewColumn);
  _icon_paths[TableColumn] = get_node_icon_path(TableColumn);
  _icon_paths[Index] = get_node_icon_path(Index);
  _icon_paths[ForeignKey] = get_node_icon_path(ForeignKey);
  _icon_paths[Trigger] = get_node_icon_path(Trigger);
}

std::string LiveSchemaTree::get_node_icon_path(ObjectType type)
{
  bec::IconId icon = get_node_icon(type);
  return bec::IconManager::get_instance()->get_icon_file(icon);
}

bec::IconId LiveSchemaTree::get_node_icon(ObjectType type)
{
  std::string suffix;
  bec::IconId icon;

#ifdef _WIN32
  suffix = "win.$.png";
#else
  suffix = "$.png";
#endif

  switch(type)
  {
  case Schema:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.unloaded.side." + suffix, bec::Icon16);
    break;
  case TableCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Table.many.side." + suffix, bec::Icon16);
    break;
  case ViewCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.View.many.side." + suffix, bec::Icon16);
    break;
  case RoutineCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Routine.many.side." + suffix, bec::Icon16);
    break;
  case Table:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Table.side." + suffix, bec::Icon16);
    break;
  case View:
    icon = bec::IconManager::get_instance()->get_icon_id("db.View.side." + suffix, bec::Icon16);
    break;
  case Procedure:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Routine.side." + suffix, bec::Icon16);
    break;
  case Function:
    icon = bec::IconManager::get_instance()->get_icon_id("grt_function.png", bec::Icon16);
    break;
  case ColumnCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Column.many.side." + suffix, bec::Icon16);
    break;
  case IndexCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Index.many.side." + suffix, bec::Icon16);
    break;
  case ForeignKeyCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.ForeignKey.many.side." + suffix, bec::Icon16);
    break;
  case TriggerCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Trigger.many.side." + suffix, bec::Icon16);
    break;
  case ViewColumn:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Column.side." + suffix, bec::Icon16);
    break;
  case TableColumn:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Column.side." + suffix, bec::Icon16);
    break;
  case Index:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Index.side." + suffix, bec::Icon16);
    break;
  case ForeignKey:
    icon = bec::IconManager::get_instance()->get_icon_id("db.ForeignKey.side." + suffix, bec::Icon16);
    break;
  case Trigger:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Trigger.side." + suffix, bec::Icon16);
    break;
  default:
    break;
  }
  
  return icon;
}

void LiveSchemaTree::update_node_icon(mforms::TreeNodeRef node)
{
  std::string suffix;
  bec::IconId icon = 0;

#ifdef _WIN32
  suffix = "win.$.png";
#else
  suffix = "$.png";
#endif

  LSTData* pnode_data = dynamic_cast<LSTData*> (node->get_data());
  if (pnode_data)
  {
    switch(pnode_data->get_type())
    {
      case Schema:
        {
          SchemaData *pdata = dynamic_cast<SchemaData*> (node->get_data());
          if (pdata->fetched)
            icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.side." + suffix, bec::Icon16);
          else if (pdata->fetching)
            icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.loading.side." + suffix, bec::Icon16);
        }
        break;

      case View:
        {
          ViewData *pdata = dynamic_cast<ViewData*> (node->get_data());

          if (pdata->columns_load_error)
            icon = bec::IconManager::get_instance()->get_icon_id("db.View.broken.side." + suffix, bec::Icon16);
          else
            icon = bec::IconManager::get_instance()->get_icon_id("db.View.side." + suffix, bec::Icon16);
        }
        break;
      case TableColumn:
        {
          ColumnData *pdata = dynamic_cast<ColumnData*> (node->get_data());

          if (pdata->is_pk)
            icon = bec::IconManager::get_instance()->get_icon_id("db.Column.pk.side." + suffix, bec::Icon16);
          else if (pdata->is_fk)
            icon = bec::IconManager::get_instance()->get_icon_id("db.Column.fk.side." + suffix, bec::Icon16);
        }
        break;
      // No other item changes icon
      default:
        break;
    }
    
    if (icon)
      node->set_icon_path(0, bec::IconManager::get_instance()->get_icon_file(icon));
  }
}

/*
 * Function: update_change_data
 * Description: Uses the received information to
 *              - Generate the list of nodes to be removed
 *              - Purge children to let there only the items to be added
 * Parameters:
 *   parent: the tree node for which the children list will be updated
 *   children: the list of names that will be used on the process
 *   type: the type of children that will be affected (some nodes may have children of different types)
 *   to_remove: a vector containing the nodes to be removed from the parent node
 */
void LiveSchemaTree::update_change_data(mforms::TreeNodeRef parent, std::list<std::string>& children, ObjectType type, std::vector<mforms::TreeNodeRef>& to_remove)
{
  mforms::TreeNodeRef node;

    int total_nodes = parent->count();
    if (total_nodes == 1 && parent->get_child(0)->get_string(0) == FETCHING_CAPTION)
      to_remove.push_back(parent->get_child(0));
    else
    {
      for(int index = 0; index < total_nodes; index++)
      {
        node = parent->get_child(index);
        LSTData *pchild_data = dynamic_cast<LSTData*>(node->get_data());
      
        // Ensure only items of the same type are analyzed
        if ( pchild_data && pchild_data->get_type() == type)
        {
          std::list<std::string>::iterator found_child = std::find(children.begin(), children.end(), node->get_string(0));

          // If not found, the item will be removed
          if ( found_child == children.end())
            to_remove.push_back(node);
          // If found, the item is removed from the incoming list
          // to let only nodes to be added
          else
            children.erase(found_child);
        }
      }
    }
}

/*
 * Function: update_node_children
 * Description: Updates the children of a node based on a received children name list:
 *              Removes children nodes not contained on the received name list (if not just appending)
 *              Adds children nodes for names on the list for which a node is missing
 * Parameters:
 *   parent: the tree node for which the children list will be updated
 *   children: the list of names that will be used on the process
 *   type: the type of children that will be affected (some nodes may have children of different types)
 *   sorted: used to determine whether the nodes should be inserted in the proper position to keep the objects
 *           sorted or if they just need to be appended the way they come.
 *   just_append: suppresses the logic of removing nodes (i.e. when searching nodes with different filters
 *                they are only appended on the base tree)
 * Return Value: a boolean value indicating whether the children list changed (nodes were added or removed)
 * 
 * NOTE : That children may change, so if the original list is needed, the caller needs to have a copy
 */
bool LiveSchemaTree::update_node_children(mforms::TreeNodeRef parent, std::list<std::string>& children, ObjectType type, bool sorted, bool just_append)
{
  bool ret_val = false;

  if (_base)
  {
    std::vector<std::string> path = get_node_path(parent);
    mforms::TreeNodeRef base_parent = _base->get_node_from_path(path);
    ret_val = _base->update_node_children(base_parent, children, type, sorted, just_append);

    // Filters the arrived data
    GPatternSpec* pattern = NULL;
    if (type == Schema)
      pattern = _schema_pattern;
    else if(is_object_type(SchemaObject, type))
      pattern = _object_pattern;

    filter_children(type, base_parent, parent, pattern);
  }
  else
  {
    mforms::TreeNodeRef node;
    bool added = false;
    bool removed = false;
    std::vector<mforms::TreeNodeRef> childs_to_remove;
    
    _model_view->freeze_refresh();
    
    // Calculates the nodes to be removed and the new nodes to be created
    update_change_data(parent, children, type, childs_to_remove);
    
    // Removes deleted children if needed...
    if (!just_append && !childs_to_remove.empty())
    {
      for(size_t index = 0; index < childs_to_remove.size(); index++)
        childs_to_remove[index]->remove_from_parent();
      
      removed = true;
    }

    // If at this point there are nodes on the parent, then the new nodes
    // need to be inserted into the right position.
    // If not, as they are sorted, they just get added.
    std::vector<mforms::TreeNodeRef> added_nodes;
    std::vector<mforms::TreeNodeRef> group_added_nodes;
    std::string icon_path = get_node_icon_path(type);

    if (sorted)
      children.sort(compare_func);
    
    if (!children.empty())
    {
      // it and it_end are used on the iteration process
      // start and end are used to determine a group to be inserted
      // which by default is the whole list
      std::list<std::string>::const_iterator   it = children.begin(),
                                               start = it,
                                               it_end = children.end(),
                                               end = it_end;

      // final_group will be used to prevent searching for the position
      // of the childs once one has been found to be at the end.
      bool final_group = false;
      int last_position = -1;
      _node_collections[type].captions.clear();

      if (parent->count() > 0 && sorted)
      {
        // Initializes last_position with the position of the first
        // Item to be inserted
        find_child_position(parent, *it, type, last_position);

        end = start;
        
        do
        {
          it++;
          if (it != it_end)
          {
            int position = 0;
            if (!find_child_position(parent, *it, type, position))
            {
              if (position != last_position)
              {
                // Assigns the captions for the group to be inserted
                _node_collections[type].captions.assign(start, it);

                group_added_nodes = parent->add_node_collection(_node_collections[type], last_position);
                added_nodes.insert(added_nodes.end(), group_added_nodes.begin(), group_added_nodes.end());

                // Start is reset to the iterator position
                start = it;
                last_position = position + _node_collections[type].captions.size();

                // Shortcut to quit once an item has been found to be located
                // At the end of the list
                final_group = (last_position == -1);
              }
            }
          }

          // All the time end will advance with the iterator
          end = it;
        }while (it != it_end && !final_group);
      }

      // Inserts the last group of nodes...
      end = children.end();
      _node_collections[type].captions.assign(start, end);
      if (!_node_collections[type].captions.empty())
      {
        // positions the index on the previous position (last in group)
        group_added_nodes = parent->add_node_collection(_node_collections[type], last_position);
        added_nodes.insert(added_nodes.end(), group_added_nodes.begin(), group_added_nodes.end());
      }

      for(size_t index = 0; index < added_nodes.size(); index++)
      {
        setup_node(added_nodes[index], type);
        added = true;
      }
    }
    
    ret_val = (added || removed);

    std::string icon = get_node_icon_path(type);

    _model_view->thaw_refresh();
    }

  return ret_val;
}

std::string LiveSchemaTree::get_field_description(const mforms::TreeNodeRef& node)
{
  std::string text("");
  mforms::TreeNodeRef temp_node = node;

  while(temp_node && !text.length())
  {
    LSTData *pdata = dynamic_cast<LSTData*>(temp_node->get_data());

    if (pdata)
    {
      ObjectType type = pdata->get_type();
      
      if (is_object_type(TableOrView, type))
      {
        short fetch_mask = (type == Table) ? COLUMN_DATA|INDEX_DATA : COLUMN_DATA;
      
        load_table_details(type, get_schema_name(node), temp_node->get_string(0), fetch_mask);
      }

      text = pdata->get_details(true, temp_node);
    }
    else
      temp_node = temp_node->get_parent();
  }

  return text;
}

void LiveSchemaTree::set_active_schema(const std::string &schema)
{
  mforms::TreeNodeTextAttributes attrs;

  if (_model_view)
  {
    mforms::TreeNodeRef current_active = get_child_node(_model_view->root_node(), _active_schema);
    mforms::TreeNodeRef new_active     = get_child_node(_model_view->root_node(), schema);

    if (current_active)
    {
      std::string name = current_active->get_string(0);
      current_active->set_string(0, name);
      current_active->set_attributes(0, attrs);
    }

    if (new_active)
    {
      std::string name = new_active->get_string(0);
      attrs.bold = true;
      new_active->set_string(0, name);
      new_active->set_attributes(0, attrs);
    }
  }

  _active_schema = schema;

  if(_base)
    _base->set_active_schema(schema);
}

void LiveSchemaTree::update_live_object_state(ObjectType type, const std::string &schema_name, const std::string &old_obj_name, const std::string &new_obj_name)
{
  if (_model_view)
  {
    mforms::TreeNodeRef schema_node;
    bool created = old_obj_name.empty() && !new_obj_name.empty();
    bool deleted = !old_obj_name.empty() && new_obj_name.empty();
    bool changed = !old_obj_name.empty() && !new_obj_name.empty();

    if (type == Schema)
    {
      if (created)
        insert_node(_model_view->root_node(), new_obj_name, type);
      else if (deleted)
      {
        schema_node = get_child_node(_model_view->root_node(), old_obj_name);

        if (schema_node)
          schema_node->remove_from_parent();
      }
    }
    else
    {
      schema_node = get_child_node(_model_view->root_node(), schema_name);

      if (schema_node)
      {
        mforms::TreeNodeRef object_node;
        if (!created)
        {
          switch(type)
          {
          case Table:
            object_node = this->get_child_node(schema_node->get_child(TABLES_NODE_INDEX), old_obj_name);
            break;
          case View:
            object_node = this->get_child_node(schema_node->get_child(VIEWS_NODE_INDEX), old_obj_name);
            break;
          case Procedure:
          case Function:
            object_node = this->get_child_node(schema_node->get_child(ROUTINES_NODE_INDEX), old_obj_name, type);
            break;
          default:
            break;
          }
        }

        if (changed && object_node)
        {
            if (old_obj_name != new_obj_name)
                object_node->set_string(0, new_obj_name);

            // As the object has changed we trigger a reload on
            // Its content
            reload_object_data(object_node);
        }
        else
        {
          if (created)
          {
            mforms::TreeNodeRef parent_node;
            int target_position = 0;
            switch(type)
            {
            case Table:
              if (!find_child_position(schema_node->get_child(TABLES_NODE_INDEX), new_obj_name, type, target_position))
                parent_node = schema_node->get_child(TABLES_NODE_INDEX);
              break;
            case View:
              if (!find_child_position(schema_node->get_child(VIEWS_NODE_INDEX), new_obj_name, type, target_position))
                parent_node = schema_node->get_child(VIEWS_NODE_INDEX);
              break;
            case Procedure:
            case Function:
              if (!find_child_position(schema_node->get_child(ROUTINES_NODE_INDEX), new_obj_name, type, target_position))
                parent_node = schema_node->get_child(ROUTINES_NODE_INDEX);
              break;
            default:
              break;
            }
            
            if (parent_node)
              insert_node(parent_node, new_obj_name, type);
          }
          else
          {
            if (object_node)
              object_node->remove_from_parent();
          }
        }
      }
    }
  }
}

void LiveSchemaTree::schema_contents_arrived(const std::string &schema_name,
  std::list<std::string>* tables, std::list<std::string>* views,
  std::list<std::string>* procedures, std::list<std::string>* functions, bool just_append)
{
  if (_base)
  {
    _base->schema_contents_arrived(schema_name, tables, views, procedures, functions, just_append);
    filter_data();
  }
  else
  {
    if (_model_view)
    {
      mforms::TreeNodeRef schema_node = get_child_node(_model_view->root_node(), schema_name);

      if (schema_node)
      {
        mforms::TreeNodeRef tables_node   = schema_node->get_child(TABLES_NODE_INDEX);
        mforms::TreeNodeRef views_node    = schema_node->get_child(VIEWS_NODE_INDEX);
        mforms::TreeNodeRef routines_node = schema_node->get_child(ROUTINES_NODE_INDEX);

        int old_table_count = tables_node->count();
        int old_view_count = tables_node->count();
        
        update_node_children(tables_node,   *tables,     Table,    true, just_append);
        update_node_children(views_node,    *views,      View,     true, just_append);
        update_node_children(routines_node, *procedures, Procedure,true, just_append);
        update_node_children(routines_node, *functions,  Function, true, just_append);
        

        // If there were nodes that means this is a refresh, in such case loaded tables
        // must be reloaded so the changes are displayed
        if (old_table_count)
        {
            for (int index = 0; index < tables_node->count(); index++)
            {
                mforms::TreeNodeRef pnode = tables_node->get_child(index);
                if (pnode->is_expanded())
                    reload_object_data(pnode);
            }
        }
        
        // If there were nodes that means this is a refresh, in such case loaded tables
        // must be reloaded so the changes are displayed
        if (old_view_count)
        {
            for (int index = 0; index < views_node->count(); index++)
            {
                mforms::TreeNodeRef pnode = views_node->get_child(index);
                if (pnode->is_expanded())
                    reload_object_data(pnode);
            }
        }

        SchemaData *pdata = dynamic_cast<SchemaData*>(schema_node->get_data());
        pdata->fetching = false;
        
        if (!just_append)
          pdata->fetched = true;

        update_node_icon(schema_node);

        tables_node->set_string(0, TABLES_CAPTION);
        views_node->set_string(0, VIEWS_CAPTION);
        routines_node->set_string(0, ROUTINES_CAPTION);

        delete tables;
        delete views;
        delete procedures;
        delete functions;
      }
    }
  }
}

void LiveSchemaTree::load_table_details(ObjectType object_type, const std::string schema_name, const std::string object_name, int fetch_mask)
{
  if (_model_view)
  {
    mforms::TreeNodeRef node;
    
    // If object type is unknown there's no need to pull the
    // object from the tree
    if (object_type != Any)
      node = get_node_for_object(schema_name, object_type, object_name);

    if (node)
      load_table_details(node, fetch_mask);
    else
      fetch_table_details(object_type, schema_name, object_name, fetch_mask);
  }
}

void LiveSchemaTree::load_table_details(mforms::TreeNodeRef& node, int fetch_mask)
{
  ViewData* pdata = dynamic_cast<ViewData*>(node->get_data());

  if (pdata)
  {
    short loaded_mask = pdata->get_loaded_mask();
    short loading_mask = pdata->get_loading_mask();

    // Calculates what needs to be loaded based on what was requested and what is already loaded
    short data_load_flags = (fetch_mask ^ loaded_mask) & fetch_mask;

    // Calculates what needs to be loaded based on what was left on the previous check and what is already being loaded
    data_load_flags = (data_load_flags ^ loading_mask) & data_load_flags;

    if (data_load_flags)
    {
      pdata->set_loading_mask(data_load_flags);
      std::string schema_name = get_schema_name(node);
      fetch_table_details(pdata->get_type(), schema_name, node->get_string(0), data_load_flags);
    }
  }
}


void LiveSchemaTree::fetch_table_details(ObjectType object_type, const std::string schema_name, const std::string object_name, int fetch_mask)
{
    boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock();

    if (delegate)
    {

      delegate->fetch_object_details(schema_name, object_name, object_type, fetch_mask,
                                    boost::bind(&LiveSchemaTree::update_node_children, this, _1, _2, _3, _4, _5));
    }
}

void LiveSchemaTree::load_data_for_filter(const std::string& schema_filter, const std::string& object_filter)
{
    if (boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock())
    {
      std::string remote_schema_filter = get_filter_wildcard(schema_filter, RemoteLike);
      std::string remote_object_filter = get_filter_wildcard(object_filter, RemoteLike);
      delegate->fetch_data_for_filter(remote_schema_filter, remote_object_filter, 
                              boost::bind(&LiveSchemaTree::schema_contents_arrived, this, _1, _2, _3, _4, _5, _6));
    }
}

void LiveSchemaTree::load_schema_content(mforms::TreeNodeRef& schema_node)
{
  SchemaData* data = dynamic_cast<SchemaData*>(schema_node->get_data());

  if (!data->fetched && !data->fetching)
  {
    data->fetching = true;
    std::string name = schema_node->get_string(0);

    if (_base)
    {
      mforms::TreeNodeRef base_schema_node = _base->get_node_from_path(get_node_path(schema_node));

      base_schema_node->get_child(TABLES_NODE_INDEX)->set_string(0, TABLES_CAPTION + " " + FETCHING_CAPTION);
      base_schema_node->get_child(VIEWS_NODE_INDEX)->set_string(0, VIEWS_CAPTION + " " + FETCHING_CAPTION);
      base_schema_node->get_child(ROUTINES_NODE_INDEX)->set_string(0, ROUTINES_CAPTION + " " + FETCHING_CAPTION);
    }

    schema_node->get_child(TABLES_NODE_INDEX)->set_string(0, TABLES_CAPTION + " " + FETCHING_CAPTION);
    schema_node->get_child(VIEWS_NODE_INDEX)->set_string(0, VIEWS_CAPTION + " " + FETCHING_CAPTION);
    schema_node->get_child(ROUTINES_NODE_INDEX)->set_string(0, ROUTINES_CAPTION + " " + FETCHING_CAPTION);

    update_node_icon(schema_node);

    if (boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock())
    {
      delegate->fetch_schema_contents(name, 
                              boost::bind(&LiveSchemaTree::schema_contents_arrived, this, _1, _2, _3, _4, _5, _6));
    }
  }
}

mforms::TreeNodeRef LiveSchemaTree::get_node_for_object(const std::string &schema_name, ObjectType type, const std::string& name)
{
  mforms::TreeNodeRef object_node = mforms::TreeNodeRef();

  if (_model_view)
  {
    mforms::TreeNodeRef schema_node = get_child_node(_model_view->root_node(), schema_name);

    if (schema_node)
    {
      if (type == Schema)
        object_node = schema_node;
      else
      {
        switch(type)
        {
        case Table:
          object_node = get_child_node(schema_node->get_child(TABLES_NODE_INDEX), name);
          break;
        case View:
          object_node = get_child_node(schema_node->get_child(VIEWS_NODE_INDEX), name);
          break;
        case Procedure:
        case Function:
          object_node = get_child_node(schema_node->get_child(ROUTINES_NODE_INDEX), name, type);
          break;
        default:
          break;
        }
      }
    }
  }

  return object_node;
}


mforms::TreeNodeRef LiveSchemaTree::create_node_for_object(const std::string &schema_name, ObjectType type, const std::string& name)
{
  bool created_schema = false;
  mforms::TreeNodeRef object_node = mforms::TreeNodeRef();
  mforms::TreeNodeRef parent_node = mforms::TreeNodeRef();

  if (_model_view)
  {
    mforms::TreeNodeRef schema_node = get_child_node(_model_view->root_node(), schema_name);

    // Creates the schema if doesnt exist...
    if (!schema_node)
    {
      schema_node = insert_node(_model_view->root_node(), schema_name, Schema);
      created_schema = true;
    }

    switch(type)
    {
    case Table:
      parent_node = schema_node->get_child(TABLES_NODE_INDEX);
      break;
    case View:
      parent_node = schema_node->get_child(VIEWS_NODE_INDEX);
      break;
    case Procedure:
    case Function:
      parent_node = schema_node->get_child(ROUTINES_NODE_INDEX);
      break;
    default:
      break;
    }

    if (parent_node)
      object_node = insert_node(parent_node, name, type);
    else if (created_schema)
      schema_node->remove_from_parent();
  }

  return object_node;
}


bec::MenuItemList LiveSchemaTree::get_popup_items_for_nodes(const std::list<mforms::TreeNodeRef> &nodes)
{
  bec::MenuItemList items;

  {
    bec::MenuItem item;
    item.type = MenuAction;

    bec::MenuItem edit_item;
    edit_item.caption= _("Edit Table Data");
    edit_item.name= "edit_data";
    edit_item.enabled= false;
        
    bec::MenuItem view_item;
    {
      std::string caption = _("Select Rows");
      {
        DictRef options= DictRef::cast_from(_grt->get("/wb/options/options"));
        bool limit_rows= (0 != options.get_int("SqlEditor:LimitRows"));
        int limit_rows_count= options.get_int("SqlEditor:LimitRowsCount");
        if (limit_rows && (limit_rows_count <= 0))
          limit_rows= false;
        if (limit_rows)
          caption += strfmt(_(" - Limit %i"), limit_rows_count);
      }
      view_item.caption= caption;
    }
    view_item.name= "select_data";
    view_item.enabled= false;

    
    bec::MenuItem active_schema_item;
    active_schema_item.caption= _("Set as Default Schema");
    active_schema_item.name= "set_active_schema";
    active_schema_item.enabled= false;

    bec::MenuItem filter_schema_item;
    filter_schema_item.caption= _("Filter to This Schema");
    filter_schema_item.name= "filter_schema";
    filter_schema_item.enabled= false;
    
    bec::MenuItem clipboard_copy_item;
    clipboard_copy_item.caption= _("Copy to Clipboard");
    clipboard_copy_item.name= "clipboard_copy";
    clipboard_copy_item.type= MenuCascade;
    clipboard_copy_item.enabled= false;

    bec::MenuItem editor_insert_item;
    editor_insert_item.caption= _("Send to SQL Editor");
    editor_insert_item.name= "editor_insert";
    editor_insert_item.type= MenuCascade;
    editor_insert_item.enabled= false;

    bec::MenuItem object_name_short_item;
    object_name_short_item.caption= _("Name (short)");
    object_name_short_item.name= "object_name_short";
    object_name_short_item.enabled= false;

    bec::MenuItem object_name_long_item;
    object_name_long_item.caption= _("Name (long)");
    object_name_long_item.name= "object_name_long";
    object_name_long_item.enabled= false;

    bec::MenuItem select_statement_item;
    select_statement_item.caption= _("Select All Statement");
    select_statement_item.name= "table_select_statement";
    select_statement_item.enabled= false;

    bec::MenuItem insert_statement_item;
    insert_statement_item.caption= _("Insert Statement");
    insert_statement_item.name= "table_insert_statement";
    insert_statement_item.enabled= false;

    bec::MenuItem update_statement_item;
    update_statement_item.caption= _("Update Statement");
    update_statement_item.name= "table_update_statement";
    update_statement_item.enabled= false;

    bec::MenuItem delete_statement_item;
    delete_statement_item.caption= _("Delete Statement");
    delete_statement_item.name= "table_delete_statement";
    delete_statement_item.enabled= false;

    bec::MenuItem create_statement_item;
    create_statement_item.caption= _("Create Statement");
    create_statement_item.name= "table_create_statement";
    create_statement_item.enabled= false;
    
    bec::MenuItem create_item;
    create_item.caption= _("Create...");
    create_item.name= "create";
    create_item.enabled= false;

    // This is needed to differentiate betwee a create for procedures or functions
    // Only used on Create Function
    bec::MenuItem create_item2;
    create_item2.caption= _("Create...");
    create_item2.name= "create2";
    create_item2.enabled= false;

    bec::MenuItem alter_item;
    alter_item.caption= _("Alter...");
    alter_item.name= "alter";
    alter_item.enabled= false;

    bec::MenuItem drop_item;
    drop_item.caption= _("Drop...");
    drop_item.name= "drop";
    drop_item.enabled= false;

    if (nodes.size() > 1)
    {
      // Go through all nodes and see if they belong to depths 1 or 3 (the schema and object node levels).
      // Only then we can do object actions.
      int type = -1; // -1 = no object, -2 = mixed, object type in other case
      int count = 0;
      int non_db_objects = 0;

      bool can_create = true;
      bool can_drop = true;
      bool can_alter = true;

      std::list<mforms::TreeNodeRef>::const_iterator index, end = nodes.end();
      for (index = nodes.begin(); index != end; index++)
      {
        mforms::TreeNodeRef node = *index;
        LSTData* pdata = dynamic_cast<LSTData*>(node->get_data());

        ObjectType current_type = pdata ? pdata->get_type() : Any;

        if (is_object_type(DatabaseObject, current_type) || is_object_type(ColumnObject, current_type))
        {
          if (is_object_type(ColumnObject, current_type))
          {
            can_create = false;
            can_drop = false;
            can_alter = false;
          }

          if (type == -1)
            type = current_type;
          else if (type != current_type)
            type = -2;
        }
        else
          non_db_objects++;
      }

      // Calculates the right object count for Alter and Drop operations...
      count = nodes.size() - non_db_objects;
      
      if (type != -1)
      {
        clipboard_copy_item.enabled = true;
        editor_insert_item.enabled = true;
        object_name_long_item.enabled = true;
        drop_item.enabled = can_drop;
        create_statement_item.enabled = can_create;
        alter_item.enabled = can_alter;

        switch (type)
        {
        case Table:
          drop_item.caption= base::strfmt(_("Drop %i Tables..."), count);
          view_item.enabled = true;
          edit_item.enabled = true;
          alter_item.caption = base::strfmt(_("Alter %i Tables..."), count);
          object_name_short_item.enabled= true;
          select_statement_item.enabled= true;
          insert_statement_item.enabled= true;
          update_statement_item.enabled= true;
          delete_statement_item.enabled= true;
          break;
        case TableColumn:
          drop_item.enabled = false;
          view_item.enabled = true;
          view_item.name += "_columns";
          edit_item.enabled = true;
          edit_item.name += "_columns";
          select_statement_item.caption = _("Select Columns Statement");
          object_name_short_item.enabled= true;
          object_name_short_item.name += "_columns";
          object_name_long_item.name += "_columns";
          select_statement_item.enabled= true;
          select_statement_item.name += "_columns";
          insert_statement_item.enabled= true;
          insert_statement_item.name += "_columns";
          update_statement_item.enabled= true;
          update_statement_item.name += "_columns";
          create_statement_item.enabled = false;
          alter_item.enabled = false;
          break;
        case ViewColumn:
          drop_item.enabled = false;
          view_item.enabled = true;
          view_item.name += "_columns";
          select_statement_item.caption = _("Select Columns Statement");
          object_name_short_item.enabled= true;
          object_name_short_item.name += "_columns";
          object_name_long_item.name += "_columns";
          select_statement_item.enabled= true;
          select_statement_item.name += "_columns";
          create_statement_item.enabled = false;
          alter_item.enabled = false;
          break;
        case View:
          drop_item.caption= base::strfmt(_("Drop %i Views..."), count);
          alter_item.caption = base::strfmt(_("Alter %i Views..."), count);
          object_name_short_item.enabled= true;
          select_statement_item.enabled= true;
          create_statement_item.enabled= true;
          break;
        case Procedure:
          drop_item.caption= base::strfmt(_("Drop %i Procedures..."), count);
          alter_item.caption = base::strfmt(_("Alter %i Procedures..."), count);
          object_name_short_item.enabled= true;
          break;
        case Function:
          drop_item.caption= base::strfmt(_("Drop %i Functions..."), count);
          alter_item.caption = base::strfmt(_("Alter %i Functions..."), count);
          object_name_short_item.enabled= true;
          break;
        case Schema:
          drop_item.caption = base::strfmt(_("Drop %i Schemas..."), count);
          create_item.enabled = true;
          create_item.caption = _("Create Schema...");
          alter_item.caption = base::strfmt(_("Alter %i Schemas..."), count);
          object_name_long_item.caption = _("Name");
          break;
        case -2: // If there's at least column involved these caption have no effect.
          drop_item.caption= base::strfmt(_("Drop %i Objects..."), count);
          alter_item.caption = base::strfmt(_("Alter %i Objects..."), count);
          object_name_short_item.enabled= true;
          break;
        default:
          break;
        }
      }
    }
    else // nodes.size() <= 1
    {
      mforms::TreeNodeRef node;
      if (nodes.size())
        node = nodes.front();

      int type = -1;
      if (node)
      {
        LSTData* pdata = dynamic_cast<LSTData*>(node->get_data());

        if (pdata)
          type = pdata->get_type();
        else
        {
          if (node->get_tag() == TABLES_TAG)
            type = TableCollection;
          else if (node->get_tag() == VIEWS_TAG)
            type = ViewCollection;
          else if (node->get_tag() == ROUTINES_TAG)
            type = RoutineCollection;
          else if (node->get_tag() == COLUMNS_TAG)
            type = ColumnCollection;
        }
      }

      switch (type)
      {
        case Schema:
          object_name_long_item.caption= _("Name");
          object_name_long_item.enabled= true;
          active_schema_item.enabled= true;
          filter_schema_item.enabled= true;
          clipboard_copy_item.enabled= true;
          editor_insert_item.enabled= true;
          create_item.enabled= true;
          create_item.caption= _("Create Schema...");
          alter_item.caption= _("Alter Schema...");
          drop_item.caption= _("Drop Schema...");
          alter_item.enabled= true;
          drop_item.enabled= true;
          create_statement_item.enabled= true;
          break;
        case Table:
          alter_item.caption= _("Alter Table...");
          drop_item.caption= _("Drop Table...");
          edit_item.enabled= true;
          view_item.enabled= true;
          select_statement_item.enabled= true;
          insert_statement_item.enabled= true;
          update_statement_item.enabled= true;
          delete_statement_item.enabled= true;
          create_statement_item.enabled= true;
          clipboard_copy_item.enabled= true;
          editor_insert_item.enabled= true;
          object_name_short_item.enabled= true;
          object_name_long_item.enabled= true;
          create_statement_item.enabled= true;
          alter_item.enabled= true;
          drop_item.enabled= true;
          break;
        case View:
          alter_item.caption= _("Alter View...");
          drop_item.caption= _("Drop View...");
          view_item.enabled= true;
          select_statement_item.enabled= true;
          create_statement_item.enabled= true;
          clipboard_copy_item.enabled= true;
          editor_insert_item.enabled= true;
          object_name_short_item.enabled= true;
          object_name_long_item.enabled= true;
          create_statement_item.enabled= true;
          alter_item.enabled= true;
          drop_item.enabled= true;
          break;
        case Procedure:
          alter_item.caption= _("Alter Procedure...");
          drop_item.caption= _("Drop Procedure...");

          clipboard_copy_item.enabled= true;
          editor_insert_item.enabled= true;
          object_name_short_item.enabled= true;
          object_name_long_item.enabled= true;
          create_statement_item.enabled= true;
          alter_item.enabled= true;
          drop_item.enabled= true;
          break;
        case Function:
          alter_item.caption= _("Alter Function...");
          drop_item.caption= _("Drop Function...");

          clipboard_copy_item.enabled= true;
          editor_insert_item.enabled= true;
          object_name_short_item.enabled= true;
          object_name_long_item.enabled= true;
          create_statement_item.enabled= true;
          alter_item.enabled= true;
          drop_item.enabled= true;
          break;
        case TableCollection: // Table
          create_item.caption= _("Create Table...");
          create_item.enabled= true;
          break;
        case ViewCollection: // View
          create_item.caption= _("Create View...");
          create_item.enabled= true;
          break;
        case RoutineCollection: // SP
          create_item.caption= _("Create Procedure...");
          create_item.enabled= true;
          create_item2.caption= _("Create Function...");
          create_item2.enabled= true;
          break;                  
        case ViewColumn:
          clipboard_copy_item.enabled= true;
          editor_insert_item.enabled= true;
          view_item.enabled = true;
          view_item.name += "_columns";
          select_statement_item.caption = _("Select Columns Statement");
          object_name_short_item.enabled= true;
          object_name_short_item.name += "_columns";
          object_name_long_item.enabled= true;
          object_name_long_item.name += "_columns";
          select_statement_item.enabled= true;
          select_statement_item.name += "_columns";
          break;
        case ColumnCollection:
        case TableColumn:
          clipboard_copy_item.enabled= true;
          editor_insert_item.enabled= true;
          view_item.enabled = true;
          view_item.name += "_columns";
          edit_item.enabled = true;
          edit_item.name += "_columns";
          select_statement_item.caption = _("Select Columns Statement");
          object_name_short_item.enabled= true;
          object_name_short_item.name += "_columns";
          object_name_long_item.enabled= true;
          object_name_long_item.name += "_columns";
          select_statement_item.enabled= true;
          select_statement_item.name += "_columns";
          insert_statement_item.enabled= true;
          insert_statement_item.name += "_columns";
          update_statement_item.enabled= true;
          update_statement_item.name += "_columns";
          break;
        default:
          break;
      }
    }
    
    if (view_item.enabled)
      items.push_back(view_item);
    if (edit_item.enabled)
      items.push_back(edit_item);
    if (active_schema_item.enabled)
    {
      items.push_back(active_schema_item);
      items.push_back(filter_schema_item);
      bec::MenuItem item;
      item.type= MenuSeparator;
      items.push_back(item);
    }
    if (clipboard_copy_item.enabled)
    {
      MenuItemList subitems;
      subitems.push_back(object_name_short_item);
      subitems.push_back(object_name_long_item);
      subitems.push_back(select_statement_item);
      subitems.push_back(insert_statement_item);
      subitems.push_back(update_statement_item);
      subitems.push_back(delete_statement_item);
      subitems.push_back(create_statement_item);
      for (MenuItemList::iterator i= subitems.begin(), end= subitems.end(); i != end; ++i)
      {
        if (!i->enabled)
          continue;
        i->name= "clipboard_" + i->name;
        clipboard_copy_item.subitems.push_back(*i);
      }
      items.push_back(clipboard_copy_item);
    }
    if (editor_insert_item.enabled)
    {
      MenuItemList subitems;
      subitems.push_back(object_name_short_item);
      subitems.push_back(object_name_long_item);
      subitems.push_back(select_statement_item);
      subitems.push_back(insert_statement_item);
      subitems.push_back(update_statement_item);
      subitems.push_back(delete_statement_item);
      subitems.push_back(create_statement_item);
      for (MenuItemList::iterator i= subitems.begin(), end= subitems.end(); i != end; ++i)
      {
        if (!i->enabled)
          continue;
        i->name= "editor_" + i->name;
        editor_insert_item.subitems.push_back(*i);
      }
      items.push_back(editor_insert_item);
    }
    if (!items.empty() && (alter_item.enabled || create_item.enabled || drop_item.enabled))
    {
      item.enabled= false;
      item.type = MenuSeparator;
      items.push_back(item);
    }
    if (alter_item.enabled)
      items.push_back(alter_item);
    if (create_item.enabled)
      items.push_back(create_item);
    if (create_item2.enabled)
      items.push_back(create_item2);
    if (drop_item.enabled)
      items.push_back(drop_item);
  }

  try
  {
    mforms::TreeNodeRef first_node = nodes.size() ? nodes.front() : mforms::TreeNodeRef();
    if (first_node)
    {
      LSTData* pdata = dynamic_cast<LSTData*>(first_node->get_data());
      bec::MenuItem item;

      std::string schema;
      std::string object;
      std::string type;

      if (pdata)
      {
        schema = get_schema_name(first_node);
        object=first_node->get_string(0);

        switch(pdata->get_type())
        {
          case Schema:
            object="";
            type = "schema";
            break;
          case Table:
            type = "table";
            break;
          case View:
            type = "view";
            break;

          // TODO: This will have to be updated once the routines are split to
          //       procedures and functions on the rest of the code
          case Procedure:
          case Function:
            type = "routine";
            break;
          default:
            break;
        }
    
        if (!type.empty() && object_plugin_items_slot)
        {
          // plugin items
          bec::MenuItemList plugin_items(object_plugin_items_slot(schema, object, type));

          if (!plugin_items.empty())
          {
            bec::MenuItem item;
            item.type = MenuSeparator;
            items.push_back(item);    
            items.insert(items.end(), plugin_items.begin(), plugin_items.end());
          }
        }
      }
    }
  }
  catch (const std::exception &exc)
  {
    g_message("exception fetching context menu items for live tree: %s", exc.what());
  }
  
  {
    bec::MenuItem item;

    if (!items.empty())
    {
      item.type = MenuSeparator;
      items.push_back(item);    
    }
    item.type = MenuAction;
    item.caption= _("Refresh All");
    item.name= "refresh";
    items.push_back(item);
  }

  return items;
}

bool LiveSchemaTree::activate_popup_item_for_nodes(const std::string &name, const std::list<mforms::TreeNodeRef> &unsorted_nodes)
{
  std::list<mforms::TreeNodeRef>::const_iterator index, end = unsorted_nodes.end();
  std::vector<ChangeRecord> changes;
  
  mforms::TreeNodeRef pnode;
  LSTData* pdata;

  std::string schema_name = "";
  std::string object_name = "";
  std::string object_detail = "";
  ObjectType type = Any;


  if (boost::shared_ptr<Delegate> delegate = _delegate.lock())
  {
    if (name == "refresh")
    {
      delegate->tree_refresh();
      return true;
    }
        
    if (name == "alter" || name == "drop")
    {
      for(index = unsorted_nodes.begin(); index != end; index++)
      {
        pnode = (*index);
        pdata = dynamic_cast<LSTData*>(pnode->get_data());

        if (pdata)
        {
          schema_name = "";
          object_name = "";
          type = pdata->get_type();

          if (is_object_type(DatabaseObject, type))
          {
            if (type != Schema)
              schema_name = get_schema_name(pnode);

            object_name = pnode->get_string(0);

            ChangeRecord record = { type, schema_name, object_name, "" };
            changes.push_back(record);
          }
        }
      }

      if (changes.size())
      {
        if (name == "alter")
          delegate->tree_alter_objects(changes);
        else
          delegate->tree_drop_objects(changes);
      }

      return true;
    }
    
    bool clipboard_or_editor = (0 == name.find("clipboard_")) || (0 == name.find("editor_"));

    if ((0 == name.find("select_data")) || (0 == name.find("edit_data")) ||
        clipboard_or_editor)
    {
      for(index = unsorted_nodes.begin(); index != end; index++)
      {
        pnode = (*index);
        pdata = dynamic_cast<LSTData*>(pnode->get_data());

        if (pdata)
        {
          schema_name = "";
          object_name = "";
          object_detail = "";
          type = pdata->get_type();
          schema_name = get_schema_name(pnode);
          
          // Clipboard or editor options should be available for Schemas too, otherwise
          // only tables, views and routines
          if (is_object_type(clipboard_or_editor ? DatabaseObject : SchemaObject, type))
            object_name = pnode->get_string(0);
          else
          {
            if (pdata->get_type() == TableColumn)
            {
              object_name = pnode->get_parent()->get_parent()->get_string(0);
              object_detail = pnode->get_string(0);
              type = Table;
            }
            else if(pdata->get_type() == ViewColumn)
            {
              object_name = pnode->get_parent()->get_string(0);
              object_detail = pnode->get_string(0);
              type = View;
            }
          }

          if (object_name.length() > 0)
          {
            ChangeRecord record = { type, schema_name, object_name, object_detail };
            changes.push_back(record);
          }
        }
        else
        {
          if (pnode->get_tag() == COLUMNS_TAG)
          {
            int size = pnode->count();
            mforms::TreeNodeRef child;

            schema_name = get_schema_name(pnode);
            object_name = pnode->get_parent()->get_string(0);

            for(int index = 0; index < size; index++)
            {
              child = pnode->get_child(index);
              ChangeRecord record = { Table, schema_name, object_name, child->get_string(0) };
              changes.push_back(record);
            }
          }
        }
      }

      if (changes.size())
        delegate->tree_activate_objects(name, changes);
      
      return true;    
    }
    
    // Note: create2 was created to differentiate between a create for functions from
    //       one for procedures as both are active on the same node: Routines Collection
    if (name == "create" || name == "create2")
    {
      std::string schema_name;

      if (unsorted_nodes.size() == 0)
        type = Schema;
      else
      {
        pnode = unsorted_nodes.front();
        pdata = dynamic_cast<LSTData*>(pnode->get_data());
        
        schema_name = get_schema_name(pnode);
        
        if (pdata)
        {
          if (is_object_type(DatabaseObject, pdata->get_type()))
          {
            type = pdata->get_type();

            if (type == Schema)
              schema_name = "";
          }
        }
        else
        {
          if(pnode->get_tag() == TABLES_TAG)
            type = Table;
          else if(pnode->get_tag() == VIEWS_TAG)
            type = View;
          else if(pnode->get_tag() == ROUTINES_TAG)
          {
            if (name == "create")
              type = Procedure;
            else
              type = Function;
          }
        }
      }
        
      // The call to create objects will occur if the type is not Any
      if (type != Any)
        delegate->tree_create_object(type, schema_name, "");

      return true;
    }
    
    if (name == "set_active_schema")
    {
      pnode = unsorted_nodes.front();
      ChangeRecord record = { Schema, "", pnode->get_string(0), "" };
      changes.push_back(record);
      delegate->tree_activate_objects("activate", changes);
      return true;
    }
    if (name == "filter_schema")
    {
      pnode = unsorted_nodes.front();
      ChangeRecord record = { Schema, "", pnode->get_string(0), "" };
      changes.push_back(record);
      delegate->tree_activate_objects("filter", changes);
      return true;
    }
    
    try
    {
      bec::MenuItem item;
      
      std::string object;
      std::string str_type;

      pnode = unsorted_nodes.size() ? unsorted_nodes.front() : mforms::TreeNodeRef();

      if (pnode)
      {
        pdata = dynamic_cast<LSTData*>(pnode->get_data());

        if(pdata)
        {
          schema_name = this->get_schema_name(pnode);

          switch(pdata->get_type())
          {
            case Schema:
              str_type = "schema";
              break;
            case Table:
              object_name = pnode->get_string(0);
              str_type = "table";
              break;
            case View:
              object_name = pnode->get_string(0);
              str_type = "view";
              break;

            // TODO: These two should send "procedure" and "function" accordingly once
            //       the split is done on all the code.
            case Procedure:
            case Function:
              object_name = pnode->get_string(0);
              str_type = "routine";
              break;
            default:
              object_name = "";
              str_type = "";
              break;
          }

          if (!str_type.empty())
            return call_object_plugin_items_slot(name, schema_name, object_name, str_type);
        }
      }
    }
    catch (const std::exception &exc)
    {
      g_message("exception fetching context menu items for live tree: %s", exc.what());
    }
  }

  return false;
}


bool LiveSchemaTree::is_schema_contents_enabled() const
{
  return _is_schema_contents_enabled;
}


void LiveSchemaTree::is_schema_contents_enabled(bool value)
{
  _is_schema_contents_enabled= value;
}

//--------------------------------------------------------------------------------------------------

std::string LiveSchemaTree::get_filter_wildcard(const std::string& filter, FilterType type)
{
  std::string wildcard = filter;
  if (filter.length() == 0)
    wildcard = "*";

  switch(type)
  {
    case LocalRegexp:
    case LocalLike:
    case RemoteRegexp:
      if ('*' != wildcard.at(wildcard.length() - 1))
        wildcard += "*";
      break;
    case RemoteLike:
      base::replace(wildcard, "%","\\%");
      base::replace(wildcard, "_","\\_");
      base::replace(wildcard, "?","_");
      base::replace(wildcard, "*","%");

      if ('%' != wildcard.at(wildcard.length() - 1))
        wildcard += "%";

      break;
  }

  return wildcard;
}

//--------------------------------------------------------------------------------------------------
void LiveSchemaTree::filter_data()
{
  _enabled_events = false;

  // Removes all the objects on the target tree
  _model_view->clear();

  mforms::TreeNodeRef base_root = _base->_model_view->root_node();
  mforms::TreeNodeRef this_root = _model_view->root_node();
  filter_children(Schema, base_root, this_root, _schema_pattern);

  // To keep the active schema on the filtered tree
  set_active_schema(_base->_active_schema);

  _enabled_events = true;
}

//--------------------------------------------------------------------------------------------------

/*
*  filter_children_collection: will trigger a children copy for the collection nodes of the given source
*                              right now the pattern is only used for nodes on schema collections
*/
void LiveSchemaTree::filter_children_collection(mforms::TreeNodeRef& source, mforms::TreeNodeRef& target)
{
  LSTData* pdata = dynamic_cast<LSTData*>(source->get_data());

  if(pdata)
  {
    mforms::TreeNodeRef source_collection;
    mforms::TreeNodeRef target_collection;

    switch(pdata->get_type())
    {
    case Schema:
      {
        source_collection = source->get_child(TABLES_NODE_INDEX);
        target_collection = target->get_child(TABLES_NODE_INDEX);
        bool found_tables   = filter_children(Table,   source_collection, target_collection, _object_pattern);

        source_collection = source->get_child(VIEWS_NODE_INDEX);
        target_collection = target->get_child(VIEWS_NODE_INDEX);
        bool found_views    = filter_children(View,    source_collection, target_collection, _object_pattern);

        source_collection = source->get_child(ROUTINES_NODE_INDEX);
        target_collection = target->get_child(ROUTINES_NODE_INDEX);

        // When called for Procedures or Functions the filter process will be executed on both types of
        // objects as they are contained on the same collection
        bool found_procedures = filter_children(Procedure, source_collection, target_collection, _object_pattern);

        if (_object_pattern && !(found_tables || found_views || found_procedures))
          target->remove_from_parent();
      }
      break;
    case Table:
      source_collection = source->get_child(TABLE_COLUMNS_NODE_INDEX);
      target_collection = target->get_child(TABLE_COLUMNS_NODE_INDEX);
      filter_children(TableColumn, source_collection, target_collection);

      source_collection = source->get_child(TABLE_INDEXES_NODE_INDEX);
      target_collection = target->get_child(TABLE_INDEXES_NODE_INDEX);
      filter_children(Index, source_collection, target_collection);

      source_collection = source->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX);
      target_collection = target->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX);
      filter_children(ForeignKey, source_collection, target_collection);

      source_collection = source->get_child(TABLE_TRIGGERS_NODE_INDEX);
      target_collection = target->get_child(TABLE_TRIGGERS_NODE_INDEX);
      filter_children(Trigger, source_collection, target_collection);
      break;
    case View:
      filter_children(ViewColumn,source, target);
      break;
    default:
      break;
    }
  }
}
/*
*  filter_children: will create duplicate objects in target for the children in source matching the given pattern
*                   if no pattern is specified, all the children will be cuplicated
*/
bool LiveSchemaTree::filter_children(ObjectType type, mforms::TreeNodeRef& source, mforms::TreeNodeRef& target, GPatternSpec* pattern)
{
  // Validation to occur only on schema child objects if a pattern is set
  bool validate = is_object_type(DatabaseObject, type) && pattern;


  // Clears the collection...
  target->remove_children();

  int count = source->count();
  for(int index = 0; index < count; index++)
  {
    mforms::TreeNodeRef source_node = source->get_child(index);
    if (!validate || g_pattern_match_string(pattern, base::toupper(source_node->get_string(0)).c_str()))
    {
      // This is needed because Procedures and Functions are contained on the same collection...
      // So the real type needs to be retrieved from the found node.
      if (type == Procedure || type == Function)
      {
        LSTData* pdata = dynamic_cast<LSTData*>(source_node->get_data());
        type = pdata->get_type();
      }

      std::vector<mforms::TreeNodeRef> group_added_nodes;
      _node_collections[type].captions.clear();
      _node_collections[type].captions.push_back(source_node->get_string(0));
      group_added_nodes = target->add_node_collection(_node_collections[type]);
      setup_node(group_added_nodes[0], type, source_node->get_data(), true);

      // For each found node, continues with their children...
      if (type == Schema || type == Table || type == View)
        filter_children_collection(source_node, group_added_nodes[0]);

      if (source_node->is_expanded() != group_added_nodes[0]->is_expanded())
      {
        if (group_added_nodes[0]->is_expanded())
          group_added_nodes[0]->expand();
        else
          group_added_nodes[0]->collapse();
      }
    }
  }

  if (source->is_expanded() != target->is_expanded())
  {
    if (source->is_expanded())
      target->expand();
    else
      target->collapse();
  }

  return target->count() > 0;
}

//--------------------------------------------------------------------------------------------------

void LiveSchemaTree::clean_filter()
{
  if (_filter.length() > 0)
  {
    _filter_type = Any;
    _filter = "";

    g_pattern_spec_free(_schema_pattern);
    _schema_pattern = NULL;
  
    if(_object_pattern)
    {
      g_pattern_spec_free(_object_pattern);
      _object_pattern = NULL;
    }
  }
}

void LiveSchemaTree::set_filter(std::string filter)
{
  // Cleans the previous filter if any...
  clean_filter();

  if(filter.length() > 0)
  {
    _filter = filter;

    std::vector<std::string> filters = base::split(_filter, ".", 2);

    // Gets the filter wildcard strings
    std::string schema_filter = base::toupper(get_filter_wildcard(filters[0], LocalLike));
    std::string object_filter = base::toupper(get_filter_wildcard(filters.size() > 1 ? filters[1] : "", LocalLike));

    _schema_pattern = g_pattern_spec_new(schema_filter.c_str());

    if (filters.size() > 1 && object_filter != "*")
        _object_pattern = g_pattern_spec_new(object_filter.c_str());
  }
}

void LiveSchemaTree::set_model_view(mforms::TreeNodeView* target)
{
  _model_view = target;

  if (_model_view)
  {
    scoped_connect(_model_view->signal_expand_toggle(),boost::bind(&LiveSchemaTree::expand_toggled, this, _1, _2));
    scoped_connect(_model_view->signal_node_activated(),boost::bind(&LiveSchemaTree::node_activated, this, _1, _2));
  }
}

/* Function : compare_identifiers
 * Description : comparison function to be used on the sorting process
 * Return Value : following the STL requirements should return true if the
 *                first string is lower than the second
 */
bool LiveSchemaTree::compare_identifiers(const std::string &first, const std::string &second)
{
  return string_compare(first, second) < 0;
}

/* Function : string_compare
 * Description : compares two strings either case sensitive or no
 * Parameters : the strings to be compared
 * Return Value:
 *              0 - If the strings are equal
 *              Negative value if the first string is lower than the second
 *              Positive value if the first string is bigger than the second
 */
 int LiveSchemaTree::string_compare(const std::string &first, const std::string &second)
{
  int ret_val = 0;
  
  if (_case_sensitive_identifiers)
  {
    if (first < second)
      ret_val = -1;
    else if (first > second)
      ret_val = 1;
  }
  else
    ret_val = g_strcasecmp(first.c_str(), second.c_str());
    
  return ret_val;
}

/* Function : binary_search_node
 * Description : Search a child on a given node based on it's name, assumes the children
 *               are sorted
 * Parameters : parent, the node where the search is being performed
 *              min, max : usual parameters on the binary search
 *              name : the name of the child node being searched
 *              type : the type of the child node to be searched (not being used)
 *              position : output parameter to store:
 *                        - If the node is found, the position in parent's list
 *                        - If not found, the position where it should be located if it is going to be added
 * Return Value : if found, the child node
 */
mforms::TreeNodeRef LiveSchemaTree::binary_search_node(const mforms::TreeNodeRef& parent, int min, int max, const std::string &name, ObjectType type, int &position)
{
  if (max < min)
    return mforms::TreeNodeRef();
  else
  {
    int middle = (max+min) / 2;
    position = middle;
    
    mforms::TreeNodeRef node = parent->get_child(middle);
    
    int comparison = string_compare(node->get_string(0), name);
    
    if (comparison < 0)
      return binary_search_node(parent, middle + 1, max, name, type, ++position);
    else if (comparison > 0)
      return binary_search_node(parent, min, middle - 1, name, type, position);
    else
      return node;     
  }
}

/* Function : get_child_node
 * Description : Searches a specific child on a given node, supports both sequential search
 *               and binary search. Binary search should be used when searching for schemas,
 *               tables, views and routines which are sorted. Sequential search is there for 
 *               the non sorted nodes.
 */
mforms::TreeNodeRef LiveSchemaTree::get_child_node(const mforms::TreeNodeRef& parent, const std::string& name, ObjectType type, bool binary_search)
{
  int last_position = 0;
  bool found = false;
  int ps, pe, fs, fe;
  mforms::TreeNodeRef child;

  if (binary_search)
  {
    // Routines require a special handling
    if (type == Procedure || type == Function)
    {
      // Gets the ranges where the procedures and functions exist
      // on the parent list
      find_routine_ranges(parent, ps, pe, fs, fe);
      
      // Now searches for the correct node type
      if (type == Procedure && ps != -1)
        child = binary_search_node(parent, ps, pe, name, type, last_position);
      else if (type == Function && fs != -1)
        child = binary_search_node(parent, fs, fe, name, type, last_position);
    }
    else if (parent && parent->count())
      child = binary_search_node(parent, 0, parent->count()-1, name, type, last_position);
      
    if (child)
      found = true;
  }
  else
  {
    if (parent && parent->count())
    {
      for(int index=0; !found && index < parent->count(); index++)
      {
        child = parent->get_child(index);
        
        found = (string_compare(child->get_string(0), name) == 0);
        
        if (found && type != Any)
        {
          LSTData* pdata = dynamic_cast<LSTData*>(child->get_data());
          found = (pdata && type == pdata->get_type());          
        }
      }
    }
  }

  return found ? child : mforms::TreeNodeRef();
}

/* Function : find_child_position
 * Description : given a child name, searches it on the received parent. This function is
 *               intended for usage on sorted collections of nodes
 * Parameters : parent, the node where the search will occur
 *              type, the type of the object being searched
 *              position, output parameter containing:
 *                - If found, the index of the found node
 *                - If not found, the position where the node should be (i.e. to add it there)
 * Return value : boolean value indicating whether the node was found or not
 */
bool LiveSchemaTree::find_child_position(const mforms::TreeNodeRef& parent, const std::string& name, ObjectType type, int &position)
{
  mforms::TreeNodeRef child;
  
  position = 0;
  
  if (type == Procedure || type == Function)
  {
    int ps, pe, fs, fe;
    find_routine_ranges(parent, ps, pe, fs, fe);
    
    if (type == Procedure)
    {
      if (ps == -1)
        position = (fs == -1) ? -1 : 0;
      else
        child = binary_search_node(parent, ps, pe, name, type, position);
    }
    else
    {
      if (fs == -1)
        position = -1;
      else
        child = binary_search_node(parent, fs, fe, name, type, position);
    }
  }
  else
  {
    if (parent && parent->count())
      child = binary_search_node(parent, 0, parent->count()-1, name, type, position);
  }
  
  if (parent->count() == position)
    position = -1;

  return child ? true : false;
}

/* Function: find_routine_ranges
 * Description: traverses the child items of a ROUTINES node to identify the start/end
 *              positions of the procedures and functions
 * Parameters: parent, the ROUTINES node where the search will be done
 *             rest, output parameters containing the indexes of the procedures and function
 *             nodes, if no nodes found of a specific type, -1 is returned on its associated parameters
 */
void LiveSchemaTree::find_routine_ranges(const mforms::TreeNodeRef& parent, int& pstart, int& pend, int& fstart, int& fend)
{
  pstart = -1;
  pend = -1;
  fstart = -1;
  fend = -1;
  
  if (parent->count())
  {
    for (int index = 0; index < parent->count(); index++)
    {
      LSTData* pdata = dynamic_cast<LSTData*>(parent->get_child(index)->get_data());
      if (pdata->get_type() == Procedure)
      {
        if (pstart == -1)
        {
          pstart = index;
          pend = index;
        }
        else
          pend = index;
      }
      else
      {
        if (fstart == -1)
        {
          fstart = index;
          fend = index;
        }
        else
          fend = index;
      }
    }
  }
}

void LiveSchemaTree::update_schemata(std::list<std::string> &schema_list)
{
  mforms::TreeNodeRef schema_node;

  if (_model_view)
  {
    mforms::TreeNodeRef root = _model_view->root_node();
    
    schema_list.sort(boost::bind(&LiveSchemaTree::compare_identifiers, this, _1, _2));
    
    update_node_children(root, schema_list, Schema, true);

    // Re-sets the active schema at view level
    if (_active_schema.length())
      set_active_schema(_active_schema);

    int total_schemas = root->count();
    for(int index = 0; index < total_schemas; index++)
    {
      schema_node = root->get_child(index);
      SchemaData *data = dynamic_cast<SchemaData*>(schema_node->get_data());

      if (data->fetched)
      {
        data->fetched = false;

        if (schema_node->is_expanded())
          load_schema_content(schema_node);
      }
    }
  }
}

void LiveSchemaTree::expand_toggled(mforms::TreeNodeRef node, bool value)
{
  if (_enabled_events)
  {
    LSTData *node_data = dynamic_cast<LSTData*> (node->get_data());

    if (value)
    {
      if (node_data)
      {
        switch(node_data->get_type())
        {
        case Schema:
          load_schema_content(node);
          break;
        case Table:
          load_table_details(node, COLUMN_DATA|INDEX_DATA);
          break;
        case View:
          {
            load_table_details(node, COLUMN_DATA);

            ViewData *pdata = dynamic_cast<ViewData*>(node->get_data());
            if (pdata->columns_load_error)
            {
              node->remove_children();
              update_node_icon(node);
            }
          }
          break;
        default:
          break;
        }
      }
      else
      {
        std::string node_tag = node->get_tag();
        mforms::TreeNodeRef parent = node->get_parent();

        if (node_tag == TRIGGERS_TAG)
          load_table_details(parent, TRIGGER_DATA);
        else if (node_tag == FOREIGN_KEYS_TAG)
          load_table_details(parent, FK_DATA);
      }
    }

    // If there's a base tree the expansion state needs to be propagated to that tree
    // Events should be disabled there so only the expand state will be propagated
    if (_base)
    {
      std::vector<std::string> path = get_node_path(node);
      mforms::TreeNodeRef base_node = _base->get_node_from_path(path);
      if (value)
        base_node->expand();
      else
        base_node->collapse();
    }
  }
}

void LiveSchemaTree::node_activated(mforms::TreeNodeRef node, int column)
{
  LSTData *node_data = dynamic_cast<LSTData*> (node->get_data());

  if (node_data)
  {
    std::string node_name = node->get_string(0);

    switch(node_data->get_type())
    {
    case Schema:
      {
        std::vector<ChangeRecord> changes;
      
        ChangeRecord record = { Schema, "", node_name, "" };
        changes.push_back(record);

        if (boost::shared_ptr<Delegate> delegate = _delegate.lock())
          delegate->tree_activate_objects("activate", changes);
      }
      break;
    default:
      node_name = base::quote_identifier_if_needed(node_name, '`');
      sql_editor_text_insert_signal(node_name);
      break;
    }
  }
}

/*
* get_schema_name: finds the parent schema for a specific node in the tree
*/
std::string LiveSchemaTree::get_schema_name(const mforms::TreeNodeRef& node)
{
  std::string ret_val;
  mforms::TreeNodeRef temp_node = node;
  mforms::TreeNodeRef parent = temp_node->get_parent();

  // Safety validation, all nodes in the LST should have a parent
  // Even schema nodes which it's parent is root
  if (parent)
  {
    while(parent->get_parent())
    {
      temp_node = parent;
      parent = parent->get_parent();
    }

    ret_val = temp_node->get_string(0);
  }

  return ret_val;
}

/*
* get_node_path: Gets the name path to a node from root
*/
std::vector<std::string> LiveSchemaTree::get_node_path(const mforms::TreeNodeRef& node)
{
  std::vector<std::string> path;

  mforms::TreeNodeRef temp_node = node;
  mforms::TreeNodeRef parent = temp_node->get_parent();

  // Safety validation, all nodes in the LST should have a parent
  // Even schema nodes which it's parent is root
  if (parent)
  {
    path.insert(path.begin(), temp_node->get_string(0));

    while(parent->get_parent())
    {
      temp_node = parent;
      path.insert(path.begin(), temp_node->get_string(0));

      parent = parent->get_parent();
    }
  }

  return path;
}

/*
* get_node_from_path: Finds a node in a tree following a name path
* TODO: This will not work in the case of the routines, as there's no way to know if the path is
*       for a procedure or for a function, on this case a sequential search will be done so
*       procedures will be searched first all the time
*/
mforms::TreeNodeRef LiveSchemaTree::get_node_from_path(std::vector<std::string> path)
{
  mforms::TreeNodeRef temp_node = _model_view->root_node();
  size_t index = 0;
  bool error = false;
  bool use_binary_search = true;
  
  while(!error && index < path.size())
  {
    temp_node = get_child_node(temp_node, path[index], Any, use_binary_search);

    if (temp_node && temp_node->is_valid())
    {
      index++;
      
      // Uses binary search only on the db object collection nodes
      std::string tag = temp_node->get_tag();
      
      use_binary_search = (tag == TABLES_TAG ||
                           tag == VIEWS_TAG );
    }
    else
      error = true;
  }

  return error ? mforms::TreeNodeRef() : temp_node;
}

/*
* is_object_type: Validates that the type of a given object is in a specific group
*/
bool LiveSchemaTree::is_object_type(ObjectTypeValidation validation, ObjectType type)
{
  switch(validation)
  {
  case DatabaseObject:
    return (type == Schema || type == Table || type == View || type == Procedure || type == Function);
    break;
  case SchemaObject:
    return (type == Table || type == View || type == Procedure || type == Function);
    break;
  case TableOrView:
    return (type == Table || type == View);
    break;
  case ColumnObject:
	return (type == TableColumn || type == ViewColumn);
	break;
  }

  return false;
}

mforms::TreeNodeRef LiveSchemaTree::insert_node(mforms::TreeNodeRef parent, const std::string& name, ObjectType type)
{
  mforms::TreeNodeRef node;

  int target_position = 0;
  if (!find_child_position(parent, name, type, target_position))
  {
    std::vector<mforms::TreeNodeRef> group_added_nodes;

    _node_collections[type].captions.clear();
    _node_collections[type].captions.push_back(name);
    group_added_nodes = parent->add_node_collection(_node_collections[type], target_position);
    node = group_added_nodes[0];
              
    setup_node(node, type);
  }

  return node;
}

void LiveSchemaTree::reload_object_data(mforms::TreeNodeRef& node)
{
    ViewData* pdata = dynamic_cast<ViewData*>(node->get_data());
    if (pdata)
    {
        short loaded_mask = pdata->get_loaded_mask();

        // This was a successful update so in case the error icon was loaded
        // It needs to be reset
        if (pdata->columns_load_error)
        {
          pdata->columns_load_error = false;
          update_node_icon(node);
        }
        
        if (loaded_mask)
        {
            // Identifies the node expansion state
            bool is_expanded = node->is_expanded();
            int expanded_mask = 0;
            
            if (is_expanded && pdata->get_type() == Table)
            {
                expanded_mask |= node->get_child(TABLE_COLUMNS_NODE_INDEX)->is_expanded() ? COLUMN_DATA : 0;
                expanded_mask |= node->get_child(TABLE_INDEXES_NODE_INDEX)->is_expanded() ? INDEX_DATA : 0;
                expanded_mask |= node->get_child(TABLE_TRIGGERS_NODE_INDEX)->is_expanded() ? TRIGGER_DATA : 0;
                expanded_mask |= node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->is_expanded() ? FK_DATA : 0;
            }
            
            // Invalidates any loaded data on the object
            pdata->set_unloaded_data(loaded_mask);
                
            // Removes any loaded information to allow reload
            discard_object_data(node, loaded_mask);

            // Reloads the previously loaded information if the node is
            // expanded, if not, it will be loaded on expansion
            if (loaded_mask && is_expanded)
            {
                load_table_details(node, loaded_mask);
                
                // Triggers the expansion of the subnodes
                node->expand();
                if (expanded_mask)
                {
                    if (expanded_mask & COLUMN_DATA)
                        node->get_child(TABLE_COLUMNS_NODE_INDEX)->expand();
                    if (expanded_mask & INDEX_DATA)
                        node->get_child(TABLE_INDEXES_NODE_INDEX)->expand();
                    if (expanded_mask & TRIGGER_DATA)
                        node->get_child(TABLE_TRIGGERS_NODE_INDEX)->expand();
                    if (expanded_mask & FK_DATA)
                        node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->expand();
                }
            }
        }
    }
}

void LiveSchemaTree::discard_object_data(mforms::TreeNodeRef& node, int data_mask)
{
    mforms::TreeNodeRef parent_node;
    
    if (data_mask & COLUMN_DATA)
    {
        LSTData* pdata = dynamic_cast<LSTData*>(node->get_data());
        if (pdata->get_type() == Table)
            parent_node = node->get_child(TABLE_COLUMNS_NODE_INDEX);
        else
            parent_node = node;
            
        parent_node->remove_children();
    }
    
    if (data_mask & INDEX_DATA)
    {
        parent_node = node->get_child(TABLE_INDEXES_NODE_INDEX);
        parent_node->remove_children();
    }

    if (data_mask & TRIGGER_DATA)
    {
        parent_node = node->get_child(TABLE_TRIGGERS_NODE_INDEX);
        parent_node->remove_children();
    }
    
    if (data_mask & FK_DATA)
    {
        parent_node = node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX);
        parent_node->remove_children();
    }
}