#include "stdafx.h"

#include "wb_overview_physical_schema.h"

#include "wb_context.h"
#include "wb_component_physical.h"

#include "grt/icon_manager.h"
#include "grt/clipboard.h"
#include "grt/ui_form.h"
#include "grt/exceptions.h"

#include "grts/structs.db.h"

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

#include "grtdb/db_object_helpers.h"
#include "grtdb/editor_table.h"

/**
 * @file  wb_overview_physical_schema.cpp
 * @brief Schema specific panels for the overview window
 */

using namespace bec;
using namespace wb;
using namespace wb::internal;




class SchemaObjectNode : public OverviewBE::ObjectNode
{
  SchemaObjectNode(const SchemaObjectNode &copy) : OverviewBE::ObjectNode(copy) {}

public:
  SchemaObjectNode(const db_DatabaseObjectRef &dbobject) : OverviewBE::Node() 
  {
    object= dbobject; 
    label= object->name();
  }

  virtual void delete_object(WBContext *wb)
  {
    wb->get_component<WBComponentPhysical>()->delete_db_object(db_DatabaseObjectRef::cast_from(object));
  }

  virtual bool is_deletable()
  {
    return true;
  }

  virtual bool is_renameable()
  { 
    return true;
  }

  virtual void copy_object(WBContext *wb, bec::Clipboard *clip)
  {
    clip->append_data(grt::copy_object(wb->get_grt_manager()->get_grt(), object));
    clip->set_content_description(label);
  }

  virtual bool is_copyable()
  {
    return true;
  }
};


class DummyTableEditorBE : public bec::TableEditorBE
{
  db_TableRef _table;
  bec::TableColumnsListBE _columns;
  bec::IndexListBE _indexes;
public:
  DummyTableEditorBE(bec::GRTManager *grtm, const db_TableRef &table, const db_mgmt_RdbmsRef &rdbms)
  : bec::TableEditorBE(grtm, table, rdbms), _table(table), _columns(this), _indexes(this)
  {
  }
  
  virtual db_TableRef get_table() { return _table; }
  virtual void set_table_option_by_name(const std::string& name, const std::string& value) {}
  virtual std::string get_table_option_by_name(const std::string& name) { return ""; }
  
  virtual bec::TableColumnsListBE *get_columns() { return &_columns; }
  virtual bec::IndexListBE *get_indexes() { return &_indexes; }
  virtual std::vector<std::string> get_index_types() { return std::vector<std::string>(); }
};


class SchemaTableNode : public SchemaObjectNode
{
public:
  SchemaTableNode(const db_DatabaseObjectRef &dbobject) : SchemaObjectNode(dbobject) {}

  static SchemaObjectNode *create(const db_DatabaseObjectRef &dbobject)
  {
    return new SchemaTableNode(dbobject);
  }

  virtual std::string get_detail(int field) 
  {
    switch (field)
    {
    case 0: // engine
      return db_mysql_TableRef::cast_from(object)->tableEngine();
    case 1: // created
      return db_TableRef::cast_from(object)->createDate();
    case 2: // modified
      return db_TableRef::cast_from(object)->lastChangeDate();
    case 3: // comment
      return db_TableRef::cast_from(object)->comment();
    }
    return "";
  }
  
  virtual bool rename(WBContext *wb, const std::string &name)
  {
    db_TableRef table(db_TableRef::cast_from(object));
    
    if (table.is_valid())
    {
      // temporary code 
      DummyTableEditorBE *editor= new DummyTableEditorBE(wb->get_grt_manager(), table, get_rdbms_for_db_object(table));
      
      editor->set_name(name);
      
      delete editor;
    }
    else
      throw std::runtime_error("rename not implemented for this object");
    
    return true;
  }  
};


class SchemaViewNode : public SchemaObjectNode
{
public:
  SchemaViewNode(const db_DatabaseObjectRef &dbobject) : SchemaObjectNode(dbobject) {}

  static SchemaObjectNode *create(const db_DatabaseObjectRef &dbobject)
  {
    return new SchemaViewNode(dbobject);
  }

  virtual bool is_renameable()
  { 
    return false;
  }

  virtual std::string get_detail(int field) 
  {
    switch (field)
    {
    case 0: // created
      return db_ViewRef::cast_from(object)->createDate();
    case 1: // modified
      return db_ViewRef::cast_from(object)->lastChangeDate();
    case 2: // comment
      return db_ViewRef::cast_from(object)->comment();
    }
    return "";
  }
};


class SchemaRoutineGroupNode : public SchemaObjectNode
{
public:
  SchemaRoutineGroupNode(const db_DatabaseObjectRef &dbobject) : SchemaObjectNode(dbobject) {}

  static SchemaObjectNode *create(const db_DatabaseObjectRef &dbobject)
  {
    return new SchemaRoutineGroupNode(dbobject);
  }

  virtual std::string get_detail(int field) 
  {
    switch (field)
    {
    case 0: // created
      return db_RoutineGroupRef::cast_from(object)->createDate();
    case 1: // modified
      return db_RoutineGroupRef::cast_from(object)->lastChangeDate();
    case 2: // comment
      return db_RoutineGroupRef::cast_from(object)->comment();
    }
    return "";
  }
};


class SchemaRoutineNode : public SchemaObjectNode
{
public:
  SchemaRoutineNode(const db_DatabaseObjectRef &dbobject) : SchemaObjectNode(dbobject) {}

  static SchemaObjectNode *create(const db_DatabaseObjectRef &dbobject)
  {
    return new SchemaRoutineNode(dbobject);
  }

  virtual std::string get_detail(int field) 
  {
    switch (field)
    {
    case 0: // created
      return db_RoutineRef::cast_from(object)->createDate();
    case 1: // modified
      return db_RoutineRef::cast_from(object)->lastChangeDate();
    case 2: // comment
      return db_RoutineRef::cast_from(object)->comment();
    }
    return "";
  }

  virtual bool is_renameable()
  { 
    return false;
  }
};




/*
 * MySQL
 * 
 * [+] Physical Schemata 
 * ------------------------------------------------------------
 * 
 * | webshop       |  inventory      | Add Schema       |
 * |  MySQL schema |    MySQL schema |   Add new schema |
 * ------------------------------------------------------------
 * Tables
 * ...
 * Views
 * ...
 * Routines
 * 
 * [+] Synchronized Databases
 * -------------------------------------------------------------
 * 
 * 
 */


static bool CompNodeLabel(OverviewBE::Node *a, OverviewBE::Node *b)
{
  return a->label < b->label;
}


class wb::internal::PhysicalSchemaContentNode : public OverviewBE::ContainerNode
{
  std::vector<std::string> _fields;

private:
  std::string id;
  grt::ListRef<db_DatabaseObject> _list;
  SchemaObjectNode* (*_create_node)(const db_DatabaseObjectRef&);

public:
  PhysicalSchemaContentNode(const std::string &name, const db_SchemaRef &owner,
                            grt::ListRef<db_DatabaseObject> list,
                            SchemaObjectNode*(*create_node)(const db_DatabaseObjectRef&))
    : ContainerNode(OverviewBE::OItem), _list(list), _create_node(create_node)
  {
    id= owner->id() + "/" + name;
    label= name;
    type= OverviewBE::OSection;
    small_icon= 0;
    large_icon= 0;
    expanded= false;
    display_mode= OverviewBE::MSmallIcon;

    refresh_children();
  }


  virtual void refresh_children()
  {
    Node *add_node= 0;

    focused= 0;

    if (!children.empty())
    {
      add_node= children.front();
      children.erase(children.begin());
    }
    clear_children();

    if (add_node)
      children.push_back(add_node);

    for (size_t c= _list.count(), i= 0; i < c; i++)
    {
      db_DatabaseObjectRef object(_list[i]);

      SchemaObjectNode *node= _create_node(object);
    
      node->type= OverviewBE::OItem;
      node->label= object->name();
      node->small_icon= IconManager::get_instance()->get_icon_id(object->get_metaclass(), Icon16);
      node->large_icon= IconManager::get_instance()->get_icon_id(object->get_metaclass(), Icon48);

      children.push_back(node);
    }
    //std::sort(children.begin(), children.end(), CompNodeLabel);
  }


  void set_detail_fields(const std::vector<std::string> &fields)
  {
    _fields= fields;
  }

  virtual int count_detail_fields() 
  {
    return _fields.size();
  }

  virtual std::string get_detail_name(int field) 
  {
    return _fields[field];
  }

  virtual std::string get_unique_id()
  { 
    return id;
  }
};


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

PhysicalSchemaNode::PhysicalSchemaNode(db_SchemaRef schema)
: ContainerNode(OverviewBE::OSection)
{
  std::vector<std::string> fields;

  object= schema;

  type= OverviewBE::OGroup;
  label= schema->name();
  description= _("MySQL Schema");
  small_icon= IconManager::get_instance()->get_icon_id("db.Schema.$.png", Icon16);
  large_icon= IconManager::get_instance()->get_icon_id("db.Schema.$.png", Icon32);

  PhysicalSchemaContentNode *node;

  node= new PhysicalSchemaContentNode("Tables", schema,
    grt::ListRef<db_DatabaseObject>::cast_from(schema->tables()),
    &SchemaTableNode::create);
  fields.clear();
  fields.push_back(_("Engine"));
  fields.push_back(_("Created"));
  fields.push_back(_("Modified"));
  fields.push_back(_("Comment"));
  node->set_detail_fields(fields);
  children.push_back(node);

  OverviewBE::AddObjectNode *add_node= new OverviewBE::AddObjectNode(sigc::mem_fun(this, &PhysicalSchemaNode::add_new_db_table));
  add_node->label= _("Add Table");
  add_node->type= OverviewBE::OItem;
  add_node->small_icon= IconManager::get_instance()->get_icon_id("db.Table.$.png", Icon16, "add");
  add_node->large_icon= IconManager::get_instance()->get_icon_id("db.Table.$.png", Icon48, "add");
  node->children.insert(node->children.begin(), add_node); // Add special node in front of all others.

  node= new PhysicalSchemaContentNode("Views", schema, 
    grt::ListRef<db_DatabaseObject>::cast_from(schema->views()),
    &SchemaViewNode::create);
  fields.clear();
  fields.push_back(_("Created"));
  fields.push_back(_("Modified"));
  fields.push_back(_("Comment"));
  node->set_detail_fields(fields);
  children.push_back(node);

  add_node= new OverviewBE::AddObjectNode(sigc::mem_fun(this, &PhysicalSchemaNode::add_new_db_view));
  add_node->label= _("Add View");
  add_node->type= OverviewBE::OItem;
  add_node->small_icon= IconManager::get_instance()->get_icon_id("db.View.$.png", Icon16, "add");
  add_node->large_icon= IconManager::get_instance()->get_icon_id("db.View.$.png", Icon48, "add");
  node->children.insert(node->children.begin(), add_node);

  node= new PhysicalSchemaContentNode("Routines", schema, 
    grt::ListRef<db_DatabaseObject>::cast_from(schema->routines()),
    &SchemaRoutineNode::create);
  fields.clear();
  fields.push_back(_("Created"));
  fields.push_back(_("Modified"));
  fields.push_back(_("Comment"));
  node->set_detail_fields(fields);
  children.push_back(node);

  add_node= new OverviewBE::AddObjectNode(sigc::mem_fun(this, &PhysicalSchemaNode::add_new_db_routine));
  add_node->label= _("Add Routine");
  add_node->type= OverviewBE::OItem;
  add_node->small_icon= IconManager::get_instance()->get_icon_id("db.Routine.$.png", Icon16, "add");
  add_node->large_icon= IconManager::get_instance()->get_icon_id("db.Routine.$.png", Icon48, "add");
  node->children.insert(node->children.begin(), add_node);

  node= new PhysicalSchemaContentNode("Routine Groups", schema, 
    grt::ListRef<db_DatabaseObject>::cast_from(schema->routineGroups()),
    &SchemaRoutineGroupNode::create);
  fields.clear();
  fields.push_back(_("Created"));
  fields.push_back(_("Modified"));
  fields.push_back(_("Comment"));
  node->set_detail_fields(fields);
  children.push_back(node);

  add_node= new OverviewBE::AddObjectNode(sigc::mem_fun(this, &PhysicalSchemaNode::add_new_db_routine_group));
  add_node->label= _("Add Group");
  add_node->type= OverviewBE::OItem;
  add_node->small_icon= IconManager::get_instance()->get_icon_id("db.RoutineGroup.$.png", Icon16, "add");
  add_node->large_icon= IconManager::get_instance()->get_icon_id("db.RoutineGroup.$.png", Icon48, "add");
  node->children.insert(node->children.begin(), add_node);
}


void PhysicalSchemaNode::paste_object(WBContext *wb, bec::Clipboard *clip)
{
  std::list<grt::ObjectRef> objects(clip->get_data());
  db_SchemaRef schema(db_SchemaRef::cast_from(object));
  WBComponentPhysical *pc= wb->get_component<WBComponentPhysical>();
  grt::CopyContext context(wb->get_grt_manager()->get_grt());

  grt::AutoUndo undo(wb->get_grt());
  for (std::list<grt::ObjectRef>::const_iterator iter= objects.begin(); 
    iter != objects.end(); ++iter)
  {
    if (iter->is_instance(db_DatabaseObject::static_class_name()))
      pc->clone_db_object_to_schema(schema, db_DatabaseObjectRef::cast_from(*iter), context);
  }
  context.finish();
  undo.end(strfmt(_("Paste %s"), clip->get_content_description().c_str()));
}

bool PhysicalSchemaNode::is_renameable() 
{ 
  return false;
}


bool PhysicalSchemaNode::rename(WBContext *wb, const std::string &name)
{
  return false;
}

bool PhysicalSchemaNode::is_pasteable(bec::Clipboard *clip)
{
  std::string prefix= object.get_metaclass()->name();

  prefix= prefix.substr(0, prefix.length()-strlen(".Schema"));

  std::list<grt::ObjectRef> objects(clip->get_data());
  for (std::list<grt::ObjectRef>::const_iterator iter= objects.begin(); 
    iter != objects.end(); ++iter)
  {
    if (!(*iter).is_instance(db_Table::static_class_name())
      && !(*iter).is_instance(db_View::static_class_name())
      && !(*iter).is_instance(db_Routine::static_class_name())
      && !(*iter).is_instance(db_RoutineGroup::static_class_name()))
      return false;

    if (!bec::has_prefix((*iter).get_metaclass()->name(), prefix))
      return false;
  }
  return !objects.empty();
}


void PhysicalSchemaNode::delete_object(WBContext *wb)
{
  wb->get_component<WBComponentPhysical>()->delete_db_schema(db_SchemaRef::cast_from(object));
}


bool PhysicalSchemaNode::activate(WBContext *wb)
{
  wb->open_object_editor(object);
  return true;
}


bool PhysicalSchemaNode::is_deletable()
{
  return true;
}


void PhysicalSchemaNode::focus(OverviewBE *sender)
{
  db_SchemaRef schema(db_SchemaRef::cast_from(object));

  db_CatalogRef::cast_from(schema->owner())->defaultSchema(schema);
}



void PhysicalSchemaNode::refresh()
{
  label= object->name();
}





bool PhysicalSchemaNode::add_new_db_table(WBContext *wb)
{
  wb->get_component<WBComponentPhysical>()->add_new_db_table(db_SchemaRef::cast_from(object));
  return true;
}


bool PhysicalSchemaNode::add_new_db_view(WBContext *wb)
{
  wb->get_component<WBComponentPhysical>()->add_new_db_view(db_SchemaRef::cast_from(object));
  return true;
}


bool PhysicalSchemaNode::add_new_db_routine_group(WBContext *wb)
{
  wb->get_component<WBComponentPhysical>()->add_new_db_routine_group(db_SchemaRef::cast_from(object));
  return true;
}


bool PhysicalSchemaNode::add_new_db_routine(WBContext *wb)
{
  wb->get_component<WBComponentPhysical>()->add_new_db_routine(db_SchemaRef::cast_from(object));
  return true;
}
