/* 
 * Copyright (c) 2009, 2014, 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 "grtui/grt_wizard_plugin.h"
#include "grtui/wizard_progress_page.h"
#include "grtui/wizard_schema_filter_page.h"
#include "grtui/wizard_object_filter_page.h"
#include "grtui/wizard_finished_page.h"
#include "grtui/grtdb_connect_panel.h"
#include "db_rev_eng_be.h"
#include "base/string_utilities.h"

using namespace grtui;
using namespace mforms;
using namespace base;

namespace DBImport
{
#include "grtui/connection_page.h"
#include "fetch_schema_names_page.h"
#include "schema_selection_page.h"
#include "fetch_schema_contents_page.h"

class WbPluginDbImport;

class ObjectSelectionPage : public WizardObjectFilterPage
{
public:
  ObjectSelectionPage(WbPluginDbImport *form);
  void setup_filters();

protected:
  virtual void enter(bool advancing)
  {
    if (advancing)
      setup_filters();

    WizardObjectFilterPage::enter(advancing);
  }
  
  
  virtual bool advance();

  virtual std::string next_button_caption()
  {
    return execute_caption();
  }
  
private:
  std::map<Db_plugin::Db_object_type, DBObjectFilterFrame*> _filters;
  mforms::Box _box;
  mforms::Label _empty_label;
  mforms::CheckBox _autoplace_check;
};

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

/* not used
class ReviewScriptPage : public WizardPage
{
public:
  ReviewScriptPage(WbPluginDbImport *form)
    : WizardPage(form, "review"), _text(true)
  {
    set_title(_("Review DDL Script to be Parsed and Imported"));
    set_short_title(_("Review DDL Script"));

    add(&_text, true, true);
    _text.set_read_only(true);
  }


  virtual void enter(bool advancing)
  {
    if (advancing)
    {
      std::string text;
    
      ((WbPluginDbImport*)_form)->db_plugin()->dump_ddl(text);
    
      _text.set_value(text);
    }
  }
  
  virtual std::string next_button_caption()
  {
    return execute_caption();
  }

private:
  mforms::TextBox _text;
};
  */


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

class DBImportProgressPage : public WizardProgressPage
{
  TaskRow *_place_task;
public:
    DBImportProgressPage(WbPluginDbImport *form);
  
  virtual void enter(bool advancing)
  {
    bool place = values().get_int("import.place_figures", 0) != 0;

    _place_task->set_enabled(place);
    
    WizardProgressPage::enter(advancing);
  }

  bool perform_import();
  bool perform_place();
 
  virtual bool allow_back()
  {
    return false;
  }
};


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

class FinishPage : public WizardFinishedPage
{

public:
  FinishPage(WbPluginDbImport *form);
  virtual void enter(bool advancing);

  virtual bool next_closes_wizard()
  {
    return true;
  }

private:
  struct Summary
  {
    int tables;
    int views;
    int routines;
    
    Summary() : tables(0), views(0), routines(0) {}
  };

  std::string create_summary(const grt::ListRef<GrtObject> &objects)
  {
    std::map<std::string, Summary> schema_summary;
    
    std::string summary= _("Summary of Reverse Engineered Objects:\n\n");

    for(grt::ListRef<GrtObject>::const_iterator iter= objects.begin();
        iter != objects.end(); ++iter)
    {
      std::string owner_name= (*iter)->owner()->name();

      if ((*iter).is_instance<db_Schema>())
      {
        if (schema_summary.find((*iter)->id()) == schema_summary.end())
        {
          Summary s;
          schema_summary[(*iter)->name()]= s;
        }
      }
      else if ((*iter).is_instance<db_Table>())
        schema_summary[owner_name].tables++;
      else if ((*iter).is_instance<db_View>())
        schema_summary[owner_name].views++;
      else if ((*iter).is_instance<db_Routine>())
        schema_summary[owner_name].routines++;
    }

    for (std::map<std::string,Summary>::const_iterator iter= schema_summary.begin();
         iter != schema_summary.end(); ++iter)
    {
      if (iter->second.tables == 0 && iter->second.views == 0 && iter->second.routines == 0)
        summary.append(strfmt(_(" - empty schema '%s'\n"), iter->first.c_str()));
      else
      {
        summary.append(" - ");
        std::string sep= "";
        if (iter->second.tables > 0)
        {
          summary.append(strfmt(_("%i tables"), iter->second.tables));
          sep= ", ";
        }
        if (iter->second.views > 0)
        {
          summary.append(strfmt(_("%s%i views"), sep.c_str(), iter->second.views));
          sep= ", ";
        }
        if (iter->second.routines > 0)
        {
          summary.append(strfmt(_("%s%i routines"), sep.c_str(), iter->second.routines));
          sep= ", ";
        }
        summary.append(strfmt(_(" from schema '%s'\n"), iter->first.c_str()));
      }
    }
    
    return summary;
  }
};

class WbPluginDbImport : public WizardPlugin
{
  ConnectionPage *_connection_page;
  FetchSchemaNamesProgressPage *_fetch_progress_page;
  SchemaSelectionPage *_schema_selection_page;
  FetchSchemaContentsProgressPage *_fetch_schemas_progress_page;
  ObjectSelectionPage *_object_selection_page;
//  ReviewScriptPage *_review_script_page;
  DBImportProgressPage *_import_progress_page;
  FinishPage *_finish_page;

  Db_rev_eng _db_rev_eng;
  
  
  std::vector<std::string> load_schemas()
  {
    std::vector<std::string> schema_names;

    db_plugin()->load_schemata(schema_names);
    return schema_names;
  }

public:
  WbPluginDbImport(grt::Module *module);
  
  Db_plugin *db_plugin() { return &_db_rev_eng; }
  Sql_import *sql_import() { return &_db_rev_eng; }
};

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

WbPluginDbImport::WbPluginDbImport(grt::Module *module)
  : WizardPlugin(module)
{

  set_name("db_import_wizard");
  _db_rev_eng.grtm(grtm());
  
  _connection_page= new ConnectionPage(this);
  _connection_page->set_db_connection(db_plugin()->db_conn());

  _fetch_progress_page= new FetchSchemaNamesProgressPage(this);
  _fetch_progress_page->set_db_connection(db_plugin()->db_conn());
  _fetch_progress_page->set_load_schemas_slot(boost::bind(&WbPluginDbImport::load_schemas, this));

  _schema_selection_page= new SchemaSelectionPage(this);
  _schema_selection_page->set_db_plugin(db_plugin());

  _fetch_schemas_progress_page= new FetchSchemaContentsProgressPage(this);
  _fetch_schemas_progress_page->set_db_plugin(db_plugin());
  _object_selection_page= new ObjectSelectionPage(this);
//  _review_script_page= new ReviewScriptPage(this);
  _import_progress_page= new DBImportProgressPage(this);
  _finish_page= new FinishPage(this);
    
  add_page(mforms::manage(_connection_page));
  add_page(mforms::manage(_fetch_progress_page));
  add_page(mforms::manage(_schema_selection_page));
  add_page(mforms::manage(_fetch_schemas_progress_page));
  add_page(mforms::manage(_object_selection_page));
//  add_page(mforms::manage(_review_script_page));
  add_page(mforms::manage(_import_progress_page));
  add_page(mforms::manage(_finish_page));
  
  set_title("Reverse Engineer Database");
}

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


  ObjectSelectionPage::ObjectSelectionPage(WbPluginDbImport *form)
    : WizardObjectFilterPage(form, "objectFilter"), _box(false)
  {
    set_title(_("Select Objects to Reverse Engineer"));
    set_short_title(_("Select Objects"));

    _box.set_padding(12);
    add_end(&_box, false);
    
    _empty_label.set_text(_("The selected schemas contain no objects."));
    _box.add(&_empty_label, false);
    
    _autoplace_check.set_text(_("Place imported objects on a diagram"));
    _autoplace_check.set_active(true);
    _box.add(&_autoplace_check, false);    
  }


  void ObjectSelectionPage::setup_filters()
  {
    Db_plugin *plugin= ((WbPluginDbImport*)_form)->db_plugin();
    bool empty= true;
    
    reset();
    _filters.clear();
    
    if (plugin->db_objects_selection_model(Db_plugin::dbotTable)->total_items_count() > 0)
    {
      _filters[Db_plugin::dbotTable]=
      add_filter(plugin->db_objects_struct_name_by_type(Db_plugin::dbotTable),
                 _("Import %s Objects"),
                 plugin->db_objects_selection_model(Db_plugin::dbotTable),
                 plugin->db_objects_exclusion_model(Db_plugin::dbotTable),
                 plugin->db_objects_enabled_flag(Db_plugin::dbotTable));
      empty= false;
    }
    if (plugin->db_objects_selection_model(Db_plugin::dbotView)->total_items_count() > 0)
    {
      _filters[Db_plugin::dbotView]=
      add_filter(plugin->db_objects_struct_name_by_type(Db_plugin::dbotView),
                 _("Import %s Objects"),
                 plugin->db_objects_selection_model(Db_plugin::dbotView),
                 plugin->db_objects_exclusion_model(Db_plugin::dbotView),
                 plugin->db_objects_enabled_flag(Db_plugin::dbotView));
      empty= false;
    }
    if (plugin->db_objects_selection_model(Db_plugin::dbotRoutine)->total_items_count() > 0)
    {
      _filters[Db_plugin::dbotRoutine]=
      add_filter(plugin->db_objects_struct_name_by_type(Db_plugin::dbotRoutine),
                 _("Import %s Objects"),
                 plugin->db_objects_selection_model(Db_plugin::dbotRoutine),
                 plugin->db_objects_exclusion_model(Db_plugin::dbotRoutine),
                 plugin->db_objects_enabled_flag(Db_plugin::dbotRoutine));
      empty= false;
    }
    if (plugin->db_objects_selection_model(Db_plugin::dbotTrigger)->total_items_count() > 0)
    {
      _filters[Db_plugin::dbotTrigger]=
      add_filter(plugin->db_objects_struct_name_by_type(Db_plugin::dbotTrigger),
                 _("Import %s Objects"),
                 plugin->db_objects_selection_model(Db_plugin::dbotTrigger),
                 plugin->db_objects_exclusion_model(Db_plugin::dbotTrigger),
                 plugin->db_objects_enabled_flag(Db_plugin::dbotTrigger));
      empty= false;
    }
    
    _empty_label.show(empty);
  }

  bool ObjectSelectionPage::advance()
  {
    Db_plugin *plugin= ((WbPluginDbImport*)_form)->db_plugin();
    std::list<std::string> errors;
    std::string text;
    
    if (!plugin->validate_db_objects_selection(&errors))
    {
      for (std::list<std::string>::const_iterator iter= errors.begin();
           iter != errors.end(); ++iter)
        text.append(*iter + "\n");
    }

    if (_autoplace_check.get_active())
    {
      size_t total_placable_object_count = plugin->db_objects_selection_model(Db_plugin::dbotTable)->active_items_count() +
        plugin->db_objects_selection_model(Db_plugin::dbotView)->active_items_count() +
        plugin->db_objects_selection_model(Db_plugin::dbotRoutine)->active_items_count();
      if (total_placable_object_count > 250)
      {
        mforms::Utilities::show_warning(_("Resource Warning"), _("Too many objects are selected for auto placement."
          "\nSelect fewer elements to create the EER diagram."), "OK");
        _autoplace_check.set_active(false);
        return false;
      }
    }

    if (!text.empty())
    {
      mforms::Utilities::show_error("Error in Object Selection", text, "OK");

      return false;
    }

    for (std::map<Db_plugin::Db_object_type,DBObjectFilterFrame*>::const_iterator iter= _filters.begin();
         iter != _filters.end(); ++iter)
    {
      plugin->db_objects_activated(iter->first, iter->second->get_active());
    }

    values().gset("import.place_figures", _autoplace_check.get_active());
    
    return true;
  }

  DBImportProgressPage::DBImportProgressPage(WbPluginDbImport *form)
    : WizardProgressPage(form, "importProgress", true)
  {
    set_title(_("Reverse Engineering Progress"));
    set_short_title(_("Reverse Engineer"));

    add_async_task(_("Reverse Engineer Selected Objects"),
                   boost::bind(&DBImportProgressPage::perform_import, this),
                   _("Reverse engineering DDL from selected objects..."));

    _place_task= add_async_task(_("Place Objects on Diagram"), 
             boost::bind(&DBImportProgressPage::perform_place, this),
             _("Placing objects..."));

    end_adding_tasks(_("Operation Completed Successfully"));
  }

  bool DBImportProgressPage::perform_import()
  {
    execute_grt_task(((WbPluginDbImport*)_form)->sql_import()->get_task_slot(),
                     false);
    return true;
  }
    
  bool DBImportProgressPage::perform_place()
  {
    execute_grt_task(((WbPluginDbImport*)_form)->sql_import()->get_autoplace_task_slot(), false);
    
    return true;
  }

  FinishPage::FinishPage(WbPluginDbImport *form)
    : WizardFinishedPage(form, "Reverse Engineering Finished")
  {
    set_title(_("Reverse Engineering Results"));
    set_short_title(_("Results"));
  }

  
  void FinishPage::enter(bool advancing)
  {
    if (advancing)
      set_summary(create_summary(((WbPluginDbImport*)_form)->sql_import()->get_created_objects()));
  }
}; // namespace DBImport

grtui::WizardPlugin *createDbImportWizard(grt::Module *module, db_CatalogRef catalog)
{
  return new DBImport::WbPluginDbImport(module);
}

void deleteDbImportWizard(grtui::WizardPlugin *plugin)
{
    delete plugin;
}
