#include "stdafx.h"

#include "grtdb/db_object_helpers.h"

#include "grts/structs.h"
#include "grts/structs.db.mgmt.h"
#include "grts/structs.db.mysql.h"

#include "grtpp.h"
#include "grt/grt_reporter.h"
#include "grtdb/diff_dbobjectmatch.h"

using namespace grt;

#include "grti/sqlgenerator.h"
//QQQ#include "grti/wbvalidation.h"
//QQQ#include "grti/wbvalidationmysql.h"

#include "db_mysql_sql_export.h"
#include "diff_tree.h"

#include "grtdb/catalog_templates.h"
#include "db.mysql/src/module_db_mysql_shared_code.h"
#include "grtsqlparser/sql_facade.h"

void DbMySQLSQLExport::init_from_ctor(bec::GRTManager *grtm, db_mysql_CatalogRef catalog)
{
  //_manager= grtm;

  _tables_are_selected= true;
  _triggers_are_selected= true;
  _routines_are_selected= true;
  _views_are_selected= true;
  _users_are_selected= true;
  _catalog= catalog;

  if(!_catalog.is_valid())
    _catalog= get_model_catalog();  // call own version

  _users_model= new bec::GrtStringListModel();
  _users_exc_model= new bec::GrtStringListModel();
 
  _tables_model= new bec::GrtStringListModel();
  _tables_exc_model= new bec::GrtStringListModel();
  _views_model= new bec::GrtStringListModel();
  _views_exc_model= new bec::GrtStringListModel();
  _routines_model= new bec::GrtStringListModel();
  _routines_exc_model= new bec::GrtStringListModel();
  _triggers_model= new bec::GrtStringListModel();
  _triggers_exc_model= new bec::GrtStringListModel();
}


DbMySQLSQLExport::DbMySQLSQLExport(bec::GRTManager *grtm, db_mysql_CatalogRef catalog)
  : DbMySQLValidationPage(grtm)
{
  init_from_ctor(grtm, catalog);
}

DbMySQLSQLExport::DbMySQLSQLExport(bec::GRTManager *grtm)
  : DbMySQLValidationPage(grtm)
//  : messages_list(NULL)
{
  init_from_ctor(grtm, db_mysql_CatalogRef());
}

db_mysql_CatalogRef DbMySQLSQLExport::get_model_catalog()
{
  return db_mysql_CatalogRef::cast_from(_manager->get_grt()->get("/wb/doc/physicalModels/0/catalog")); 
}

void DbMySQLSQLExport::set_option(const std::string& name, bool value)
{
  if(name.compare("GenerateDrops") == 0)
    _gen_drops= value;
  else if(name.compare("GenerateSchemaDrops") == 0)
    _gen_schema_drops= value;
  else if(name.compare("GenerateWarnings") == 0)
    _gen_warnings= value;
  else if(name.compare("GenerateCreateIndex") == 0)
    _gen_create_index= value;
  else if(name.compare("NoUsersJustPrivileges") == 0)
    _no_users_just_privileges=value;
  else if(name.compare("GenerateInserts") == 0)
    _gen_inserts=value;
  else if(name.compare("TablesAreSelected") == 0)
    _tables_are_selected= value;
  else if(name.compare("TriggersAreSelected") == 0)
    _triggers_are_selected= value;
  else if(name.compare("RoutinesAreSelected") == 0)
    _routines_are_selected= value;
  else if(name.compare("ViewsAreSelected") == 0)
    _views_are_selected= value;
  else if(name.compare("UsersAreSelected") == 0)
    _users_are_selected= value;
  else if(name.compare("OmitSchemata") == 0)
    _omit_schemata= value;
  else if(name.compare("GenerateUse") == 0)
    _generate_use= value;
  else if(name.compare("SkipForeignKeys") == 0)
    _skip_foreign_keys= value;
  else if(name.compare("SkipFKIndexes") == 0)
    _skip_fk_indexes= value;
}

void DbMySQLSQLExport::set_option(const std::string& name, const std::string& value)
{
  if(name.compare("OutputFileName") == 0)
    _output_filename= value;
}

grt::StringListRef convert_string_vector_to_grt_list(grt::GRT *grt, const std::vector<std::string>& v)
{
  grt::StringListRef grt_list(grt);
  for(std::vector<std::string>::const_iterator e= v.end(), it= v.begin(); it != e; it++)
  {
    grt_list.insert(grt::StringRef(*it));
  }
  return grt_list;
}

static void parse_string_from_filter(const std::string& source, 
                                     std::string& out_prefix,
                                     std::string& out_suffix)
{
  std::string::size_type period= source.find('.');
  out_prefix.assign(source.substr(0, period));
  if(period != std::string::npos)
    out_suffix.assign(source.substr(period+1));
}

typedef std::map<std::string, std::list<std::string> > StringListMap;

std::vector<std::string> get_names(const bec::GrtStringListModel* list,const std::map<std::string,GrtNamedObjectRef>& obj_map,std::set<db_mysql_SchemaRef>& schemas, const bool case_sensitive)
{
  std::vector<std::string> work_vector;
  const std::vector<std::string>& list_items = list->items();
  for(std::vector<std::string>::const_iterator It= list_items.begin(); It != list_items.end(); ++It)
  {
    std::map<std::string,GrtNamedObjectRef>::const_iterator ItTableName = obj_map.find(*It);
    if (ItTableName != obj_map.end())
    {
      work_vector.push_back(get_old_object_name_for_key(ItTableName->second, case_sensitive));
      if(db_mysql_TriggerRef::can_wrap(ItTableName->second))
        schemas.insert(db_mysql_SchemaRef::cast_from(ItTableName->second->owner()->owner()));
      else if (db_mysql_SchemaRef::can_wrap(ItTableName->second->owner()))
        schemas.insert(db_mysql_SchemaRef::cast_from(ItTableName->second->owner()));
    }
  }
  return work_vector;
};


grt::DictRef DbMySQLSQLExport::get_options_as_dict(grt::GRT *grt)
{
  grt::DictRef options(grt);

  // general options
  options.set("GenerateDrops", grt::IntegerRef(_gen_drops ? 1 : 0));
  options.set("GenerateSchemaDrops", grt::IntegerRef(_gen_schema_drops ? 1 : 0));
  options.set("GenerateWarnings", grt::IntegerRef(_gen_warnings ? 1 : 0));
  options.set("GenerateCreateIndex", grt::IntegerRef(_gen_create_index ? 1 : 0));
  options.set("NoUsersJustPrivileges", grt::IntegerRef(_no_users_just_privileges ? 1 : 0));
  options.set("GenerateInserts", grt::IntegerRef(_gen_inserts ? 1 : 0));
  options.set("UseShortNames", grt::IntegerRef(_omit_schemata ? 1 : 0));
  options.set("GenerateUse", grt::IntegerRef(_generate_use ? 1 : 0));
  options.set("SkipForeignKeys", grt::IntegerRef(_skip_foreign_keys ? 1 : 0));
  options.set("SkipFKIndexes", grt::IntegerRef(_skip_fk_indexes ? 1 : 0));

  std::vector<std::string> schemata_names;
  db_mysql_CatalogRef cat(get_model_catalog());

  for(size_t i= 0; i < cat->schemata().count(); i++)
    schemata_names.push_back(get_old_object_name_for_key(cat->schemata().get(i), _case_sensitive));

  std::set<db_mysql_SchemaRef> schemas;

//  options.set("SchemaFilterList", convert_string_vector_to_grt_list(grt, schemata_names));

  // filtering options
  options.set("TableFilterList", _tables_are_selected ? 
    convert_string_vector_to_grt_list(grt,get_names(_tables_model,_tables_map, schemas, _case_sensitive)) : grt::StringListRef(grt));

  options.set("ViewFilterList", _views_are_selected ? 
    convert_string_vector_to_grt_list(grt, get_names(_views_model,_views_map, schemas, _case_sensitive)) : grt::StringListRef(grt));

  options.set("RoutineFilterList", _routines_are_selected ? 
    convert_string_vector_to_grt_list(grt, get_names(_routines_model,_routines_map, schemas, _case_sensitive)) : grt::StringListRef(grt));

  options.set("TriggerFilterList", _triggers_are_selected ? 
    convert_string_vector_to_grt_list(grt, get_names(_triggers_model,_triggers_map, schemas, _case_sensitive)) : grt::StringListRef(grt));

  options.set("UserFilterList", _users_are_selected ? 
    convert_string_vector_to_grt_list(grt, get_names(_users_model,_users_map, schemas, _case_sensitive)) : grt::StringListRef(grt));

  grt::StringListRef schema_names_list(grt);
  for (std::set<db_mysql_SchemaRef>::const_iterator It = schemas.begin(); It != schemas.end(); ++It)
    schema_names_list.insert(get_old_object_name_for_key(*It, _case_sensitive));
  
  options.set("SchemaFilterList", schema_names_list);

  return options;
}


void DbMySQLSQLExport::start_export(bool wait_finish)
{
  bec::GRTTask *task= new bec::GRTTask("SQL export", 
    _manager->get_dispatcher(), 
    boost::bind(&DbMySQLSQLExport::export_task, this, _1, grt::StringRef()));

  scoped_connect(task->signal_finished(),boost::bind(&DbMySQLSQLExport::export_finished, this, _1));
  
  if (wait_finish)
    _manager->get_dispatcher()->add_task_and_wait(task);
  else
    _manager->get_dispatcher()->add_task(task);
}

void DbMySQLSQLExport::export_finished(grt::ValueRef res)
{
  CatalogMap cmap;
  update_all_old_names(get_model_catalog(), false, cmap);
  _manager->get_grt()->send_output(*grt::StringRef::cast_from(res) + '\n');
  if (_task_finish_cb)
    _task_finish_cb();
}

ValueRef DbMySQLSQLExport::export_task(grt::GRT* grt, grt::StringRef)
{
  bec::Reporter rep(grt);

  try
  {
    SQLGeneratorInterfaceWrapper *diffsql_module= 
      //grt->get_module_wrapper<SQLGeneratorInterfaceWrapper>(grt->get_module("MySQLModuleDbMySQL"));
      grt->get_module_wrapper<SQLGeneratorInterfaceWrapper>(grt->get_module("DbMySQL"));
  
    if(diffsql_module == NULL)
      return grt::StringRef("\nSQL Script Export Error: Not able to load 'MySQLModuleDbMySQL' module");

    grt::ValueRef e(NULL);

    default_omf omf;
    grt::NormalizedComparer normalizer(grt);
    normalizer.init_omf(&omf);
    boost::shared_ptr<DiffChange> create_change= diff_make(e, _catalog, &omf);
    boost::shared_ptr<DiffChange> drop_change= diff_make(_catalog, e, &omf);
    DictRef create_map(grt);
    DictRef drop_map(grt);

    char sprintf_buf[32];
    
    std::string change_ptr;
    std::string log_ptr;

    sprintf(sprintf_buf, "%p", create_change.get());
    change_ptr.assign(sprintf_buf);

    grt::DictRef options= get_options_as_dict(grt);

    options.set("OutputContainer", create_map);

    diffsql_module->generateSQL(GrtNamedObjectRef::cast_from(ValueRef(NULL)), options, change_ptr);
    //delete create_change;

    sprintf(sprintf_buf, "%p", drop_change.get());
    change_ptr.assign(sprintf_buf);

    if(_gen_drops)
    {
      options.set("OutputContainer", drop_map);
      diffsql_module->generateSQL(GrtNamedObjectRef::cast_from(_catalog), options, change_ptr);
      //delete drop_change;
    }

    grt::StringListRef strlist= grt::StringListRef::cast_from(options.get("ViewFilterList"));

    if(diffsql_module->makeSQLExportScript(_catalog, options, create_map, drop_map))
    {
      return grt::StringRef("\nSQL Script Export Error: SQL Script Export Module Returned Error");
    }

    _export_sql_script= options.get_string("OutputScript");
    if (!_output_filename.empty())
    {
      g_file_set_contents(_output_filename.c_str(), 
                          _export_sql_script.c_str(), (gssize)_export_sql_script.size(), NULL);
    }
    return StringRef("\nSQL Script Export Completed");
  }
  catch(std::exception& ex)
  {
    if(ex.what())
    {
      return grt::StringRef(std::string("\nSQL Script Export Error: ").append(ex.what()).c_str());
    }
    else
      return grt::StringRef("\nUnknown SQL Script Export Error");
  }
  
}

void DbMySQLSQLExport::setup_grt_string_list_models_from_catalog(bec::GrtStringListModel **users_model, 
                                                                 bec::GrtStringListModel **users_exc_model, 
                                                                 bec::GrtStringListModel **tables_model, 
                                                                 bec::GrtStringListModel **tables_exc_model,
                                                                 bec::GrtStringListModel **views_model, 
                                                                 bec::GrtStringListModel **views_exc_model, 
                                                                 bec::GrtStringListModel **routines_model, 
                                                                 bec::GrtStringListModel **routines_exc_model, 
                                                                 bec::GrtStringListModel **triggers_model,
                                                                 bec::GrtStringListModel **triggers_exc_model)
{
  std::list<std::string> empty_list, users_list, tables_list, views_list, routines_list, triggers_list;

  grt::ListRef<db_User> users= _catalog->users();
  for(size_t i= 0, count0= users.count(); i < count0; i++)
  {
    db_UserRef user= users.get(i);
    if (!user->modelOnly())
    {
      users_list.push_back(user->name().c_str());
      _users_map[users_list.back()] = user;
    }
  }

  grt::ListRef<db_mysql_Schema> schemata= _catalog->schemata();
  for(size_t i= 0, count1= schemata.count(); i < count1; i++)
  {
    db_mysql_SchemaRef schema= schemata.get(i);
    
    // tables
    grt::ListRef<db_mysql_Table> tables= schema->tables();
    for(size_t j= 0, count2= tables.count(); j < count2; j++)
    {
      db_mysql_TableRef table= tables.get(j);
      if (!table->modelOnly())
      {
        tables_list.push_back(get_q_name(table->owner()->name().c_str(), table->name().c_str()));
        _tables_map[tables_list.back()] = table;
      }
      
      // triigers
      grt::ListRef<db_mysql_Trigger> triggers= table->triggers();
      for(size_t k= 0, count3= triggers.count(); k < count3; k++)
      {
        db_mysql_TriggerRef trigger= triggers.get(k);
        if (!trigger->modelOnly())
        {
          triggers_list.push_back(get_q_name(trigger->owner()->owner()->name().c_str(), trigger->name().c_str()));
          _triggers_map[triggers_list.back()] = trigger;
        }
      }
    }

    // views
    grt::ListRef<db_mysql_View> views= schema->views();
    for(size_t j= 0, count2= views.count(); j < count2; j++)
    {
      db_mysql_ViewRef view= views.get(j);
      if (!view->modelOnly())
      {
        views_list.push_back(get_q_name(view->owner()->name().c_str(), view->name().c_str()));
        _views_map[views_list.back()] = view;
      }
    }

    // routines
    grt::ListRef<db_mysql_Routine> routines= schema->routines();
    for(size_t j= 0, count2= routines.count(); j < count2; j++)
    {
      db_mysql_RoutineRef routine= routines.get(j);
      if (!routine->modelOnly())
      {
        routines_list.push_back(get_q_name(routine->owner()->name().c_str(), routine->name().c_str()));
        _routines_map[routines_list.back()] = routine;
      }
    }
  }

  _users_exc_model->reset(empty_list);
  _tables_exc_model->reset(empty_list);
  _views_exc_model->reset(empty_list);
  _routines_exc_model->reset(empty_list);
  _triggers_exc_model->reset(empty_list);

  _users_model->items_val_masks(_users_exc_model);
  _tables_model->items_val_masks(_tables_exc_model);
  _views_model->items_val_masks(_views_exc_model);
  _routines_model->items_val_masks(_routines_exc_model);
  _triggers_model->items_val_masks(_triggers_exc_model);

  _users_model->reset(users_list);
  _tables_model->reset(tables_list);  
  _views_model->reset(views_list);
  _routines_model->reset(routines_list);
  _triggers_model->reset(triggers_list);

  *users_model= _users_model;
  *users_exc_model= _users_exc_model;
  *tables_model= _tables_model;
  *tables_exc_model= _tables_exc_model;
  *views_model= _views_model;
  *views_exc_model= _views_exc_model;
  *routines_model= _routines_model;
  *routines_exc_model= _routines_exc_model;
  *triggers_model= _triggers_model;
  *triggers_exc_model= _triggers_exc_model;
}

//void DbMySQLSQLExport::validation_finished(grt::ValueRef res)
//{
//  _validation_finished_cb();
//}
//
//void DbMySQLSQLExport::validation_messages(MYX_GRT_MSGS *msgs)
//{
//  for(size_t i= 0; i < msgs->msgs_num; i++)
//    messages_list.handle_message(
//      msgs->msgs[i].msg_type, 
//      msgs->msgs[i].timestamp, 
//      std::string(msgs->msgs[i].msg), 
//      fmtstringlist(msgs->msgs[i].msg_detail));
//}
//
//ValueRef DbMySQLSQLExport::validation_task(grt::GRT* grt, grt::StringRef)
//{
//  try
//  {
//    WbValidationInterfaceModule *validation_module= 
//      static_cast<WbValidationInterfaceModule *>(
//      _manager->get_grt()->get_module("WbModuleValidation"));
//  
//    if(validation_module == NULL)
//      return grt::StringRef("\nSQL Script Export Error: Not able to load 'WbModuleValidation' module");
//
//    grt->send_info("Starting general validation");
//
//    int validation_res= validation_module->validateAll(
//      GrtObjectRef::cast_from(_manager->get_grt()->get("/wb/doc/physicalModels/0/catalog")));
//
//    _manager->get_dispatcher()->call_from_main_thread<int>(
//      boost::bind(_validation_step_finished_cb, validation_res), true);
//
//    WbValidationMySQLInterfaceModule *mysql_validation_module=
//      static_cast<WbValidationMySQLInterfaceModule *>(
//      _manager->get_grt()->get_module("WbModuleValidationMySQL"));
//    
//    if(validation_module == NULL)
//      return grt::StringRef("\nSQL Script Export Error: Not able to load 'WbModuleValidationMySQL' module");
//
//    grt->send_info("Starting MySQL-specific validation");
//
//    validation_res= mysql_validation_module->validateAll(
//      GrtObjectRef::cast_from(_manager->get_grt()->get("/wb/doc/physicalModels/0/catalog")));
//
//    _manager->get_dispatcher()->call_from_main_thread<int>(
//      boost::bind(_validation_step_finished_cb, validation_res), true);
//  }
//  catch(std::exception& ex)
//  {
//    if(ex.what())
//      return grt::StringRef(std::string("\nCatalog Validation Error: ").append(ex.what()).c_str());
//    else
//      return grt::StringRef("\nUnknown Catalog Validation Error");
//  }
//
//  return grt::StringRef("");
//}
