/* 
 * Copyright (c) 2012 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 _NAME_MAPPING_EDITOR_H_
#define _NAME_MAPPING_EDITOR_H_

#include "mforms/treenodeview.h"
#include "mforms/label.h"
#include "mforms/textbox.h"
#include "mforms/box.h"
#include "mforms/form.h"
#include "mforms/panel.h"
#include "mforms/button.h"
#include "grts/structs.db.h"
#include "grtpp_util.h"
#include "db_mysql_sql_script_sync.h"

class NameMappingEditor : public mforms::Form
{  
public:
  class NodeData : public mforms::TreeNodeData
  {
  public:
    GrtNamedObjectRef object;
    bool target_missing;
    NodeData(GrtNamedObjectRef avalue, bool atarget_missing) : object(avalue), target_missing(atarget_missing) {}
  };
  
  NameMappingEditor(mforms::Form *owner, db_CatalogRef target_catalog, 
                    db_CatalogRef catalog, grt::StringListRef schemas_to_skip)
  : mforms::Form(owner), _catalog(catalog), _target_catalog(target_catalog), _schemas_to_skip(schemas_to_skip), 
    _vbox(false), _tree(TreeShowColumnLines|TreeShowRowLines), _bbox(true)
  {
    set_title(_("Resolve Object Name Mapping"));
    
    _vbox.add(&_heading, false, true);
    _heading.set_text(_("Select an object to change its matching object in the synchronization target."));
    
    _vbox.set_padding(MF_WINDOW_PADDING);
    _vbox.set_spacing(MF_TABLE_ROW_SPACING);
    _vbox.add(&_tree, true, true);
    
    _tree.add_column(IconStringColumnType, _("Model Object"), 140, false);
    _tree.add_column(IconStringColumnType, _("Default Matching Object"), 140, false);
    _tree.add_column(StringColumnType, _("Desired Matching Object"), 140, false);
    _tree.end_columns();
    _tree.signal_changed()->connect(boost::bind(&NameMappingEditor::list_selection_changed, this));
    
    mforms::Panel *p = mforms::manage(new mforms::Panel(mforms::TitledBoxPanel));
    _panel = p;
    p->set_title(_("Change Object Mapping"));

    mforms::Table *table = mforms::manage(new mforms::Table());
    table->set_row_count(3);
    table->set_column_count(2);
    table->set_row_spacing(MF_TABLE_ROW_SPACING);
    table->set_column_spacing(MF_TABLE_COLUMN_SPACING);
    table->set_padding(MF_PANEL_PADDING);
    table->add(mforms::manage(new mforms::Label(_("Name in Model:"), true)), 0, 1, 0, 1, 0);
    table->add(&_model_name, 1, 2, 0, 1, mforms::HFillFlag|mforms::HExpandFlag);
    table->add(mforms::manage(new mforms::Label(_("Original Target Object:"), true)), 0, 1, 1, 2, 0);
    table->add(&_original_name, 1, 2, 1, 2, mforms::HFillFlag|mforms::HExpandFlag);

    mforms::Label *l = mforms::manage(new mforms::Label(_("Change Target Object To:"), true));
    table->add(l, 0, 1, 2, 3, 0);
    table->add(&_remap_selector, 1, 2, 2, 3, mforms::HFillFlag|mforms::HExpandFlag);
    
    scoped_connect(_remap_selector.signal_changed(), boost::bind(&NameMappingEditor::remap_selected, this));

    _vbox.add(p, false, true);
    p->add(table);
    
    _filter_check.set_text("Show mismatches only");
    scoped_connect(_filter_check.signal_clicked(), boost::bind(&NameMappingEditor::update_name_tree, this));
    _vbox.add(&_filter_check, false, true);
    
    _bbox.set_spacing(MF_BUTTON_SPACING);
    
    _ok_button.set_text(_("OK"));
    _cancel_button.set_text(_("Cancel"));
    
    mforms::Utilities::add_end_ok_cancel_buttons(&_bbox, &_ok_button, &_cancel_button);
    _vbox.add(&_bbox, false, true);
    
    set_content(&_vbox);
    
    set_size(540, 500);
    
    update_remap_selector();
    update_name_tree();
  }
  
  
  void apply_changes(TreeNodeRef node)
  {
    for (int c = node->count(), i = 0; i < c; i++)
    {
      TreeNodeRef child = node->get_child(i);
      NodeData *data = dynamic_cast<NodeData*>(child->get_data());
      if (data)
      {
        std::string new_name = node->get_string(2);
        if (!new_name.empty())
          data->object->oldName(new_name);
      }
      apply_changes(child);
    }
  }
  
  bool run()
  {
    if (run_modal(&_ok_button, &_cancel_button))
    {
      apply_changes(_tree.root_node());
      return true;
    }
    return false;
  }

  void list_selection_changed()
  {
    update_remap_selector();
  }

  void remap_selected()
  {
    mforms::TreeNodeRef node = _tree.get_selected_node();
    if (node)
    {
      int i = _remap_selector.get_selected_index();
      if (i > 0)
      {
        std::string item = _remap_selector.get_item_title(i);
        node->set_string(2, item);
      }
      else
        node->set_string(2, "");
    }
  }

  std::list<std::string> get_table_names(db_TableRef model_table)
  {
    std::list<std::string> names;
    if (model_table.is_valid() && model_table->owner().is_valid())
    {
      db_SchemaRef model_schema = db_SchemaRef::cast_from(model_table->owner());
      GRTLIST_FOREACH(db_Schema, _target_catalog->schemata(), schema)
      {
        if (model_schema->oldName() == (*schema)->name())
        {
          GRTLIST_FOREACH(db_Table, (*schema)->tables(), table)
          {
            names.push_back((*table)->name());
          }
          break;
        }
      }
    }
    return names;
  }

  std::list<std::string> get_column_names(db_ColumnRef model_column)
  {
    std::list<std::string> names;
    if (model_column.is_valid())
    {
      db_TableRef model_table = db_TableRef::cast_from(model_column->owner());
      db_SchemaRef model_schema = db_SchemaRef::cast_from(model_table->owner());
      
      GRTLIST_FOREACH(db_Schema, _target_catalog->schemata(), schema)
      {
        if (model_schema->oldName() == (*schema)->name())
        {
          GRTLIST_FOREACH(db_Table, (*schema)->tables(), table)
          {
            if (model_table->oldName() == (*table)->name())
            {
              GRTLIST_FOREACH(db_Column, (*table)->columns(), column)
              {
                names.push_back((*column)->name());
              }
              break;
            }
          }
          break;
        }
      }
    }
    return names;
  }
  
  std::list<std::string> get_view_names(db_ViewRef model_view)
  {
    std::list<std::string> names;
    if (model_view.is_valid() && model_view->owner().is_valid())
    {
      db_SchemaRef model_schema = db_SchemaRef::cast_from(model_view->owner());
      GRTLIST_FOREACH(db_Schema, _target_catalog->schemata(), schema)
      {
        if (model_schema->oldName() == (*schema)->name())
        {
          GRTLIST_FOREACH(db_View, (*schema)->views(), view)
          {
            names.push_back((*view)->name());
          }
          break;
        }
      }
    }
    return names;
  }
  
  std::list<std::string> get_routine_names(db_RoutineRef model_routine)
  {
    std::list<std::string> names;
    if (model_routine.is_valid() && model_routine->owner().is_valid())
    {
      db_SchemaRef model_schema = db_SchemaRef::cast_from(model_routine->owner());
      GRTLIST_FOREACH(db_Schema, _target_catalog->schemata(), schema)
      {
        if (model_schema->oldName() == (*schema)->name())
        {
          GRTLIST_FOREACH(db_Routine, (*schema)->routines(), routine)
          {
            names.push_back((*routine)->name());
          }
          break;
        }
      }
    }
    return names;
  }
  
  
  void update_remap_selector()
  {
    _remap_selector.clear();
    _remap_selector.add_item("Leave Unchanged");

    _model_name.set_text("");
    _original_name.set_text("");
    
    bool enabled = false;
    mforms::TreeNodeRef selected(_tree.get_selected_node());
    if (selected)
    {
      NodeData *data = dynamic_cast<NodeData*>(selected->get_data());
      std::string target;
      if (data && !data->object.is_instance(db_Schema::static_class_name()))
      {
        std::list<std::string> names;
        if (data->object.is_instance(db_Table::static_class_name()))
        {
          db_TableRef table = db_TableRef::cast_from(data->object);
          names = get_table_names(table);
          enabled = true;
          _model_name.set_text(table->name());
          if (data->target_missing)
            _original_name.set_text(base::strfmt("%s (does not exist in target)", table->oldName().c_str()));
          else
            _original_name.set_text(table->oldName());
          target = table->oldName();
        }
        else if (data->object.is_instance(db_View::static_class_name()))
        {
          db_ViewRef view = db_ViewRef::cast_from(data->object);
          names = get_view_names(view);
          enabled = true;
          _model_name.set_text(view->name());
          _original_name.set_text(view->oldName());
          target = view->oldName();
        }
        else if (data->object.is_instance(db_Routine::static_class_name()))
        {
          db_RoutineRef routine = db_RoutineRef::cast_from(data->object);
          names = get_routine_names(routine);
          enabled = true;
          _model_name.set_text(routine->name());
          _original_name.set_text(routine->oldName());
          target = routine->oldName();
        }
        _remap_selector.add_items(names);
        
        if (!selected->get_string(2).empty())
          target = selected->get_string(2);
        
        if (!target.empty())
        {
          int i = _remap_selector.index_of_item_with_title(target);
          if (i >= 0)
            _remap_selector.set_selected(i);
          else
            _remap_selector.set_selected(0);
        }
      }
    }
    _panel->set_enabled(enabled);    
  }
  
  void update_name_tree()
  {
    bool filter = _filter_check.get_active();
    _tree.clear();
    if (_catalog.is_valid())
    {
      GRTLIST_FOREACH(db_Schema, _catalog->schemata(), schema)
      {
        if (_schemas_to_skip.get_index((*schema)->name()) == grt::BaseListRef::npos)
        {
          bool has_anomaly;
          mforms::TreeNodeRef schema_node(_tree.add_node());
          schema_node->set_icon_path(0, "db.Schema.16x16.png");
          schema_node->set_string(0, *(*schema)->name());
          schema_node->set_string(1, *(*schema)->oldName());
          schema_node->set_data(new NodeData(*schema, false));
          
          db_SchemaRef target_schema = find_named_object_in_list(_target_catalog->schemata(), (*schema)->oldName(),
                                                                 false, "name");

          mforms::TreeNodeRef group_node(schema_node->add_child());
          group_node->set_icon_path(0, "qe_sql-editor-tb-icon_open.png");
          group_node->set_string(0, "Tables");
          has_anomaly = false;
          GRTLIST_FOREACH(db_Table, (*schema)->tables(), table)
          {
            db_TableRef target_table;
            if (target_schema.is_valid())
              target_table = find_named_object_in_list(target_schema->tables(), (*table)->oldName(),
                                                       false, "name");

            if (!filter || ((*table)->name() != (*table)->oldName() || !target_table.is_valid()))
            {
              mforms::TreeNodeRef table_node(group_node->add_child());
              
              table_node->set_icon_path(0, "db.Table.16x16.png");
              table_node->set_string(0, *(*table)->name());
              table_node->set_string(1, *(*table)->oldName());
              if (!target_table.is_valid())
              {
                table_node->set_icon_path(1, "change_alert_drop.png");
                table_node->set_data(new NodeData(*table, true));
                has_anomaly = true;
              }
              else
              {
                if ((*table)->name() != (*table)->oldName())
                {
                  table_node->set_icon_path(1, "change_alert_thin.png");
                  has_anomaly = true;
                }
                table_node->set_data(new NodeData(*table, false));
              }
              GRTLIST_FOREACH(db_Column, (*table)->columns(), column)
              {
                mforms::TreeNodeRef column_node(table_node->add_child());
                column_node->set_icon_path(0, "db.Column.16x16.png");
                column_node->set_string(0, *(*column)->name());
                column_node->set_string(1, *(*column)->oldName());
                column_node->set_data(new NodeData(*column, false));
              }
            }
          }
          if (has_anomaly)
            group_node->expand();
          group_node = schema_node->add_child();
          group_node->set_icon_path(0, "qe_sql-editor-tb-icon_open.png");
          group_node->set_string(0, "Views");
          has_anomaly = false;
          GRTLIST_FOREACH(db_View, (*schema)->views(), view)
          {
            db_ViewRef target_view;
            if (target_schema.is_valid())
              target_view = find_named_object_in_list(target_schema->views(), (*view)->oldName(),
                                                       false, "name");
            if (!filter || ((*view)->name() != (*view)->oldName() || !target_view.is_valid()))
            {
              mforms::TreeNodeRef view_node(group_node->add_child());
              view_node->set_icon_path(0, "db.View.16x16.png");
              view_node->set_string(0, *(*view)->name());
              view_node->set_string(1, *(*view)->oldName());
              view_node->set_data(new NodeData(*view, false));
              if ((*view)->name() != (*view)->oldName() || !target_view.is_valid())
                has_anomaly = true;
            }
          }
          if (has_anomaly)
            group_node->expand();
          group_node = schema_node->add_child();
          group_node->set_icon_path(0, "qe_sql-editor-tb-icon_open.png");
          group_node->set_string(0, "Routines");
          has_anomaly = false;
          GRTLIST_FOREACH(db_Routine, (*schema)->routines(), routine)
          {
            db_RoutineRef target_routine;
            if (target_schema.is_valid())
              target_routine = find_named_object_in_list(target_schema->routines(), (*routine)->oldName(),
                                                         false, "name");
            if (!filter || (*routine)->name() != (*routine)->oldName())
            {
              mforms::TreeNodeRef routine_node(group_node->add_child());
              routine_node->set_icon_path(0, "db.Routine.16x16.png");
              routine_node->set_string(0, *(*routine)->name());
              routine_node->set_string(1, *(*routine)->oldName());
              routine_node->set_data(new NodeData(*routine, false));
              if ((*routine)->name() != (*routine)->oldName() || !target_routine.is_valid())
                has_anomaly = true;
            }
          }
          if (has_anomaly)
            group_node->expand();
          schema_node->expand();
        }
      }
    }
  }
  
private:
  db_CatalogRef _catalog;
  db_CatalogRef _target_catalog;
  grt::StringListRef _schemas_to_skip;
  mforms::Box _vbox;
  mforms::Label _heading;
  mforms::TreeNodeView _tree;
  
  mforms::Panel *_panel;
  
  mforms::Box _bbox;
  mforms::Button _ok_button;
  mforms::Button _cancel_button;
  
  mforms::Label _model_name;
  mforms::Label _original_name;
  mforms::Selector _remap_selector;
  
  mforms::CheckBox _filter_check;
};


#endif

