#include "wb_config.h"

#include <grtpp.h>
#include "wb_model.h"
#include "grts/structs.workbench.physical.h"
#include "graph_renderer.h"
#include "grt/grt_manager.h"
#include <grtpp_undo_manager.h>

using namespace grt;

GRT_MODULE_ENTRY_POINT(WbModelImpl);




// plugin registration

static void def_export_view_plugin(grt::GRT* grt, const char* aName, const char* aCaption, grt::ListRef<app_Plugin> &list)
{
    app_PluginRef plugin(grt);
    app_PluginObjectInputRef pdef(grt);
    
    plugin->name((std::string("wb.model.") + aName).c_str());
    
    plugin->caption(aCaption);
    plugin->moduleName("WbModel");
    plugin->moduleFunctionName(aName);
    plugin->pluginType("normal");
    plugin->rating(100);
    plugin->showProgress(1);
    pdef->name("activeDiagram");
    pdef->objectStructName("model.Diagram");
    pdef->owner(plugin);
    plugin->inputValues().insert(pdef);
    plugin->groups().insert("Application/Workbench");

    list.insert(plugin);
}

/*
#define def_export_view_plugin(aName, aCaption)\
  {\
    app_PluginRef plugin(get_grt());\
    app_PluginObjectInputRef pdef(get_grt());\
    plugin->name("wb.model."aName);\
    plugin->caption(aCaption);\
    plugin->moduleName("WbModel");\
    plugin->moduleFunctionName(aName);\
    plugin->pluginType("normal");\
    plugin->rating(100);\
    plugin->showProgress(1);\
    pdef->name("activeDiagram");\
    pdef->objectStructName("model.Diagram");\
    pdef->owner(plugin);\
    plugin->inputValues().insert(pdef);\
    plugin->groups().insert("Application/Workbench");\
    list.insert(plugin);\
  }
*/

static void def_export_catalog_plugin(grt::GRT* grt, const char* aName, const char* aCaption, grt::ListRef<app_Plugin> &list)
{
    app_PluginRef plugin(grt);
    app_PluginObjectInputRef pdef1(grt);
    app_PluginObjectInputRef pdef2(grt);

    plugin->name((std::string("wb.model.") + aName).c_str());
    plugin->caption(aCaption);
    plugin->moduleName("WbModel");
    plugin->moduleFunctionName(aName);
    plugin->pluginType("normal");
    plugin->rating(100);
    plugin->showProgress(1);
    pdef1->name("activeModel");
    pdef1->objectStructName("workbench.physical.Model");
    pdef1->owner(plugin);
    plugin->inputValues().insert(pdef1);
  
    pdef2->name("activeCatalog");
    pdef2->objectStructName("db.Catalog");
    pdef2->owner(plugin);
    plugin->inputValues().insert(pdef2);
    plugin->groups().insert("database/Database");
    list.insert(plugin);
}
/*
#define def_export_catalog_plugin(aName, aCaption)\
  {\
    app_PluginRef plugin(get_grt());\
    app_PluginObjectInputRef pdef(get_grt());\
    plugin->name("wb.model."aName);\
    plugin->caption(aCaption);\
    plugin->moduleName("WbModel");\
    plugin->moduleFunctionName(aName);\
    plugin->pluginType("normal");\
    plugin->rating(100);\
    plugin->showProgress(1);\
    pdef.name("activeCatalog");\
    pdef.objectStructName("db.Catalog");\
    pdef.owner(plugin);\
    plugin->inputValues().insert(pdef);\
    plugin->groups().insert("database/Database");\
    list.insert(plugin);\
  }
*/

static void def_figure_selection_plugin(grt::GRT                 *grt, 
                                        const std::string        &aName, 
                                        const std::string        &aCaption, 
                                        const std::string        &aCard, 
                                        grt::ListRef<app_Plugin> &list
                                       )
{
    app_PluginRef plugin(grt);
    app_PluginSelectionInputRef pdef(grt);

    plugin->name(("wb.model." + aName).c_str());

    plugin->caption(aCaption);
    plugin->moduleName("WbModel");
    plugin->moduleFunctionName(aName);
    plugin->pluginType("normal");
    plugin->rating(100);
    plugin->showProgress(0);
    pdef->objectStructNames().insert(model_Figure::static_class_name());
    pdef->argumentCardinality(aCard);
    pdef->owner(plugin);
    plugin->inputValues().insert(pdef);
    plugin->groups().insert("Application/Workbench");
    list.insert(plugin);
}

ListRef<app_Plugin> WbModelImpl::getPluginInfo()
{
  ListRef<app_Plugin> list(get_grt());

  def_export_view_plugin(get_grt(), "center", "Center Diagram Contents", list);
  def_export_view_plugin(get_grt(), "autolayout", "Autolayout Figures", list);

  def_export_catalog_plugin(get_grt(), "createDiagramWithCatalog", "Autoplace Objects of the Catalog on New Model", list);

  def_figure_selection_plugin(get_grt(), "fitObjectsToContents", "AutoSize Objects", "+", list);
  def_export_view_plugin(get_grt(), "collapseAllObjects", "Collapse Objects", list);
  def_export_view_plugin(get_grt(), "expandAllObjects", "Expand Objects", list);

  return list;
}


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


struct Rect
{
  Rect(double left_val, double top_val, double width_val, double height_val)
    : left(left_val), top(top_val), width(width_val), height(height_val) {}
  Rect()
    : left(0.), top(0.), width(0.), height(0.) {}
  double left;
  double top;
  double width;
  double height;
};


inline Rect figure_coords(const model_FigureRef &figure)
{
  return Rect(figure->left(), figure->top(), figure->width(), figure->height());
}


template<typename T1, typename T2>
void overwrite_default_option(T2 &value, const std::string &name, const grt::DictRef &options)
{
  if (options.is_valid() && options.has_key(name))
    value= T1::cast_from(options.get(name));
}

template<typename T>
void overwrite_default_option(T &value, const std::string &name, const grt::DictRef &options, bool init_with_empty_value)
{
  if (options.is_valid() && options.has_key(name))
  {
    value= T::cast_from(options.get(name));
    if (init_with_empty_value && !value.is_valid())
      value= T(options.get_grt());
  }
}


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

std::string WbModelImpl::getTemplateDirFromName(const std::string& template_name)
{
  // get pointer to the GRT
  grt::GRT *grt= get_grt();
  std::string template_base_dir= bec::make_path(
    bec::GRTManager::get_instance_for(grt)->get_basedir(), 
    "modules/data/wb_model_reporting");

  // reformat the template name, replace spaces with _
  char *temp= g_strdup(template_name.c_str());
  char *ptr= temp; 
  while ((ptr= strchr(ptr, ' '))) 
    *ptr= '_';

  std::string template_dir(temp);
  g_free(temp);

  template_dir.append(".tpl");

  return bec::make_path(template_base_dir, template_dir);
}

WbModelImpl::WbModelImpl(grt::CPPModuleLoader *ldr)
  : grt::ModuleImplBase(ldr),
    _use_objects_from_catalog(false),
    _undo_man(NULL)
{
}


void WbModelImpl::begin_undo_group()
{
  _undo_man= get_grt()->get_undo_manager();
  if (_undo_man)
    _undo_man->begin_undo_group();
}


void WbModelImpl::end_undo_group(const std::string &action_desc)
{
  if (_undo_man)
  {
    _undo_man->end_undo_group();
    _undo_man->set_action_description(action_desc);
  }
}


int WbModelImpl::autolayout(model_DiagramRef view)
{
  int result= 0;
  ListRef<model_Object> selection= view->selection();
  ListRef<model_Layer> layers= view->layers();

  //! _gcView->canvas()->beginUpdate();

  begin_undo_group();

  do_autolayout(view->rootLayer(), selection);
  for (size_t i= 0, layerCount= layers.count(); i != layerCount; ++i)
  {
    result= do_autolayout(layers.get(i), selection);
    if (0 != result)
      break;
  }

  end_undo_group(std::string(_("Autolayout Model '")).append(view->name()).append(_("'")));

  //!_gcView->canvas()->endUpdate();
  //!_gcView->canvas()->refresh();*/

  return result;
}



int WbModelImpl::do_autolayout(const model_LayerRef &layer, ListRef<model_Object> &selection)
{
  int result= 0;

  ListRef<model_Figure> figures= layer->figures();
  ListRef<model_Connection> connections= layer->owner()->connections();
  std::map<std::string, model_FigureRef> fkTables;
  std::map<std::string, GraphNode*> nodes;
  std::list<model_FigureRef> tableList, viewList, otherList;
  bool selectedOnly= false; //! potential param

  double width= layer->width();
  double height= layer->height();
  if ((0. == width) && (0. == height))
  {
    model_DiagramRef view= model_DiagramRef::cast_from(layer->owner());
    width= view->width();
    height= view->height();
  }
  GraphRenderer arranger(10, width, height);

  // go through connections and remember all nodes that are connected
  for (size_t i= 0, edgeCount= connections.count(); i < edgeCount; ++i)
  {
    model_ConnectionRef conn= connections.get(i);
    model_FigureRef figure= conn->startFigure();
    fkTables[figure->id()]= figure;
    figure= conn->endFigure();
    fkTables[figure->id()]= figure;
  }

  // Pass coordinates to arrange code.
  size_t movableFiguresCount= 0;
  for (size_t i= 0, nodeCount= figures.count(); i < nodeCount; ++i)
  {
    model_FigureRef figure= figures.get(i);
    Rect rect= figure_coords(figure);

    if (rect.width < 1.)
      rect.width= 200.;

    if (rect.height < 1.)
      rect.height= 200.;

    // do not move non-table figures
    if (!workbench_physical_TableFigureRef::can_wrap(figure))
    {
      if (workbench_physical_ViewFigureRef::can_wrap(figure))
        viewList.push_back(figure);
      else
        otherList.push_back(figure);
      continue;
    }
    // do not move tables with no connections
    if (fkTables.find(figure->id()) == fkTables.end())
    {
      tableList.push_back(figure);
      continue;
    }
    GraphNode *node= arranger.add_node(rect.left, rect.top, rect.width, rect.height);
    nodes[figure->id()]= node;
    if (selectedOnly && find_object_in_list(selection, figure->id()).is_valid())
      node->set_movable(false);
    else
      ++movableFiguresCount;
  };
/*//!
  if (2 > movableFiguresCount)
    return 0;
*/
  for (size_t i= 0, edgeCount= connections.count(); i < edgeCount; ++i)
  {
    model_ConnectionRef conn= connections.get(i);
    GraphNode *node1 = nodes[conn->startFigure()->id()];
    GraphNode *node2 = nodes[conn->endFigure()->id()];

    if ((node1 != NULL) && (node2 != NULL))
      arranger.add_edge(node1, node2);
  }

  // Do the magic.
  if (figures.count() > 0)
    arranger.recalc();

  double bottom_y= 0.;
  double right_x= 0.;
  double left_x= 10000000000.;
  
  // update positions
  for (std::map<std::string, GraphNode*>::iterator iter= nodes.begin();
       iter != nodes.end(); ++iter)
  {
    GraphNode *node = iter->second;
    if (node != NULL)
    {
      double newx= node->newleft();
      double newy= node->newtop();

      bottom_y= std::max(newy + node->height(), bottom_y);
      left_x= std::min(newx, left_x);
      right_x= std::max(newx + node->width(), right_x);

      model_FigureRef figure= find_object_in_list(figures, iter->first);
      figure->left(newx);
      figure->top(newy);
    }
  };
  
  if (right_x < left_x)
  {
    left_x= 0;
    right_x= 1000;
  }
  else if (right_x - left_x < 1000)
    right_x= left_x + 1000;

  // arrange non-moved objects below everything else
  double x, y;
  double spacing= 20.;
  y= bottom_y + spacing;
  double row_height;
  for (size_t j= 0; j < 3; j++)
  {
    std::list<model_FigureRef> *figureList= NULL;
    
    switch (j)
    {
    case 0: figureList= &tableList; break;
    case 1: figureList= &viewList; break;
    case 2: figureList= &otherList; break;
    }
    row_height= 0;
    x= left_x;
    size_t i= 0;
    for (std::list<model_FigureRef>::iterator iter= figureList->begin();
         iter != figureList->end(); ++iter)
    {
      Rect rect= figure_coords(*iter);

      (*iter)->left(x);
      (*iter)->top(y);

      x+= rect.width + spacing;
      row_height= std::max(rect.height, row_height);
      ++i;
      if (x >= right_x)
      {
        i= 0;
        x= left_x;
        y+= row_height + spacing;
        row_height= 0;
      }
    }
    if (i > 0)
    {
      x= left_x;
      y+= row_height + spacing;
    }
  }

  return result;
}


static bool calculate_view_size(const app_PageSettingsRef &page, double &width, double &height)
{
  if (page->paperType().is_valid())
  {
    width= page->paperType()->width();
    height= page->paperType()->height();

    width-= page->marginLeft() + page->marginRight();
    height-= page->marginTop() + page->marginBottom();

    width*= page->scale();
    height*= page->scale();

    if (page->orientation() == "landscape")
      std::swap(width, height);

    return true;
  }
  else
  {
    width= 1000;
    height= 1000;
    return false;
  }
}


workbench_physical_DiagramRef WbModelImpl::add_model_view(const db_CatalogRef &catalog, int xpages, int ypages)
{//XXX TODO move this to Workbench module so we can reuse the same code as from wb_component
  // also add code to place db objects or figures in canvas 
  workbench_physical_DiagramRef view;

  workbench_physical_ModelRef model= workbench_physical_ModelRef::cast_from(catalog->owner());

  app_PageSettingsRef page(app_PageSettingsRef::cast_from(get_grt()->get("/wb/doc/pageSettings")));
  double width, height;

  calculate_view_size(page, width, height);

  std::string name_prefix= "Model";
  std::string view_class_name= bec::replace_string(model->get_metaclass()->name(), ".Model", ".Diagram");
  std::string name= grt::get_name_suggestion_for_list_object(model->diagrams(), name_prefix, true);

  model_DiagramRef diagram= model->addNewDiagram(0);
  
  view->name(StringRef(name));
  view->width(DoubleRef(width * xpages));
  view->height(DoubleRef(height * ypages));
  view->zoom(DoubleRef(1));

  return view;
}



static workbench_physical_DiagramRef create_view_for_object_count(workbench_physical_ModelRef model, int object_count)
{
  int xpages= 2, ypages= 1;
  int pages;
  // guesstimate about 20 objects per page
  pages= object_count / 20;
  ypages= (int)sqrt((float)pages);
  if (ypages < 1) 
    ypages= 1;
  xpages= pages / ypages;
  if (xpages < 1) 
    xpages= 1;

  workbench_physical_DiagramRef view=
    workbench_physical_DiagramRef::cast_from(model->addNewDiagram(false));

  view->setPageCounts(xpages, ypages);
  
  return view;
}


int WbModelImpl::createDiagramWithObjects(workbench_physical_ModelRef model, grt::ListRef<GrtObject> objects)
{
  int object_count= objects.count();

  if (object_count > 0)
  {
    begin_undo_group();
    workbench_physical_DiagramRef view= create_view_for_object_count(model, object_count);

    do_autoplace_any_list(view, objects);
    ListRef<db_Table> tables(get_grt());
    for (size_t n= 0, count= objects.count(); n < count; ++n)
    {
      if (db_TableRef::can_wrap(objects[n]))
      {
        db_TableRef table= db_TableRef::cast_from(objects[n]);
        if (table.is_valid())
          tables.insert(table);
      }
    }
    autoplace_relations(view, tables);

    autolayout(view);

    end_undo_group(_("Create Diagram with Objects"));
  }

  return 0;
}


int WbModelImpl::createDiagramWithCatalog(workbench_physical_ModelRef model, db_CatalogRef catalog)
{
  int object_count= 0;
  ListRef<db_Schema> schemata= catalog->schemata();
  for (size_t n= 0, count= schemata.count(); n < count; ++n)
  {
    db_SchemaRef schema= schemata[n];
    object_count+= schema->tables().count();
    object_count+= schema->views().count()/4; // views and routine groups
    object_count+= schema->routineGroups().count()/3; // take less space
  }

  begin_undo_group();
  workbench_physical_DiagramRef view= create_view_for_object_count(model, object_count);

  for (size_t n= 0, count= schemata.count(); n < count; ++n)
  {
    db_SchemaRef schema= schemata[n];
    do_autoplace_typed_list(view, schema->tables());
    do_autoplace_typed_list(view, schema->views());
    do_autoplace_typed_list(view, schema->routineGroups());
    autoplace_relations(view, schema->tables());
  }

  autolayout(view);

  end_undo_group(_("Create Diagram with Catalog"));
  
  return 0;
}


int WbModelImpl::do_autoplace_any_list(const model_DiagramRef &view, ListRef<GrtObject> &obj_list)
{
  if (!obj_list.is_valid())
    return 0;
  size_t count= obj_list.count();
  if (!count)
    return 0;

  DictRef wb_options= DictRef::cast_from(get_grt()->get("/wb/options/options"));
  std::string object_member;
  std::string figure_struct_name;
  std::string figure_color;
  GrtObjectRef object;
  model_LayerRef layer= view->rootLayer();

  for (size_t n= 0; n < count; ++n)
  {
    object= obj_list.get(n);

    if (object.is_instance("db.Table"))
    {
      object_member= "table";
      figure_struct_name= workbench_physical_TableFigure::static_class_name();
    }
    else if (object.is_instance("db.View"))
    {
      object_member= "view";
      figure_struct_name= workbench_physical_ViewFigure::static_class_name();
    }
    else if (object.is_instance("db.RoutineGroup"))
    {
      object_member= "routineGroup";
      figure_struct_name= workbench_physical_RoutineGroupFigure::static_class_name();
    }
    else
      continue;

    figure_color= wb_options.get_string(figure_struct_name + ":Color");

    model_FigureRef figure= get_grt()->create_object<model_Figure>(figure_struct_name);
    figure->owner(view);
    figure->layer(layer);
    figure->left(DoubleRef(0.0));
    figure->top(DoubleRef(0.0));

    if (!figure.is_valid())
      throw std::runtime_error("Could not create object of type " + figure_struct_name);

    figure->name(figure->get_metaclass()->get_attribute("caption"));

    figure->set_member(object_member.c_str(), object);
    if (!figure_color.empty())
      figure->color(StringRef(figure_color));

    view->figures().insert(figure);
    layer->figures().insert(figure);
  }

  return 0;
}


template<typename T>
int WbModelImpl::do_autoplace_typed_list(const model_DiagramRef &view, const ListRef<T> &obj_list)
{
  size_t count= obj_list.count();
  if (!count)
    return 0;

  std::string object_member;
  std::string figure_struct_name;

  Ref<T> object= obj_list.get(0);

  if (object.is_instance("db.Table"))
  {
    object_member= "table";
    figure_struct_name= workbench_physical_TableFigure::static_class_name();
  }
  else if (object.is_instance("db.View"))
  {
    object_member= "view";
    figure_struct_name= workbench_physical_ViewFigure::static_class_name();
  }
  else if (object.is_instance("db.RoutineGroup"))
  {
    object_member= "routineGroup";
    figure_struct_name= workbench_physical_RoutineGroupFigure::static_class_name();
  }
  else
    throw std::invalid_argument("trying to place invalid object on view");

  DictRef wb_options= DictRef::cast_from(get_grt()->get("/wb/options/options"));
  std::string figure_color= wb_options.get_string(figure_struct_name+":Color");
  grt::GRT *grt= get_grt();

  model_LayerRef layer= view->rootLayer();

  for (size_t n= 0; n < count; ++n)
  {
    object= obj_list.get(n);
    
    model_FigureRef figure= grt->create_object<model_Figure>(figure_struct_name);
    figure->owner(view);
    figure->layer(layer);
    figure->left(DoubleRef(0.0));
    figure->top(DoubleRef(0.0));

    if (!figure.is_valid())
      throw std::runtime_error("Could not create object of type " + figure_struct_name);

    figure->name(figure->get_metaclass()->get_attribute("caption"));

    figure->set_member(object_member.c_str(), object);
    if (!figure_color.empty())
      figure->color(figure_color);

    view->figures().insert(figure);
    layer->figures().insert(figure);
  }

  return 0;
}


int WbModelImpl::autoplace_relations(const model_DiagramRef &view, const ListRef<db_Table> &tables)
{//XXX TODO remove this (should be handled automatically by wb_component_physical)
  for (size_t t= 0, count= tables.count(); t < count; ++t)
  {
    db_TableRef table= tables.get(t);
    ListRef<db_ForeignKey> fkeys= table->foreignKeys();
    for (size_t f= 0, count= fkeys.count(); f < count; ++f)
      handle_fklist_change(view, table, fkeys.get(f), true);      
  }
  return 0;
}


void WbModelImpl::handle_fklist_change(const model_DiagramRef &view, const db_TableRef &table, const db_ForeignKeyRef &fk, bool added)
{//XXX TODO remove this
  if (!view.is_valid())
    return;

  if (!fk.is_valid())
  {
    if (!added)
    {
      // all FKs from table removed
    }
  }
  else
  {
    if (!fk->referencedTable().is_valid() || !fk->owner().is_valid())
      return;
    
    if (added)
    {
      // we have to go through all views in the model and find all table figures
      // that correspond to the FK for creating the relationship in all these views

      grt::ListRef<model_Figure> figures(view->figures());
      workbench_physical_TableFigureRef table1, table2;

      for (size_t d= figures.count(), f= 0; f < d; f++)
      {
        model_FigureRef fig(figures[f]);
        
        if (fig.is_instance(workbench_physical_TableFigure::static_class_name()))
        {
          workbench_physical_TableFigureRef tablefig(workbench_physical_TableFigureRef::cast_from(fig));
          
          if (tablefig->table() == table)
          {
            table1= tablefig;
            if (table2.is_valid())
              break;
          }
          if (tablefig->table() == fk->referencedTable())
          {
            table2= tablefig;
            if (table1.is_valid())
              break;
          }
        }
      }
      if (table1.is_valid() && table2.is_valid())
      { // both tables in the relationship are in this view, so create the connection
         
        // but 1st check if it already exists
        grt::ListRef<model_Connection> connections(view->connections());
        bool found= false;

        for (size_t d= connections.count(), j= 0; j < d; j++)
        {
          model_ConnectionRef conn(connections[j]);
            
          if (conn.is_instance(workbench_physical_Connection::static_class_name()))
          {
            workbench_physical_ConnectionRef pconn(workbench_physical_ConnectionRef::cast_from(conn));

            if (pconn->foreignKey() == fk)
            {
              found= true;
              break;
            }
          }
        }

        // connection doesnt exist yet, create it
        if (!found)
        {
          grt::GRT *grt= table.get_grt();
            
          workbench_physical_ConnectionRef conn(grt);
          conn->owner(view); 
          conn->startFigure(table1);
          conn->endFigure(table2);
          conn->caption(fk->name());
          conn->foreignKey(fk);

          view->connections().insert(conn);
        }
      }
    }
    else
    {
      // we have to go through all views in the model and find all connections
      // that correspond to the FK for removing them

      grt::ListRef<model_Connection> connections(view->connections());

      for (grt::ListRef<model_Connection>::const_reverse_iterator conn= connections.rbegin();
           conn != connections.rend(); ++conn)
      {
        if ((*conn).is_instance(workbench_physical_Connection::static_class_name()))
        {
          workbench_physical_ConnectionRef pconn(workbench_physical_ConnectionRef::cast_from(*conn));
          
          if (pconn->foreignKey() == fk)
          {
            // remove this connection
            connections.remove_value(pconn);
          }
        }
      }
    }
  }
}



int WbModelImpl::center(model_DiagramRef view)
{
  Rect model_bounds;
  model_LayerRef rootLayer(view->rootLayer());
  double view_width= rootLayer->width();
  double view_height= rootLayer->height();
  double xmin= view_width, ymin= view_height, xmax= 0, ymax= 0;

  /*KKK
  // find out model bounds
  for (size_t c= rootLayer.subLayers().count(), i= 0; i < c; i++)
  {
    model_LayerRef layer(rootLayer.subLayers().get(i));

    xmin= std::min(xmin, *layer.left());
    ymin= std::min(ymin, *layer.top());

    xmax= std::max(xmax, *layer.left() + *layer.width());
    ymax= std::max(ymax, *layer.top() + *layer.height());
  }*/

  for (size_t c= rootLayer->figures().count(), i= 0; i < c; i++)
  {
    model_FigureRef figure(rootLayer->figures().get(i));

    xmin= std::min(xmin, *figure->left());
    ymin= std::min(ymin, *figure->top());

    xmax= std::max(xmax, *figure->left() + *figure->width());
    ymax= std::max(ymax, *figure->top() + *figure->height());
  }

  model_bounds.left= xmin;
  model_bounds.top= ymin;
  model_bounds.width= xmax - xmin;
  model_bounds.height= ymax - ymin;

  // center if possible
  if (model_bounds.width <= view_width && model_bounds.height <= view_height)
  {
    double xoffs, yoffs;

    // determine offset to move everything so it gets centered
    xoffs= (view_width - model_bounds.width)/2 - model_bounds.left - model_bounds.width/2;
    yoffs= (view_height - model_bounds.height)/2 - model_bounds.top - model_bounds.height/2;

    begin_undo_group();

    /*
    // center layers that are on rootLayer
    for (size_t c= rootLayer.subLayers().count(), i= 0; i < c; i++)
    {
      model_LayerRef layer(rootLayer.subLayers().get(i));

      do_transactable_reposition(_undo_man,
                                layer,
                                *layer.left() + xoffs,
                                *layer.top() + yoffs);
    }KKK*/


    for (size_t c= rootLayer->figures().count(), i= 0; i < c; i++)
    {
      model_FigureRef figure(rootLayer->figures().get(i));

      figure->left(*figure->left() + xoffs);
      figure->top(*figure->top() + yoffs);
    }

    end_undo_group(_("Center Model"));
  }

  return 0;
}


int WbModelImpl::fitObjectsToContents(const grt::ListRef<model_Object> &selection)
{
  for (size_t c= selection.count(), i= 0; i < c; i++)
  {
    if (selection[i].is_instance<model_Figure>())
    {
      model_FigureRef figure(model_FigureRef::cast_from(selection[i]));
      int o= *figure->manualSizing();
      if (o != 0)
      {
        figure->manualSizing(0);
      }
    }
  }
  return 0;
}


int WbModelImpl::expandAllObjects(model_DiagramRef view)
{
  grt::ListRef<model_Figure> figures(view->figures());

  for (size_t c= figures.count(), i= 0; i < c; i++)
  {
    figures[i]->expanded(1);
  }

  return 0;
}


int WbModelImpl::collapseAllObjects(model_DiagramRef view)
{
  grt::ListRef<model_Figure> figures(view->figures());

  for (size_t c= figures.count(), i= 0; i < c; i++)
  {
    figures[i]->expanded(0);
  }

  return 0;
}





