/* 
 * 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 _SCHEMA_MATCHING_PAGE_H_
#define _SCHEMA_MATCHING_PAGE_H_

#include "grtui/grt_wizard_form.h"
#include "mforms/treenodeview.h"
#include "mforms/imagebox.h"
#include "mforms/checkbox.h"

class SchemaMatchingPage : public WizardPage
{
  class NodeData : public mforms::TreeNodeData
  {
  public:
    db_SchemaRef schema;
    std::string message;
    std::string button_caption;
    
    NodeData(db_SchemaRef aschema, const std::string &lmessage, const std::string &bcaption)
    : schema(aschema), message(lmessage), button_caption(bcaption) {}
  };
  
public:
  SchemaMatchingPage(WizardForm *form, const char *name= "selectSchemata", 
                     const std::string &left_name = "Model", 
                     const std::string &right_name = "Source")
  : WizardPage(form, name), _dbplugin(0), _header(true), _tree(mforms::TreeFlatList)
  {
    _header.set_spacing(4);
    
    _image.set_image(bec::IconManager::get_instance()->get_icon_path("db.Schema.32x32.png"));
    _header.add(&_image, false);
    
    _label.set_text_align(mforms::MiddleLeft);
    _label.set_text(_("Select the Schemata to be Synchronized:"));
    _label.set_style(mforms::BoldStyle);
    _header.add(&_label, true, true);
    
    add(&_header, false, false);

    set_short_title(_("Select Schemata"));
    set_title(_("Select the Schemata to be Synchronized"));

    _tree.add_column(mforms::CheckColumnType, "", 20, true);
    _tree.add_column(mforms::IconStringColumnType, left_name, 150, false);
    _tree.add_column(mforms::StringColumnType, right_name, 150, false);
    _tree.add_column(mforms::IconStringColumnType, "", 200, false);
    _tree.end_columns();
    _tree.set_cell_edit_handler(boost::bind(&SchemaMatchingPage::cell_edited, this, _1, _2, _3));
    scoped_connect(_tree.signal_changed(), boost::bind(&SchemaMatchingPage::selection_changed, this));
    
    add(&_tree, true, true);
    
    add(&_action_button, false, true);
    _action_button.show(false);
    add(&_explain_label, false, true);
    scoped_connect(_action_button.signal_clicked(), boost::bind(&SchemaMatchingPage::action_clicked, this));
    
    add(&_missing_label, false, true);
    
    _missing_label.show(false);
    _missing_label.set_style(mforms::SmallHelpTextStyle);
  }
  
  void cell_edited(mforms::TreeNodeRef node, int column, const std::string &value)
  {
    if (column == 0)
    {
      node->set_bool(column, value != "0");
      validate();
    }
  }
  
  virtual bool allow_next()
  {
    int c = _tree.count();
    for (int i = 0; i < c; i++)
    {
      mforms::TreeNodeRef node(_tree.root_node()->get_child(i));
      if (node->get_bool(0))
        return true;
    }
    return false;
  }

  virtual void leave(bool advancing)
  {
    if (advancing)
    {
      grt::StringListRef unlist(_form->grtm()->get_grt());
      grt::StringListRef list(_form->grtm()->get_grt());
      
      int c = _tree.count();
      for (int i = 0; i < c; i++)
      {
        mforms::TreeNodeRef node(_tree.root_node()->get_child(i));
        NodeData *data = dynamic_cast<NodeData*>(node->get_data());
        if (data)
        {
          if (node->get_bool(0))
            list.insert(data->schema->oldName());
          else
            unlist.insert(data->schema->oldName()); 
        }
      }
      
      values().set("unSelectedSchemata", unlist);
      values().set("selectedSchemata", list);
    }
    WizardPage::leave(advancing);
  }
  
  virtual void enter(bool advancing)
  {
    if (advancing)
    {
      grt::ListRef<db_Schema> schemas(_dbplugin->model_catalog()->schemata());
      bool has_problems = false;
      _tree.clear();
      
      {
        std::string missing_names;
        int missing_count= 0;
        grt::StringListRef db_list(grt::StringListRef::cast_from(values().get("schemata")));
        
        // check for schemas that exist only in the model and not in DB
        GRTLIST_FOREACH(db_Schema, schemas, schema)
        {
          mforms::TreeNodeRef node = _tree.add_node();

          std::string found;
          bool found_name = false;
          for (grt::StringListRef::const_iterator j= db_list.begin(); j != db_list.end(); ++j)
          {
            if (g_strcasecmp((*schema)->oldName().c_str(), (*j).c_str()) == 0)
              found= *j;
            if (g_strcasecmp((*schema)->name().c_str(), (*j).c_str()) == 0)
              found_name = true;
          }
          
          node->set_icon_path(1, "db.Schema.16x16.png");
          node->set_string(1, (*schema)->name());
          
          if (found.empty())
          {
            has_problems = true;
            if (missing_count > 0)
              missing_names.append(", ");
            missing_names.append((*schema)->name());
            missing_count++;
            node->set_bool(0, false);
            node->set_string(2, (*schema)->oldName());
            if (found_name)
            {
              // model:oldName was not found in DB, but model:name was
              node->set_string(3, _("original schema not found"));
              node->set_data(new NodeData(*schema,
                                          base::strfmt(_("The schema '%s' seems to have been renamed from '%s' in the model,\n"
                                           "but the original '%s' was not found in the RDBMS.\n"
                                           "However a schema called '%s' seems to exist there, would you like to force synchronization with it?"),
                                         (*schema)->name().c_str(), (*schema)->oldName().c_str(), (*schema)->oldName().c_str(), 
                                         (*schema)->name().c_str()),
                                          base::strfmt("Force Synchronization with %s", (*schema)->name().c_str())));
            }
            else
            {
              // model:oldName was not found in DB, model:name wasn't either
              if ((*schema)->name() == (*schema)->oldName())
              {
                node->set_string(3, _("not found in RDBMS"));
                node->set_data(new NodeData(*schema,
                                            base::strfmt(_("The schema '%s' does not exist in the RDBMS yet.\n"
                                             "You may synchronize to create it but Forward Engineering is the preferred method\n"
                                             "for initial creation of the database."),
                                           (*schema)->name().c_str()),
                                            ""));
              }
              else
              {
                node->set_string(3, _("not found in RDBMS"));
                if ((*schema)->oldName() == "")
                  node->set_data(new NodeData(*schema,
                                              base::strfmt(_("The schema '%s' seems to have been newly created in the model,\n"
                                                             "and was not found in the RDBMS.\n"
                                                             "You may synchronize to create it but Forward Engineering is the preferred method\n"
                                                             "for initial creation of the database."),
                                                           (*schema)->name().c_str()),
                                              ""));
                else
                  node->set_data(new NodeData(*schema,
                                            base::strfmt(_("The schema '%s' seems to have been renamed from '%s' in the model,\n"
                                             "but neither were found in the RDBMS.\n"
                                             "You may synchronize to create it but Forward Engineering is the preferred method\n"
                                             "for initial creation of the database."),
                                           (*schema)->name().c_str(), (*schema)->oldName().c_str()),
                                            ""));
              }
            }
          }
          else
          {
            node->set_bool(0, true);
            node->set_string(2, found);
            
            if ((*schema)->name() != (*schema)->oldName())
            {
              node->set_icon_path(3, "mini_warning.png");
              node->set_string(3, _("schema will be renamed"));
              node->set_data(new NodeData(*schema,
                                          base::strfmt(_("The schema '%s' seems to have been renamed from\n"
                                           "'%s' in the model and will be renamed accordingly in the RDBMS.\n"
                                           "WARNING: because renaming a schema is not directly supported by the RDBMS,\n"
                                           "it will be first dropped and then created from scratch.\n"
                                           "Data in tables from '%s' will be discarded!"),
                                           (*schema)->name().c_str(), (*schema)->oldName().c_str(),
                                           (*schema)->oldName().c_str()),
                                           base::strfmt("Ignore Rename and Synchronize with %s", (*schema)->name().c_str())));
              has_problems = true;
            }
            else
            {
              node->set_string(3, "");
              node->set_data(new NodeData(*schema, "", ""));
            }
          }
        }
        
        if (has_problems)
          _explain_label.set_text("Select a list item to view additional information.");
        
        if (missing_count == (int)schemas.count())
        {
          _missing_label.set_text(_("The schemata from your model are missing from the target RDBMS server.\nIf you are creating them for the first time use the Forward Engineer function."));
          _missing_label.show(true);
        }
      }
    }
  }
  
  void set_db_plugin(Db_plugin *pl)
  {
    _dbplugin= pl;
  }
  
private:
  void selection_changed()
  {
    mforms::TreeNodeRef sel(_tree.get_selected_node());
    if (sel)
    {
      NodeData *data = dynamic_cast<NodeData*>(sel->get_data());
      if (data)
      {
        _explain_label.set_text(data->message);
        _action_button.set_text(data->button_caption);
        _action_button.show(!data->button_caption.empty());
        return;
      }
    }
    _action_button.show(false);
    _explain_label.set_text("");
  }
  
  void action_clicked()
  {
    mforms::TreeNodeRef sel(_tree.get_selected_node());
    if (sel)
    {
      NodeData *data = dynamic_cast<NodeData*>(sel->get_data());
      if (data)
      {
        data->schema->oldName(data->schema->name());
        data->button_caption = "";
        data->message = base::strfmt(_("Apparent schema rename will be ignored and synchronization forced with %s"), data->schema->name().c_str());
        _action_button.show(false);
        _explain_label.set_text(data->message);
        sel->set_bool(0, true);
        sel->set_string(2, data->schema->name());
        sel->set_string(3, "forcing synchronization");
        validate();
      }
    }
  }
  
private:
  Db_plugin *_dbplugin;
  mforms::Box _header;
  mforms::ImageBox _image;
  mforms::Label _label;
  mforms::TreeNodeView _tree;

  mforms::Button _action_button;
  mforms::Label _explain_label;
  mforms::Label _missing_label;
};


#endif /* _SCHEMA_SELECTION_PAGE_H_ */
