/* 
 * © 2007-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include "stdafx.h"

#include "editor_dbobject.h"

#define DEFAULT_COLLATION_CAPTION "default collation"

using namespace bec;

DBObjectEditorBE::DBObjectEditorBE(GRTManager *grtm, 
                                   const db_DatabaseObjectRef &object,
                                   const db_mgmt_RdbmsRef &rdbms)
  : BaseEditor(grtm, object), _rdbms(rdbms)
{
  _ignored_object_fields_for_ui_refresh.insert("lastChangeDate");

  if (rdbms.is_valid())
  {
    SqlFacade *sql_facade= SqlFacade::instance_for_rdbms(rdbms);
    _sql_parser= sql_facade->invalidSqlParser();
  }
}


DBObjectEditorBE::~DBObjectEditorBE()
{
}


bool DBObjectEditorBE::should_close_on_delete_of(const std::string &oid)
{ 
  if (get_object().id() == oid)
    return true;

  db_SchemaRef schema(get_schema());
  if (schema.is_valid() && schema.id() == oid)
    return true;

  return false;
}


void DBObjectEditorBE::update_change_date()
{
  get_object().set_member("lastChangeDate", grt::StringRef(fmttime(0, DATETIME_FMT)));
}


std::string DBObjectEditorBE::get_object_type()
{
  return get_object().get_metaclass()->get_attribute("caption");
}


std::string DBObjectEditorBE::get_name()
{
  return get_dbobject()->name();
}


void DBObjectEditorBE::set_name(const std::string &name)
{
  if (get_dbobject()->name() != name)
  {
    RefreshCentry __centry(*this);
    grt::AutoUndo undo(get_grt(), new UndoObjectChangeGroup(get_dbobject().id(), "name"));

    std::string name_= rtrim(name);
    get_dbobject()->name(name_);

    update_change_date();
    undo.end(strfmt(_("Rename to '%s'"), name_.c_str()));
  }
}


std::string DBObjectEditorBE::get_comment()
{
  return get_dbobject()->comment();
}


void DBObjectEditorBE::set_comment(const std::string &descr)
{
  if (get_dbobject()->comment() != descr)
  {
    RefreshCentry __centry(*this);
    grt::AutoUndo undo(get_grt(), new UndoObjectChangeGroup(get_dbobject().id(), "comment"));

    get_dbobject()->comment(descr);

    update_change_date();
    undo.end(_("Edit Comment"));
  }
}



std::string DBObjectEditorBE::get_sql()
{
  // we should return specific sql here e.g. sqlDefinition
  return "";// get_dbobject().sql();
}


bool DBObjectEditorBE::is_sql_commented()
{
  return (*get_dbobject()->commentedOut() != 0);
}


void DBObjectEditorBE::set_sql_commented(bool flag)
{
  RefreshCentry __centry(*this);

  grt::AutoUndo undo(get_grt(), new UndoObjectChangeGroup(get_dbobject().id(), "commentedOut"));

  get_dbobject()->commentedOut(flag ? 1: 0);

  update_change_date();
  undo.end(_("Comment Out SQL"));
}


void DBObjectEditorBE::set_custom_data(const std::string &name, const std::string &value)
{
  if (get_dbobject()->customData().get_string(name) != value)
  {
    grt::AutoUndo undo(get_grt(), new UndoObjectChangeGroup(get_dbobject().id(), "custom:"+name));

    get_dbobject()->customData().set(name, grt::StringRef(value));
    update_change_date();

    undo.end(_("Set Custom Data ")+name);
  }
}


void DBObjectEditorBE::delete_custom_data(const std::string &name)
{
  grt::AutoUndo undo(get_grt());

  get_dbobject()->customData().remove(name);
  update_change_date();

  undo.end(_("Unset Custom Data ")+name);
}


size_t DBObjectEditorBE::count_custom_data()
{
  return get_dbobject()->customData().count();
}


void DBObjectEditorBE::get_custom_data(size_t index, std::string &name, std::string &value)
{
  /*QQQ
  grt::ValueRef v;

  get_dbobject()->customData().get_by_index(index, name, v);

  value= *grt::StringRef::cast_from(v);
   */
}


db_CatalogRef DBObjectEditorBE::get_catalog()
{
  GrtObjectRef object= get_dbobject();

  while (object.is_valid() &&
         !object.is_instance(db_Catalog::static_class_name()))
    object= object->owner();

  return db_CatalogRef::cast_from(object);
}


db_SchemaRef DBObjectEditorBE::get_schema()
{
  GrtObjectRef object= get_dbobject();

  while (object.is_valid() &&
         !object.is_instance(db_Schema::static_class_name()))
    object= object->owner();

  return db_SchemaRef::cast_from(object);
}

std::string DBObjectEditorBE::get_schema_name()
{
  return get_schema()->name();
}


db_SchemaRef DBObjectEditorBE::get_schema_with_name(const std::string &schema_name)
{
  db_CatalogRef catalog= db_CatalogRef::cast_from(get_schema()->owner());

  return grt::find_named_object_in_list(catalog->schemata(), schema_name);
}


std::vector<std::string> DBObjectEditorBE::get_all_table_names()
{
  grt::ListRef<db_Schema> schema_list= db_CatalogRef::cast_from(get_schema()->owner())->schemata();
  std::vector<std::string> table_list;
  db_SchemaRef myschema= get_schema();

  // put tables in the same schema at the top
  table_list= get_schema_table_names();

  for (size_t sc= schema_list.count(), s= 0; s < sc; s++)
  {
    db_SchemaRef schema= schema_list[s];
    std::string schema_name= schema->name();

    if (schema == myschema)
      continue;
    
    for (size_t c= schema->tables().count(), i= 0; i < c; i++)
      table_list.push_back(schema_name+"."+*schema->tables()[i]->name());
  }

  std::sort(table_list.begin(), table_list.end());
  return table_list;
}


std::vector<std::string> DBObjectEditorBE::get_schema_table_names()
{
  db_SchemaRef schema= get_schema();
  std::vector<std::string> table_list;
  std::string schema_name= schema->name();

  if (schema.is_valid())
  {
    for (size_t c= schema->tables().count(), i= 0; i < c; i++)
      table_list.push_back(schema_name+"."+*schema->tables()[i]->name());
  }

  std::sort(table_list.begin(), table_list.end());
  return table_list;
}


std::vector<std::string> DBObjectEditorBE::get_table_column_names(const std::string &fq_table_name)
{
  db_SchemaRef schema;
  std::vector<std::string> columns;
  
  if (fq_table_name.empty())
    return columns;
  
  std::vector<std::string> parts= split_string(fq_table_name, ".");
  std::string table_name;
  
  if (parts.size()==1)
  {
    table_name= parts[0];
    schema= get_schema();
  }
  else
  {
    schema= get_schema_with_name(parts[0]);
    table_name= parts[1];
  }

  if (schema.is_valid())
  {
    db_TableRef table(grt::find_named_object_in_list(schema->tables(), table_name));

    if (table.is_valid())
    {
      for (size_t c= table->columns().count(), i= 0; i < c; i++)
        columns.push_back(*table->columns()[i]->name());
    }
  }

  return columns;
}


std::vector<std::string> DBObjectEditorBE::get_table_column_names(const db_TableRef &table)
{
  std::vector<std::string> columns;

  if (table.is_valid())
    for (size_t c= table->columns().count(), i= 0; i < c; i++)
      columns.push_back(*table->columns()[i]->name());

  return columns;
}

std::vector<std::string> DBObjectEditorBE::get_charset_collation_list()
{
  std::vector<std::string> collation_list;
  grt::ListRef<db_CharacterSet> charsets= _rdbms->characterSets();
  size_t chsz= charsets.count();

  for(size_t j= 0; j < chsz; j++)
  {
    db_CharacterSetRef cs= charsets.get(j);
    grt::StringListRef collations(cs->collations());
    std::string cs_name(cs->name().c_str());

    collation_list.push_back(format_charset_collation(cs_name, ""));
    
    size_t collsz= collations.count();
    for(size_t k= 0; k < collsz; k++)
      collation_list.push_back(format_charset_collation(cs_name, collations.get(k)));
  }

  return collation_list;
}


std::string DBObjectEditorBE::format_charset_collation(const std::string &charset, const std::string &collation)
{
  if (collation.empty())
  {
    if (charset.empty())
      return " - ";
    else
      return charset + " - " + DEFAULT_COLLATION_CAPTION;
  }
  else
    return charset + " - " + collation;
}


bool DBObjectEditorBE::parse_charset_collation(const std::string &str, std::string &charset, std::string &collation)
{
  std::string::size_type pos;
  if ((pos= str.find(" - ")) != std::string::npos)
  {
    charset= str.substr(0, pos);
    collation= str.substr(pos+3);
    if (collation == DEFAULT_COLLATION_CAPTION)
      collation= "";

    return true;
  }
  charset= "";
  collation= "";
  return false;
}


Sql_editor * DBObjectEditorBE::get_sql_editor()
{
  if (!_sql_editor.get())
    _sql_editor= Sql_editor::create_sql_editor(get_rdbms());
  return _sql_editor.get();
}


void DBObjectEditorBE::set_sql(const std::string &sql, const db_DatabaseObjectRef &template_obj, const std::string &comment)
{
  _sql_parser_log.clear();
  if ( _sql_parser.get() ) // have to deal with auto_ptr
    _sql_parser->parse_error_cb(_sql_parser_err_cb);

  std::string task_desc= "Parse " + template_obj->get_metaclass()->get_attribute("caption") +
    (comment.empty() ? std::string("") : " - " + comment);

  bec::GRTTask *task= new bec::GRTTask(task_desc,
    _grtm->get_dispatcher(),
    sigc::bind<grt::StringRef>(_sql_parser_task_cb, grt::StringRef(sql)));

  task->signal_message().connect(sigc::mem_fun(this, &DBObjectEditorBE::sql_parser_msg_cb));
  task->signal_finished().connect(sigc::mem_fun(this, &DBObjectEditorBE::sql_parser_task_finished_cb));

  _grtm->dispatch_task(task);
}


void DBObjectEditorBE::sql_parser_msg_cb(const grt::Message &msg)
{
  if (msg.type <= grt::OutputMsg)
    _sql_parser_log.push_back(msg.format());
}


void DBObjectEditorBE::sql_parser_task_finished_cb(grt::ValueRef value)
{
  _sql_parser_log_cb(_sql_parser_log);
  _sql_parser_log.clear();
  do_ui_refresh();
}


void DBObjectEditorBE::set_sql_parser_task_cb(const Sql_parser_task_cb &cb)
{
  _sql_parser_task_cb= cb;
}


void DBObjectEditorBE::set_sql_parser_log_cb(const Sql_parser_log_cb &cb)
{
  _sql_parser_log_cb= cb;
}


void DBObjectEditorBE::set_sql_parser_err_cb(const Sql_parser_err_cb &cb)
{
  _sql_parser_err_cb = cb;
}
