/* 
 * © 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 "workbench_physical_tablefigure_impl.h"
#include "workbench_physical_diagram_impl.h"
#include "workbench_physical_model_impl.h"
#include "model_layer_impl.h"

#include "table_figure.h"
#include "table_figure_wb.h"

#include "grt/common.h"
#include "grt/grt_manager.h"
#include "grtdb/db_object_helpers.h"



workbench_physical_TableFigure::ImplData::ImplData(workbench_physical_TableFigure *self)
  : super(self), _figure(0)
{
  _pending_columns_sync= false;
  _pending_index_sync= false;

  self->signal_changed().connect(sigc::mem_fun(this, &ImplData::member_changed));
}


void workbench_physical_TableFigure::ImplData::update_options(const std::string &key)
{
  if (key == "workbench.physical.TableFigure:MaxColumnsDisplayed")
  {
    workbench_physical_ModelRef model(workbench_physical_ModelRef::cast_from(self()->owner()->owner()));
    int max_columns= model->get_data()->get_int_option(key, 30);
    
    if (_figure)
      _figure->set_max_columns_shown(max_columns);
  }
  
  if (bec::has_prefix(key, "workbench.physical.ObjectFigure:")
      || bec::has_prefix(key, "workbench.physical.TableFigure:"))
  {
    if (_figure)
      sync_columns();
  }
}


void workbench_physical_TableFigure::ImplData::set_in_view(bool flag)
{
  if (!self()->owner().is_valid())
    throw std::logic_error("adding figure to view before setting owner");

  if (flag)
  {
    if (self()->_table.is_valid())
    {
      workbench_physical_DiagramRef diagram(workbench_physical_DiagramRef::cast_from(self()->_owner));
      diagram->get_data()->add_mapping(self()->_table, self());
    }
  }
  else
  {
    if (self()->_table.is_valid())
    {
      workbench_physical_DiagramRef diagram(workbench_physical_DiagramRef::cast_from(self()->_owner));
      diagram->get_data()->remove_mapping(self()->_table);
    }
  }
  
  model_Figure::ImplData::set_in_view(flag);
}


void workbench_physical_TableFigure::ImplData::set_table(const db_TableRef &table)
{
  // Check if we had a valid table before and revert the previous setup if so.
  if (self()->_table.is_valid())
  {
    if (self()->_owner.is_valid())
      workbench_physical_DiagramRef::cast_from(self()->_owner)->get_data()->remove_mapping(self()->_table);

    _table_fk_conn.disconnect();
    _refresh_conn.disconnect();
    _changed_conn.disconnect();
  }

  // Now replace the table and run its setup.
  self()->_table= table;
  
  if (self()->_table.is_valid())
  {
    if (self()->_owner.is_valid())
      workbench_physical_DiagramRef::cast_from(self()->_owner)->get_data()->add_mapping(table, self());
    
    _table_fk_conn= table->signal_foreignKeyChanged().connect(sigc::mem_fun(this, &ImplData::fk_changed));
    _refresh_conn= table->signal_refreshDisplay().connect(sigc::mem_fun(this, &ImplData::content_changed));
    _changed_conn= table->signal_changed().connect(sigc::mem_fun(this, &ImplData::table_member_changed));

    self()->_name= self()->_table->name();

    if (_figure)
    {
      // Refresh everything because table has been replaced.
      _figure->get_title()->set_title(*self()->_table->name());
      run_later(sigc::mem_fun(this, &workbench_physical_TableFigure::ImplData::sync_columns));
      run_later(sigc::mem_fun(this, &workbench_physical_TableFigure::ImplData::sync_indexes));
    }
    else
    {
      _table_fk_conn.block();
      _changed_conn.block();
      _refresh_conn.block();
      try_realize();
    }
  }
  else
    unrealize();
}


void workbench_physical_TableFigure::ImplData::table_member_changed(const std::string &name, const grt::ValueRef &ovalue)
{
  if (name == "name")
  {
    self()->_name= self()->_table->name();

    if (_figure)
      _figure->get_title()->set_title(*self()->_table->name());
  }
  else if (name == "primaryKey")
  {
    if (_figure)
    {
      if (!_pending_columns_sync)
      {
        _pending_columns_sync= true;
        run_later(sigc::mem_fun(this, &workbench_physical_TableFigure::ImplData::sync_columns));
      }
    }
  }
}


void workbench_physical_TableFigure::ImplData::fk_changed(const db_ForeignKeyRef &fk)
{
  // resync columns to update FK indicators
  if (_figure)
  {
    if (!_pending_columns_sync)
    {
      _pending_columns_sync= true;
      run_later(sigc::mem_fun(this, &workbench_physical_TableFigure::ImplData::sync_columns));
    }
  }  
}


void workbench_physical_TableFigure::ImplData::content_changed(const std::string &where)
{
  if ((where == "column" || where == "foreignKey") &&
      _figure && !_pending_columns_sync)
  {
    _pending_columns_sync= true;
    run_later(sigc::mem_fun(this, &workbench_physical_TableFigure::ImplData::sync_columns));
  }

  if (where == "index" && _figure && !_pending_index_sync)
  {
    _pending_index_sync= true;
    run_later(sigc::mem_fun(this, &workbench_physical_TableFigure::ImplData::sync_indexes));
  }
}


void workbench_physical_TableFigure::ImplData::member_changed(const std::string &name, const grt::ValueRef &ovalue)
{
/* not good  if (name == "name")
  {
    if (self()->_table.is_valid())
      self()->_table->name(self()->_name);
  }
  */
  /*
  else if (name == "columnsExpanded")
  {
    return; // ignore
  }*/
  /*else*/ if (name == "indicesExpanded")
  {
    if (_figure)
      _figure->toggle_indexes(*self()->_indicesExpanded!=0);
  }
  else if (name == "triggersExpanded")
  {
    if (_figure)
      _figure->toggle_triggers(*self()->_triggersExpanded!=0);
  }
  else if (name == "color" && self()->owner().is_valid() && self()->owner()->owner().is_valid() && 
    self()->owner()->owner()->get_data()->get_int_option("SynchronizeObjectColors", 0))
  {
    self()->owner()->owner()->get_data()->update_object_color_in_all_diagrams(self()->_color,
                                                                              "table", self()->_table.id());
    
    super::member_changed(name, ovalue);
  }
}



void workbench_physical_TableFigure::ImplData::sync_columns()
{
  if (_figure)
  {
    wbfig::Table::ItemList::iterator iter= _figure->begin_columns_sync();

    grt::ListRef<db_Column> columns(self()->_table->columns());
    bool show_type= self()->owner()->owner()->get_data()->get_int_option("workbench.physical.TableFigure:ShowColumnTypes", 1)!=0;
    bool show_flags= self()->owner()->owner()->get_data()->get_int_option("workbench.physical.TableFigure:ShowColumnFlags", 0)!=0;
    int max_type_length= self()->owner()->owner()->get_data()->get_int_option("workbench.physical.TableFigure:MaxColumnTypeLength", 20);
    for (size_t c= columns.count(), i= 0; i < c; i++)
    {
      db_ColumnRef column= columns.get(i);
      std::string text;
      wbfig::ColumnFlags flags= (wbfig::ColumnFlags)0;
      
      if (self()->_table->isPrimaryKeyColumn(column))
        flags= (wbfig::ColumnFlags)(flags|wbfig::ColumnPK);
      if (self()->_table->isForeignKeyColumn(column))
        flags= (wbfig::ColumnFlags)(flags|wbfig::ColumnFK);

      if (column->isNotNull())
        flags= (wbfig::ColumnFlags)(flags|wbfig::ColumnNotNull);

      if (column->flags().get_index("UNSIGNED") != grt::BaseListRef::npos)
        flags= (wbfig::ColumnFlags)(flags|wbfig::ColumnUnsigned);
      if (column.has_member("autoIncrement") && column.get_integer_member("autoIncrement") != 0)
        flags= (wbfig::ColumnFlags)(flags|wbfig::ColumnAutoIncrement);

      text= *column->name();
      if (show_type)
      {
        std::string type= column->formattedRawType();
        
        // truncate enum and set types if its too long
        if (max_type_length > 0 && (int) type.length() > max_type_length)
        {
          if (g_strncasecmp(type.c_str(), "set(", 4) == 0)
          {
            type= type.substr(0, 3).append("(...)");
          }
          else if (g_strncasecmp(type.c_str(), "enum(", 5) == 0)
          {
            type= type.substr(0, 4).append("(...)");
          }
        }
        text.append(" ").append(type);
      }
      
      iter= _figure->sync_next_column(iter,
                                      column.id(),
                                      flags,
                                      text);
    }

    _figure->set_show_flags(show_flags);
    _figure->end_columns_sync(iter);

    _figure->set_dependant(self()->_table->isDependantTable() != 0);

    _pending_columns_sync= false;
  }
}


void workbench_physical_TableFigure::ImplData::sync_indexes()
{
  if (_figure)
  {
    wbfig::Table::ItemList::iterator iter= _figure->begin_indexes_sync();

    grt::ListRef<db_Index> indexes(self()->_table->indices());

    for (size_t c= indexes.count(), i= 0; i < c; i++)
    {
      db_IndexRef index(indexes.get(i));
      std::string text;
      
      text= *index->name();
      
      iter= _figure->sync_next_index(iter,
                                     index.id(),
                                     text);
    }
    _figure->end_indexes_sync(iter);

    if (_figure->get_index_title() && !_figure->indexes_hidden())
      _figure->get_index_title()->set_visible(indexes.count() > 0);

    _pending_index_sync= false;
  }
}


bool workbench_physical_TableFigure::ImplData::is_realizable()
{
  if (!super::is_realizable())
    return false;

  if (self()->_table.is_valid())
    return true;

  return false;
}


void workbench_physical_TableFigure::ImplData::unrealize()
{
  workbench_physical_ModelRef model(workbench_physical_ModelRef::cast_from(self()->owner()->owner()));
  // remove tag badges
  std::list<meta_TagRef> tags(model->get_data()->get_tags_for_dbobject(self()->_table));
  for (std::list<meta_TagRef>::const_iterator end= tags.end(), tag= tags.begin();
       tag != end; ++tag)
  {
    self()->owner()->get_data()->remove_tag_badge_from_figure(self(), *tag);
  }
  
  super::unrealize();

  delete _figure;
  _figure= 0;
}


bool workbench_physical_TableFigure::ImplData::realize()
{
  if (_figure) return true;
  if (!is_realizable()) return false;
  
  if (!is_main_thread())
  {
    run_later(sigc::hide_return(sigc::mem_fun(this, &ImplData::realize)));
    return true;
  }
  
  if (!_figure)
  {
    mdc::CanvasView *view= get_canvas_view();
    mdc::AreaGroup *agroup;
    workbench_physical_ModelRef model(workbench_physical_ModelRef::cast_from(self()->owner()->owner()));
    
    view->lock();
    
    _figure= model->get_data()->create_table_figure(view->get_current_layer(), self()->owner(), self());

    update_options("workbench.physical.TableFigure:MaxColumnsDisplayed");
    
    agroup= self()->_layer->get_data()->get_area_group();

    view->get_current_layer()->add_item(_figure, agroup);

    _figure->set_color(mdc::Color::parse(*self()->_color));
    
    _figure->get_title()->set_title(*self()->_table->name());

    _figure->get_title()->signal_expand_toggle().connect(
      sigc::bind<wbfig::Titlebar*>(sigc::mem_fun(this, &workbench_physical_TableFigure::ImplData::toggle_title), _figure->get_title()));
    if (_figure->get_index_title())
    {
      _figure->get_index_title()->signal_expand_toggle().connect(
        sigc::bind<wbfig::Titlebar*>(sigc::mem_fun(this, &workbench_physical_TableFigure::ImplData::toggle_title), _figure->get_index_title()));
    }
    if (_figure->get_trigger_title())
    {
      _figure->get_trigger_title()->signal_expand_toggle().connect(
        sigc::bind<wbfig::Titlebar*>(sigc::mem_fun(this, &workbench_physical_TableFigure::ImplData::toggle_title), _figure->get_trigger_title()));
    }

    _figure->set_dependant(self()->_table->isDependantTable() != 0);

    std::string sfont= self()->owner()->owner()->get_data()->get_string_option(strfmt("%s:SectionFont", self()->class_name().c_str()), "");
    if (!sfont.empty())
      _figure->set_section_font(parse_font_spec(sfont));

    _figure->toggle(*self()->_expanded!=0);
    _figure->toggle_indexes(*self()->_indicesExpanded!=0);
    
    if (self()->_table->columns().count() > 0)
      sync_columns();
    if (self()->_table->indices().count() > 0)
      sync_indexes();

    finish_realize();
    
    view->unlock();

    notify_realized();

    // add badges for each tag
    std::list<meta_TagRef> tags(model->get_data()->get_tags_for_dbobject(self()->_table));
    for (std::list<meta_TagRef>::const_iterator end= tags.end(), tag= tags.begin();
         tag != end; ++tag)
    {
      self()->owner()->get_data()->add_tag_badge_to_figure(self(), *tag);
    }
    
    // unblock refresh signals
    _table_fk_conn.unblock();
    _changed_conn.unblock();
    _refresh_conn.unblock();    
  }
  return true;
}

void workbench_physical_TableFigure::ImplData::toggle_title(bool expanded, wbfig::Titlebar *sender)
{
  if (sender == _figure->get_title())
  {
    grt::AutoUndo undo(self()->get_grt());
    self()->expanded(expanded);
    undo.end(expanded ? _("Expand Table") : _("Collapse Table"));
  }
  else if (sender == _figure->get_index_title())
  {
    grt::AutoUndo undo(self()->get_grt());
    self()->indicesExpanded(expanded);
    undo.end(expanded ? _("Expand Table Indices") : _("Collapse Table Indices"));
  }
  else if (sender == _figure->get_trigger_title())
  {
    grt::AutoUndo undo(self()->get_grt());
    self()->triggersExpanded(expanded);
    undo.end(expanded ? _("Expand Table Triggers") : _("Collapse Table Triggers"));
  }
}


db_ColumnRef workbench_physical_TableFigure::ImplData::get_column_at(mdc::CanvasItem *item)
{
  if (_figure && !_figure->get_columns()->empty())
  {
    for (wbfig::Table::ItemList::const_iterator iter= _figure->get_columns()->begin();
         iter != _figure->get_columns()->end();
         ++iter)
    {
      if ((*iter) == item)
        return grt::find_object_in_list(self()->_table->columns(), (*iter)->get_id());
    }
  }
  return db_ColumnRef();
}


db_IndexRef workbench_physical_TableFigure::ImplData::get_index_at(mdc::CanvasItem *item)
{
  if (_figure && _figure->get_indexes() && !_figure->get_indexes()->empty())
  {
    for (wbfig::Table::ItemList::const_iterator iter= _figure->get_indexes()->begin();
         iter != _figure->get_indexes()->end();
         ++iter)
    {
      if ((*iter) == item)
        return grt::find_object_in_list(self()->_table->indices(), (*iter)->get_id());
    }
  }
  return db_IndexRef();
}


void workbench_physical_TableFigure::ImplData::set_column_highlighted(const db_ColumnRef &column, const mdc::Color *color)
{
  if (_figure)
  {
    for (wbfig::Table::ItemList::const_iterator iter= _figure->get_columns()->begin();
        iter != _figure->get_columns()->end();
        ++iter)
    {
      if (!column.is_valid() || (*iter)->get_id() == column.id())
      {
        (*iter)->set_highlight_color(color);
        (*iter)->set_highlighted(true);
        if (column.is_valid())
          break;
      }
    }
  }
}


void workbench_physical_TableFigure::ImplData::set_column_unhighlighted(const db_ColumnRef &column)
{
  if (_figure)
  {
    for (wbfig::Table::ItemList::const_iterator iter= _figure->get_columns()->begin();
         iter != _figure->get_columns()->end();
         ++iter)
    {
      if (!column.is_valid() || (*iter)->get_id() == column.id())
      {
        (*iter)->set_highlighted(false);
        if (column.is_valid())
          break;
      }
    }
    _figure->set_needs_render();
  }
}
