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

#include "db_mysql_sql_script_sync.h"

#include "mforms/grttreeview.h"
#include "mforms/splitter.h"
#include "mforms/label.h"
#include "mforms/textbox.h"
#include "mforms/box.h"
#include "mforms/button.h"

class SynchronizeDifferencesPage : public WizardPage
{
  SynchronizeDifferencesPageBEInterface *_be;
  boost::function<db_CatalogRef ()> get_source_catalog;
  db_CatalogRef _src,_dst;

public:
  SynchronizeDifferencesPage(WizardForm *form, SynchronizeDifferencesPageBEInterface *be)
    : WizardPage(form, "diffs"), _be(be), _tree(mforms::TreeDefault),
      _diff_sql_text(mforms::BothScrollBars), _splitter(false), _bottom_box(true)
  {    
    set_title(_("Choose Direction to Apply Changes"));
    set_short_title(_("Select Changes to Apply"));

    _heading.set_wrap_text(true);
    _heading.set_text(_("Double click arrows in the list to choose whether to ignore changes, update "
      "the model with database changes or vice-versa. You can also apply an action to multiple selected rows."));
    add(&_heading, false, true);
    add(&_splitter,true,true);
    
    _splitter.add(&_tree);
    _tree.set_allow_multi_selection(true);
    _diff_sql_text.set_read_only(true);
    _splitter.add(&_diff_sql_text);


    
    add(&_bottom_box, false, false);
    
    _bottom_box.set_spacing(12);

    _update_model.set_text(_("Update Model"));
    _update_model.set_tooltip(_("Update the model with changes detected in database/script."));
    _skip.set_text(_("Ignore"));
    _skip.set_tooltip(_("Ignore the change and do not update neither the database/script or the model."));
    _update_source.set_text(_("Update Source"));
    _update_source.set_tooltip(_("Update the database/script with changes detected in the model."));

    _bottom_box.add(&_update_model, false, true);
    _bottom_box.add(&_skip, false, true);
    _bottom_box.add(&_update_source, false, true);

    scoped_connect(_update_source.signal_clicked(),boost::bind(&SynchronizeDifferencesPage::update_source, this));
    scoped_connect(_update_model.signal_clicked(),boost::bind(&SynchronizeDifferencesPage::update_model, this));
    scoped_connect(_skip.signal_clicked(),boost::bind(&SynchronizeDifferencesPage::update_none, this));
    
    _tree.add_column(mforms::IconStringGRTColumnType,
                     DiffTreeBE::ModelObjectName,
                     _be->get_col_name(0));
    _tree.add_column(mforms::IconGRTColumnType,
                     DiffTreeBE::ApplyDirection,
                     _be->get_col_name(1));
    _tree.add_column(mforms::IconStringGRTColumnType,
                     DiffTreeBE::DbObjectName,
                     _be->get_col_name(2));
    
    _tree.set_column_width(1, 50);

    scoped_connect(_tree.signal_row_activate(),boost::bind(&SynchronizeDifferencesPage::activate_row, this, _1, _2));
    scoped_connect(_tree.signal_changed(),boost::bind(&SynchronizeDifferencesPage::select_row, this));
  }

  virtual ~SynchronizeDifferencesPage()
  {
      if (get_source_catalog && _src.is_valid())
          _src->reset_references();
  }

  void select_row()
  {
    bec::NodeId node;
    std::string sql;
    if (_tree.get_selected_node(node))
    {
        ValueRef obj = _be->get_db_object(node);
        if (GrtNamedObjectRef::can_wrap(obj))
            sql.append(_be->get_sql_for_object(GrtNamedObjectRef::cast_from(obj)));

        obj = _be->get_model_object(node);
        if (GrtNamedObjectRef::can_wrap(obj))
            sql.append(_be->get_sql_for_object(GrtNamedObjectRef::cast_from(obj)));
    }
    _diff_sql_text.set_value(sql);
  }

  void activate_row(const bec::NodeId &node, int column)
  {
    if (column == DiffTreeBE::ApplyDirection)
    {
      _be->set_next_apply_direction(node);
      _tree.refresh(node);
    }
  }
  
  void set_source_catalog_slot(const boost::function<db_CatalogRef () > &source_catalog_slot)
  {
    get_source_catalog= source_catalog_slot;
  }

  void set_src(const db_CatalogRef cat){_src = cat;};
  void set_dst(const db_CatalogRef cat){_dst = cat;};

  virtual bool pre_load()
  {
    grt::StringListRef schemas_to_skip(grt::StringListRef::cast_from(values().get("unSelectedSchemata")));
    if (get_source_catalog)
      _src = get_source_catalog();
    _diff_tree= _be->init_diff_tree(std::vector<std::string>(),_src,_dst,schemas_to_skip);

    _tree.set_model(_diff_tree.get());
    for (int i= 0, c= _diff_tree->count_children(bec::NodeId()); i < c; i++)
         _tree.set_expanded(bec::NodeId(i), true);
    
    return true;
  }

  
  void update_source()
  {
    std::vector<bec::NodeId> nodes;
    if (_tree.get_selection(nodes) > 0)
    {
      bool refresh_individual_nodes= nodes.size() < 50;

      for (std::vector<bec::NodeId>::const_iterator iter= nodes.begin(); iter != nodes.end(); ++iter)
      {
        _be->set_apply_direction(*iter, DiffNode::ApplyToDb, true);
        if (refresh_individual_nodes)
          _tree.refresh(*iter);
      }
      if (!refresh_individual_nodes)
        _tree.refresh(bec::NodeId());
    }
  }
  
  void update_model()
  {
    std::vector<bec::NodeId> nodes;
    if (_tree.get_selection(nodes) > 0)
    {
      bool refresh_individual_nodes= nodes.size() < 50;

      for (std::vector<bec::NodeId>::const_iterator iter= nodes.begin(); iter != nodes.end(); ++iter)
      {
        _be->set_apply_direction(*iter, DiffNode::ApplyToModel, true);
        if (refresh_individual_nodes)
          _tree.refresh(*iter);
      }    
      if (!refresh_individual_nodes)
        _tree.refresh(bec::NodeId());
    }
  }
  
  void update_none()
  { 
    std::vector<bec::NodeId> nodes;
    if (_tree.get_selection(nodes) > 0)
    {
      bool refresh_individual_nodes= nodes.size() < 50;

      for (std::vector<bec::NodeId>::const_iterator iter= nodes.begin(); iter != nodes.end(); ++iter)
      {
        _be->set_apply_direction(*iter, DiffNode::DontApply, true);
        if (refresh_individual_nodes)
          _tree.refresh(*iter);
      }
      if (!refresh_individual_nodes)
        _tree.refresh(bec::NodeId());
    }
  }
  
protected:
  GRTTreeView _tree;
  boost::shared_ptr<DiffTreeBE> _diff_tree;
  Label _heading;
  ::mforms::TextBox _diff_sql_text;
  ::mforms::Splitter _splitter;
  
  Box _bottom_box;
  Button _select_all;
  Button _select_children;
  Button _update_source;
  Button _update_model;
  Button _skip;
};

