/* 
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "tut_stdafx.h"

#include "wb_helpers.h"
#include "grtdb/db_object_helpers.h"
#include "grt_test_utility.h"
#include "model/wb_model_diagram_form.h"
#include "workbench/wb_history_tree.h"
#include "grtpp_undo_manager.h"
#include "grtpp_util.h"

using namespace bec;
using namespace wb;
using namespace grt;
using namespace MySQL::Geometry;


BEGIN_TEST_DATA_CLASS(wb_undo_diagram)
public:
  WBTester wb;
  WBContextUI *wbui;
  UndoManager *um;
  OverviewBE *overview;
  ModelDiagramForm *view;
  model_DiagramRef mview;
  size_t last_undo_stack_height;
  size_t last_redo_stack_height;


TEST_DATA_CONSTRUCTOR(wb_undo_diagram)
{
  wbui= wb.wb->get_ui();
  um= wb.wb->get_grt()->get_undo_manager();
  overview= wbui->get_physical_overview();
  view= 0;

  last_undo_stack_height= 0;
  last_redo_stack_height= 0;
  bool flag= wb.wb->open_document("data/workbench/undo_test_model1.mwb");
  ensure("open_document", flag);
  
  ensure_equals("schemas", wb.get_catalog()->schemata().count(), 1U);
  
  db_SchemaRef schema(wb.get_catalog()->schemata()[0]);
  
  // make sure the loaded model contains expected number of things
  ensure_equals("tables", schema->tables().count(), 4U);
  ensure_equals("views", schema->views().count(), 1U);
  ensure_equals("groups", schema->routineGroups().count(), 1U);

  ensure_equals("diagrams", wb.get_pmodel()->diagrams().count(), 1U);
  model_DiagramRef view(wb.get_pmodel()->diagrams()[0]);
  
  ensure_equals("figures", view->figures().count(), 5U);
  
  ensure_equals("layers", view->layers().count(), 1U);
  
//  wb.sync_view();
  
  this->view= wb.wb->get_model_context()->get_diagram_form_for_diagram_id(wb.get_pview().id());
  ensure("viewform", this->view != 0);
  
  this->mview= view;

}

#include "wb_undo_methods.h"

void place_figure_with_tool(const std::string &tool, double x= 10, double y= 10)
{
  view->set_tool(tool);

  view->handle_mouse_button(mdc::ButtonLeft, true, x, y, (mdc::EventState)0);
}


END_TEST_DATA_CLASS;


TEST_MODULE(wb_undo_diagram, "undo tests for diagram actions in Workbench");


// setup
TEST_FUNCTION(1)
{
  wbui->set_active_form(this->view);
  
  ensure_equals("undo stack is empty", um->get_undo_stack().size(), 0U);
}


// final check
TEST_FUNCTION(99)
{
  WBTester other;

  // unselect all
  mview->unselectAll();
    
  other.wb->open_document("data/workbench/undo_test_model1.mwb");

  // check if the document matches the one originally loaded
  grt_ensure_equals("compare model",
                    wb.wb->get_root(),
                    other.wb->get_root());
}


// Diagram
//----------------------------------------------------------------------------------------

TEST_FUNCTION(10)  //  Place Table
{
  db_SchemaRef schema= wb.get_catalog()->schemata()[0];
  size_t old_figure_count= mview->figures().count();
  size_t old_root_figure_count= mview->rootLayer()->figures().count();
  size_t old_object_count= schema->tables().count();

  WBComponentPhysical *compo= wbui->get_wb()->get_component<WBComponentPhysical>();

  compo->place_new_db_object(view, Point(10, 10), wb::ObjectTable);
  check_only_one_undo_added();

  ensure_equals("figure add", mview->figures().count(), old_figure_count+1);
  ensure_equals("table add", schema->tables().count(), old_object_count+1);
  ensure_equals("figure root add", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
  ensure_equals("figure add undo", mview->figures().count(), old_figure_count);
  ensure_equals("table add undo", schema->tables().count(), old_object_count);
  ensure_equals("figure root add undo", mview->rootLayer()->figures().count(), old_root_figure_count);

  check_redo();
  ensure_equals("figure add redo", mview->figures().count(), old_figure_count+1);
  ensure_equals("table add redo", schema->tables().count(), old_object_count+1);
  ensure_equals("figure root add redo", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
}


TEST_FUNCTION(11)  //  Place View
{
  db_SchemaRef schema= wb.get_catalog()->schemata()[0];
  size_t old_figure_count= mview->figures().count();
  size_t old_root_figure_count= mview->rootLayer()->figures().count();
  size_t old_object_count= schema->views().count();

  WBComponentPhysical *compo= wbui->get_wb()->get_component<WBComponentPhysical>();

  compo->place_new_db_object(view, Point(10, 10), wb::ObjectView);
  check_only_one_undo_added();

  ensure_equals("figure add", mview->figures().count(), old_figure_count+1);
  ensure_equals("view add", schema->views().count(), old_object_count+1);
  ensure_equals("figure root add", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
  ensure_equals("figure add undo", mview->figures().count(), old_figure_count);
  ensure_equals("view add undo", schema->views().count(), old_object_count);
  ensure_equals("figure root add undo", mview->rootLayer()->figures().count(), old_root_figure_count);
  
  check_redo();
  ensure_equals("figure add redo", mview->figures().count(), old_figure_count+1);
  ensure_equals("view add redo", schema->views().count(), old_object_count+1);
  ensure_equals("figure root add redo", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
}


TEST_FUNCTION(12)  //  Place Routine Group
{
  db_SchemaRef schema= wb.get_catalog()->schemata()[0];
  size_t old_figure_count= mview->figures().count();
  size_t old_root_figure_count= mview->rootLayer()->figures().count();
  size_t old_object_count= schema->routineGroups().count();

  WBComponentPhysical *compo= wbui->get_wb()->get_component<WBComponentPhysical>();

  compo->place_new_db_object(view, Point(10, 10), wb::ObjectRoutineGroup);
  check_only_one_undo_added();

  ensure_equals("figure add", mview->figures().count(), old_figure_count+1);
  ensure_equals("group add", schema->routineGroups().count(), old_object_count+1);
  ensure_equals("figure root add", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
  ensure_equals("figure add undo", mview->figures().count(), old_figure_count);
  ensure_equals("group add undo", schema->routineGroups().count(), old_object_count);
  ensure_equals("figure root add undo", mview->rootLayer()->figures().count(), old_root_figure_count);

  check_redo();
  ensure_equals("figure add redo", mview->figures().count(), old_figure_count+1);
  ensure_equals("group add redo", schema->routineGroups().count(), old_object_count+1);
  ensure_equals("figure root add redo", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
}


TEST_FUNCTION(13)  //  Place Image
{
  size_t old_figure_count= mview->figures().count();
  size_t old_root_figure_count= mview->rootLayer()->figures().count();

  // place image will ask for a filename of the image
  wb.add_file_for_file_dialog("../../images/ui/sakila.png");

  place_figure_with_tool(WB_TOOL_IMAGE);
  check_only_one_undo_added();

  ensure_equals("figure add", mview->figures().count(), old_figure_count+1);
  ensure_equals("figure root add", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
  ensure_equals("figure add undo", mview->figures().count(), old_figure_count);
  ensure_equals("figure root add undo", mview->rootLayer()->figures().count(), old_root_figure_count);

  check_redo();
  ensure_equals("figure add redo", mview->figures().count(), old_figure_count+1);
  ensure_equals("figure root add redo", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
}


TEST_FUNCTION(14)  //  Place Text
{
  size_t old_figure_count= mview->figures().count();
  size_t old_root_figure_count= mview->rootLayer()->figures().count();

  place_figure_with_tool(WB_TOOL_NOTE);
  check_only_one_undo_added();

  ensure_equals("figure add", mview->figures().count(), old_figure_count+1);
  ensure_equals("figure root add", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
  ensure_equals("figure add undo", mview->figures().count(), old_figure_count);
  ensure_equals("figure root add undo", mview->rootLayer()->figures().count(), old_root_figure_count);

  check_redo();
  ensure_equals("figure add redo", mview->figures().count(), old_figure_count+1);
  ensure_equals("figure root add redo", mview->rootLayer()->figures().count(), old_root_figure_count+1);

  check_undo();
}


TEST_FUNCTION(15)  //  Place Layer
{
  size_t old_layer_count= mview->layers().count();
  size_t old_root_layer_count= mview->rootLayer()->subLayers().count();

  view->set_tool(WB_TOOL_LAYER);
  view->handle_mouse_button(mdc::ButtonLeft, true, 10, 10, (mdc::EventState)0);
  view->handle_mouse_move(50, 50, mdc::SLeftButtonMask);
  view->handle_mouse_button(mdc::ButtonLeft, false, 50, 50, (mdc::EventState)0);
  check_only_one_undo_added();

  ensure_equals("layer add", mview->layers().count(), old_layer_count+1);
  ensure_equals("layer root add", mview->rootLayer()->subLayers().count(), old_root_layer_count+1);

  check_undo();
  ensure_equals("layer add undo", mview->layers().count(), old_layer_count);
  ensure_equals("layer root add undo", mview->rootLayer()->subLayers().count(), old_root_layer_count);

  check_redo();
  ensure_equals("layer add redo", mview->layers().count(), old_layer_count+1);
  ensure_equals("layer root add redo", mview->rootLayer()->subLayers().count(), old_root_layer_count+1);

  check_undo();
}


TEST_FUNCTION(16) // Place something inside a Layer
{
  size_t old_figure_count= mview->figures().count();
  size_t old_layer_count= mview->layers().count();
  size_t old_root_layer_count= mview->rootLayer()->subLayers().count();

  // place layer
  view->set_tool(WB_TOOL_LAYER);
  view->handle_mouse_button(mdc::ButtonLeft, true, 10, 10, (mdc::EventState)0);
  view->handle_mouse_move(150, 150, (mdc::EventState)0);
  view->handle_mouse_button(mdc::ButtonLeft, false, 150, 150, (mdc::EventState)0);
  check_only_one_undo_added();

  ensure_equals("layer add", mview->layers().count(), old_layer_count+1);
  ensure_equals("layer root add", mview->rootLayer()->subLayers().count(), old_root_layer_count+1);

  model_LayerRef layer(mview->layers()[old_layer_count]);
  ensure("layer", layer.is_valid());
  ensure_equals("layer empty", layer->figures().count(), 0U);
  
  // place a note inside the layer
  place_figure_with_tool(WB_TOOL_NOTE, 50, 50);
  check_only_one_undo_added();

  ensure_equals("note add", mview->figures().count(), old_figure_count+1);
  model_FigureRef figure(mview->figures()[old_figure_count]);
  
  ensure_equals("new note pos", *figure->top(), 40);
  ensure_equals("new note pos", *figure->left(), 40);

  ensure_equals("layer contains figure", layer->figures().count(), 1U);
  ensure("note layer", figure->layer() == layer);

  check_undo();
  ensure_equals("note add undo", mview->figures().count(), old_figure_count);
  ensure_equals("layer contains figure undo", layer->figures().count(), 0U);

  check_redo();
  ensure_equals("note add undo", mview->figures().count(), old_figure_count+1);
  ensure_equals("layer contains figure undo", layer->figures().count(), 1U);

  check_undo();
  
  check_undo();
}


TEST_FUNCTION(17) // Place Layer around something
{
  size_t old_figure_count= mview->figures().count();
  size_t old_layer_count= mview->layers().count();
  size_t old_root_layer_count= mview->rootLayer()->subLayers().count();

  // place a note
  view->set_tool(WB_TOOL_NOTE);
  view->handle_mouse_button(mdc::ButtonLeft, true, 20, 20, (mdc::EventState)0);
  check_only_one_undo_added();

  model_FigureRef figure(mview->figures()[old_figure_count]);

  ensure_equals("note add", mview->figures().count(), old_figure_count+1);
  
  // place layer around the note
  view->set_tool(WB_TOOL_LAYER);
  view->handle_mouse_button(mdc::ButtonLeft, true, 10, 10, (mdc::EventState)0);
  view->handle_mouse_move(50, 50, (mdc::EventState)0);
  view->handle_mouse_button(mdc::ButtonLeft, false, 100, 100, (mdc::EventState)0);
  check_only_one_undo_added();


  ensure_equals("layer add", mview->layers().count(), old_layer_count+1);
  ensure_equals("layer root add", mview->rootLayer()->subLayers().count(), old_root_layer_count+1);

  model_LayerRef layer(mview->layers()[old_layer_count]);
  ensure("layer", layer.is_valid());
  ensure_equals("layer contains note only", layer->figures().count(), 1U);
  ensure("layer contains note only", layer->figures().get_index(figure) != BaseListRef::npos);

  ensure("note layer", figure->layer() == layer);

  ensure_equals("layer add", mview->layers().count(), old_layer_count+1);
  ensure_equals("layer root add", mview->rootLayer()->subLayers().count(), old_root_layer_count+1);
  ensure_equals("layer note", layer->figures().count(), 1U);
  ensure_equals("root layer note", layer->figures().count(), 1U);

  check_undo();
  ensure_equals("layer add undo", mview->layers().count(), old_layer_count);
  ensure_equals("layer root add undo", mview->rootLayer()->subLayers().count(), old_root_layer_count);
  ensure_equals("layer note", layer->figures().count(), 0U);

  check_redo();
  ensure_equals("layer add redo", mview->layers().count(), old_layer_count+1);
  ensure_equals("layer root add redo", mview->rootLayer()->subLayers().count(), old_root_layer_count+1);
  ensure_equals("layer note", layer->figures().count(), 1U);

  check_undo(); // undo the layer
  
  check_undo(); // undo the note place
}


//---

TEST_FUNCTION(20) // Move Object
{
  double x, y;
  model_FigureRef figure(mview->figures()[0]);

  x= figure->left();
  y= figure->top();

  view->handle_mouse_button(mdc::ButtonLeft, true, x+5, y+5, (mdc::EventState)0);
  view->handle_mouse_move(50, 50, mdc::SLeftButtonMask);
  view->handle_mouse_button(mdc::ButtonLeft, false, 50, 50, mdc::SLeftButtonMask);
  check_only_one_undo_added();

  ensure("object moved", *figure->left() != x);

  check_undo();
  ensure_equals("move undo", *figure->left(), x);
  
  check_redo();
  ensure("move redo", *figure->left() != x);

  check_undo();
}


TEST_FUNCTION(22) // Move into and out of Layer
{
  double x, y;
  model_FigureRef figure(mview->figures()[0]);
  model_LayerRef layer(mview->layers()[0]);
  
  ensure("layer is not root", layer != mview->rootLayer());

  x= figure->left();
  y= figure->top();

  ensure("object is in root", figure->layer() == mview->rootLayer());
  ensure_equals("layer begins empty", layer->figures().count(), 0U);

  // move object into layer
  view->handle_mouse_button(mdc::ButtonLeft, true, x+5, y+5, (mdc::EventState)0);
  view->handle_mouse_move(150, 400, mdc::SLeftButtonMask);
  view->handle_mouse_button(mdc::ButtonLeft, false, 150, 400, mdc::SLeftButtonMask);
  check_only_one_undo_added();

  ensure("object moved", *figure->left() != x);
  ensure("object moved into layer", figure->layer() == layer);
  ensure("layer contains object", layer->figures().get_index(figure) != BaseListRef::npos);
  ensure("object not in root", mview->rootLayer()->figures()->get_index(figure) == BaseListRef::npos);

  check_undo();
  ensure_equals("move undo", *figure->left(), x);
  ensure("object moved into layer undo", figure->layer() == mview->rootLayer());
  ensure_equals("layer empty on undo", layer->figures().count(), 0U);
  ensure("layer not contains object", layer->figures().get_index(figure) == BaseListRef::npos);
  ensure("object in root", mview->rootLayer()->figures()->get_index(figure) != BaseListRef::npos);

  check_redo();
  ensure("move redo", *figure->left() != x);
  ensure("object moved into layer redo", figure->layer() == layer);
  ensure_equals("layer contains stuff after redo", layer->figures().count(), 1U);
  ensure("layer contains object", layer->figures().get_index(figure) != BaseListRef::npos);
  ensure("object not in root", mview->rootLayer()->figures()->get_index(figure) == BaseListRef::npos);


  // move object out of layer
  double new_x, new_y;
  new_x= *figure->left();
  new_y= *figure->top();

  view->handle_mouse_button(mdc::ButtonLeft, true, new_x+5, new_y+5, (mdc::EventState)0);
  view->handle_mouse_move(10, 10, mdc::SLeftButtonMask);
  view->handle_mouse_button(mdc::ButtonLeft, false, 10, 10, mdc::SLeftButtonMask);
  check_only_one_undo_added();


  ensure("object moved", *figure->left() != new_x);
  ensure("object moved out of layer", figure->layer() == mview->rootLayer());
  ensure("layer is empty", layer->figures().get_index(figure) == BaseListRef::npos);
  ensure("object in root layer", mview->rootLayer()->figures().get_index(figure) != BaseListRef::npos);

  check_undo();

  ensure("object moved undo", *figure->left() == new_x);
  ensure("object moved out of layer undo", figure->layer() == layer);
  ensure("layer is not empty", layer->figures().get_index(figure) != BaseListRef::npos);
  ensure("object not in root layer", mview->rootLayer()->figures().get_index(figure) == BaseListRef::npos);

  check_redo();
  ensure("object moved", *figure->left() != new_x);
  ensure("object moved out of layer", figure->layer() == mview->rootLayer());
  ensure("layer is empty", layer->figures().get_index(figure) == BaseListRef::npos);
  ensure("object in root layer", mview->rootLayer()->figures().get_index(figure) != BaseListRef::npos);

  
  // undo both operations
  check_undo();
  check_undo();
}

template<class C>
static void resize_object(ModelDiagramForm *view, C obj, double w, double h)
{
  double px, py;
  
  px= obj->left() + obj->width() + 1;
  py= obj->top() + obj->height() + 1;

  // click once to select the layer
  view->handle_mouse_button(mdc::ButtonLeft, true, px-20, py-20, (mdc::EventState)0);
  view->handle_mouse_button(mdc::ButtonLeft, false, px-20, py-20, (mdc::EventState)0);

  // resize it by "clicking" the resize thing
  view->handle_mouse_button(mdc::ButtonLeft, true, px, py, (mdc::EventState)0);
  view->handle_mouse_move(obj->left()+w, obj->top()+h, (mdc::EventState)mdc::SLeftButtonMask);
  view->handle_mouse_button(mdc::ButtonLeft, false, px, py, (mdc::EventState)mdc::SLeftButtonMask);
}


TEST_FUNCTION(24) // Move Layer
{
  fail("not impl");
}


TEST_FUNCTION(26) // Move Layer under object to capture it
{
  fail("not impl");
}


TEST_FUNCTION(28) // Resize Layer to eat a figure
{
  /* this doesnt work because there's currently no way to test resizing from here */
  fail("skip");
#if 0
  model_LayerRef layer(mview->layers()[0]);
  
  place_figure_with_tool(WB_TOOL_NOTE, 580, 480);
  check_only_one_undo_added();

  mview->unselectAll();
  
  model_FigureRef figure(find_named_object_in_list(mview->figures(), "text1"));
  
  // resize the layer to cover figure
  resize_object(view, layer, 800, 800);
  
  check_only_one_undo_added();

  ensure_equals("layer resized proeprly", *layer->width(), 800);
  ensure_equals("layer resized proeprly", *layer->height(), 800);

  ensure("layer contains object", layer->figures().get_index(figure) != BaseListRef::npos);
  ensure("object not in root", mview->rootLayer()->figures().get_index(figure) == BaseListRef::npos);
  ensure("object layer changed", figure->layer() == layer);

  check_undo();
  ensure("layer not contains object", layer->figures().get_index(figure) == BaseListRef::npos);
  ensure("object in root", mview->rootLayer()->figures().get_index(figure) != BaseListRef::npos);
  ensure("object layer changed", figure->layer() != layer);

  check_redo();
  ensure("layer contains object", layer->figures().get_index(figure) != BaseListRef::npos);
  ensure("object not in root", mview->rootLayer()->figures().get_index(figure) == BaseListRef::npos);
  ensure("object layer changed", figure->layer() == layer);

  check_undo();
  
  // undo note add
  check_undo();
#endif
}



TEST_FUNCTION(30) // Resize Table
{  
  // same as previous test
  fail("skip");
#if 0
  model_FigureRef figure(find_named_object_in_list(mview->figures(), "table1"));
  
  ensure("table found", figure.is_valid());

  double w,h;
  w= figure->width();
  h= figure->height();
  
  // resize the figure
  resize_object(view, figure, 150, 200);
  check_only_one_undo_added();

  ensure_equals("figure resized proeprly", *figure->width(), 150);
  ensure_equals("figure resized proeprly", *figure->height(), 200);
  
  check_undo();
  ensure_equals("figure resized proeprly", *figure->width(), w);
  ensure_equals("figure resized proeprly", *figure->height(), h);

  check_redo();
  ensure_equals("figure resized proeprly", *figure->width(), 150);
  ensure_equals("figure resized proeprly", *figure->height(), 200);
  check_undo();
#endif
}


TEST_FUNCTION(32) // Delete Table
{
  model_FigureRef figure(find_named_object_in_list(mview->figures(), "table1"));
  
  ensure("table found", figure.is_valid());

  double w,h;
  w= figure->width();
  h= figure->height();
  
  // delete the figure
  wb.add_response_for_confirm_dialog(1);
  std::map<std::string,bool> opt;
  wbui->get_wb()->delete_object(figure, opt);
  check_only_one_undo_added();

  ensure("table delete", !find_named_object_in_list(mview->figures(), "table1").is_valid());
  ensure("table delete", !find_named_object_in_list(mview->rootLayer()->figures(), "table1").is_valid());

  check_undo();
  ensure("table delete undo", find_named_object_in_list(mview->figures(), "table1").is_valid());
  ensure("table delete undo", find_named_object_in_list(mview->rootLayer()->figures(), "table1").is_valid());

  check_redo();
  ensure("table delete redo", !find_named_object_in_list(mview->figures(), "table1").is_valid());
  ensure("table delete redo", !find_named_object_in_list(mview->rootLayer()->figures(), "table1").is_valid());

  check_undo();
}


TEST_FUNCTION(34) // Delete layer with stuff inside
{
  fail("not implemented");
}



TEST_FUNCTION(36) // Place with Drag/Drop
{
  db_TableRef table(find_named_object_in_list(wb.get_pmodel()->catalog()->schemata()[0]->tables(), "table3"));

  ensure("table found", table.is_valid());

  std::list<GrtObjectRef> list;
  list.push_back(table);
  view->perform_drop(10, 10, WB_DBOBJECT_DRAG_TYPE, list);
  check_only_one_undo_added();

  ensure("table figure added", find_named_object_in_list(mview->figures(), "table3").is_valid());
  model_FigureRef figure(find_named_object_in_list(mview->figures(), "table3"));
  ensure("figure has proper layer set", figure->layer().is_valid());
  ensure("figure has proper layer set", figure->layer() == mview->rootLayer());
  ensure("figure has proper owner set", figure->owner() == mview);

  check_undo();
  ensure("table figure added undo", !find_named_object_in_list(mview->figures(), "table3").is_valid());

  check_redo();
  ensure("table figure added", find_named_object_in_list(mview->figures(), "table3").is_valid());

  check_undo();
}


TEST_FUNCTION(38) // Place with Drag/Drop on a layer
{
  db_TableRef table(find_named_object_in_list(wb.get_pmodel()->catalog()->schemata()[0]->tables(), "table3"));

  ensure("table found", table.is_valid());

  std::list<GrtObjectRef> list;
  list.push_back(table);
  view->perform_drop(200, 500, WB_DBOBJECT_DRAG_TYPE, list);
  check_only_one_undo_added();
  
  model_LayerRef layer(mview->layers()[0]);
  model_FigureRef figure;
  
  figure= find_named_object_in_list(mview->figures(), "table3");
  
  ensure("table figure added", figure.is_valid());
  ensure("table in layer", figure->layer() == layer);
  ensure("layer contains table", layer->figures().get_index(figure) != BaseListRef::npos);

  check_undo();
  ensure("table figure added undo", !find_named_object_in_list(mview->figures(), "table3").is_valid());
  //not needed ensure("table not in layer", !figure->layer().is_valid());
  ensure("layer not contains table", layer->figures().get_index(figure) == BaseListRef::npos);

  check_redo();
  ensure("table figure added", find_named_object_in_list(mview->figures(), "table3").is_valid());
  ensure("table in layer", figure->layer() == layer);
  ensure("layer contains table", layer->figures().get_index(figure) != BaseListRef::npos);

  check_undo();
}


TEST_FUNCTION(40) // Create Relationship (in diagram)
{
  view->set_tool(WB_TOOL_PREL1n);

  model_FigureRef table1(find_named_object_in_list(mview->figures(), "table1"));
  model_FigureRef table2(find_named_object_in_list(mview->figures(), "table2"));

  ensure("found tables", table1.is_valid() && table2.is_valid());
  ensure_equals("rel count", mview->connections().count(), 1U);
  
  // click table1
  view->handle_mouse_button(mdc::ButtonLeft, true, *table1->left()+20, *table1->top()+20, (mdc::EventState)0);
  // click table2
  view->handle_mouse_button(mdc::ButtonLeft, true, *table2->left()+20, *table2->top()+20, (mdc::EventState)0);

  check_only_one_undo_added();

  ensure_equals("rel count", mview->connections().count(), 2U);

  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);

  check_redo();
  ensure_equals("rel count after redo", mview->connections().count(), 2U);
  
  check_undo();
}


TEST_FUNCTION(42) // Create Relationship (indirectly with FK)
{
  db_TableRef table1(find_named_object_in_list(wb.get_pmodel()->catalog()->schemata()[0]->tables(), "table1"));
  db_TableRef table2(find_named_object_in_list(wb.get_pmodel()->catalog()->schemata()[0]->tables(), "table2"));

  ensure("table valid", table1.is_valid() && table2.is_valid());
  ensure_equals("rel count", mview->connections().count(), 1U);
  
  db_ForeignKeyRef fk;
  
  fk= bec::TableHelper::create_foreign_key_to_table(table1, table2,
                                                    true, true,
                                                    true, false,
                                                    wb.get_rdbms(),
                                                    DictRef(wb.grt),
                                                    DictRef(wb.grt));
  check_only_one_undo_added();

  ensure_equals("rel count", mview->connections().count(), 2U);

  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);

  check_redo();
  ensure_equals("rel count after redo", mview->connections().count(), 2U);
  
  check_undo();
}


TEST_FUNCTION(44) // Create Relationship (dropping table with FK)
{
  db_TableRef table(find_named_object_in_list(wb.get_pmodel()->catalog()->schemata()[0]->tables(), "table_with_fk"));

  ensure("table found", table.is_valid());
  ensure_equals("rel count", mview->connections().count(), 1U);

  std::list<GrtObjectRef> list;
  list.push_back(table);
  view->perform_drop(200, 500, WB_DBOBJECT_DRAG_TYPE, list);
  check_only_one_undo_added();

  model_FigureRef figure;
  
  figure= find_named_object_in_list(mview->figures(), "table_with_fk");

  ensure("table figure added", figure.is_valid());
  ensure_equals("rel count", mview->connections().count(), 2U);

  check_undo();
  ensure("table figure added undo", !find_named_object_in_list(mview->figures(), "table_with_fk").is_valid());
  ensure_equals("rel count", mview->connections().count(), 1U);

  check_redo();
  ensure("table figure added redo", find_named_object_in_list(mview->figures(), "table_with_fk").is_valid());
  ensure_equals("rel count", mview->connections().count(), 2U);

  check_undo();
}


TEST_FUNCTION(46) // Create Relationship (dropping table referenced by FK)
{
  ensure_equals("rel count", mview->connections().count(), 1U);
  ensure("table not yet in canvas", !find_named_object_in_list(mview->figures(), "table_with_fk").is_valid());

  // now drag/drop the table back to diagram
  std::list<GrtObjectRef> list;
  list.push_back(find_named_object_in_list(wb.get_pmodel()->catalog()->schemata()[0]->tables(), "table_with_fk"));
  view->perform_drop(10, 10, WB_DBOBJECT_DRAG_TYPE, list);
  check_only_one_undo_added();

  ensure("table in canvas", find_named_object_in_list(mview->figures(), "table_with_fk").is_valid());
  ensure_equals("rel count", mview->connections().count(), 2U);
  
  check_undo();
  ensure_equals("figure count", mview->figures().count(), 5U);
  ensure_equals("rel count after undo", mview->connections().count(), 1U);
  
  check_redo();
  ensure_equals("figure count", mview->figures().count(), 6U);
  ensure_equals("rel count", mview->connections().count(), 2U);
  
  check_undo();
}


TEST_FUNCTION(48) // Delete Relationship (in diagram)
{
  ensure_equals("rel count", mview->connections().count(), 1U);

  model_ConnectionRef conn(mview->connections()[0]);

  mview->unselectAll();

  mview->selectObject(conn);
  ensure_equals("selection", mview->selection().count(), 1U);

  wb.add_response_for_confirm_dialog(1);
  view->delete_selection(true);
  check_only_one_undo_added();

  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);
  
  check_redo();
  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
}


TEST_FUNCTION(50) // Delete Relationship (indirectly with FK)
{
  db_TableRef table2(find_named_object_in_list(wb.get_pmodel()->catalog()->schemata()[0]->tables(), "table2"));

  ensure("table valid", table2.is_valid());
  ensure_equals("rel count", mview->connections().count(), 1U);
  ensure_equals("fk count", table2->foreignKeys().count(), 1U);

  db_ColumnRef column(table2->columns()[1]);
  
  table2->removeColumn(column);
  check_only_one_undo_added();
  
  ensure_equals("fk count", table2->foreignKeys().count(), 0U);
  ensure_equals("rel count", mview->connections().count(), 0U);

  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);
  ensure_equals("fk count", table2->foreignKeys().count(), 1U);

  check_redo();
  ensure_equals("rel count after redo", mview->connections().count(), 0U);
  
  check_undo();
}


TEST_FUNCTION(52) // Delete Relationship (deleted table)
{
  ensure_equals("rel count", mview->connections().count(), 1U);

  model_ConnectionRef conn(mview->connections()[0]);

  mview->unselectAll();

  mview->selectObject(find_named_object_in_list(mview->figures(), "table2"));
  ensure_equals("selection", mview->selection().count(), 1U);

  wb.add_response_for_confirm_dialog(1);
  view->delete_selection(false);
  check_only_one_undo_added();

  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);
  
  check_redo();
  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
}



TEST_FUNCTION(54) // Delete Relationship (deleted table figure only)
{
  ensure_equals("rel count", mview->connections().count(), 1U);

  model_ConnectionRef conn(mview->connections()[0]);

  mview->unselectAll();

  mview->selectObject(find_named_object_in_list(mview->figures(), "table2"));
  ensure_equals("selection", mview->selection().count(), 1U);

  // Keep db objects
  wb.add_response_for_confirm_dialog(0);
  view->delete_selection(false);
  check_only_one_undo_added();

  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);
  
  check_redo();
  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
}


TEST_FUNCTION(56) // Delete Relationship (deleted referenced table)
{
  ensure_equals("rel count", mview->connections().count(), 1U);

  model_ConnectionRef conn(mview->connections()[0]);

  mview->unselectAll();

  mview->selectObject(find_named_object_in_list(mview->figures(), "table1"));
  ensure_equals("selection", mview->selection().count(), 1U);

  view->delete_selection(true);
  check_only_one_undo_added();

  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);
  
  check_redo();
  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
}


TEST_FUNCTION(58) // Delete Relationship and Ref Table
{
  ensure_equals("rel count", mview->connections().count(), 1U);

  model_ConnectionRef conn(mview->connections()[0]);

  mview->unselectAll();

  mview->selectObject(conn->endFigure());
  mview->selectObject(conn);
  ensure_equals("selection", mview->selection().count(), 2U);

  view->delete_selection(true);
  check_only_one_undo_added();

  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);
  
  check_redo();
  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
}


TEST_FUNCTION(60) // Delete Relationship and Table
{
  ensure_equals("rel count", mview->connections().count(), 1U);

  model_ConnectionRef conn(mview->connections()[0]);

  mview->unselectAll();

  mview->selectObject(conn);
  mview->selectObject(conn->startFigure());
  ensure_equals("selection", mview->selection().count(), 2U);

  wb.add_response_for_confirm_dialog(1);
  view->delete_selection(true);
  check_only_one_undo_added();

  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);
  
  check_redo();
  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
}


TEST_FUNCTION(62) // Delete Relationship and both Tables
{
  ensure_equals("rel count", mview->connections().count(), 1U);

  model_ConnectionRef conn(mview->connections()[0]);

  mview->unselectAll();

  mview->selectObject(conn->startFigure());
  mview->selectObject(conn);
  mview->selectObject(conn->endFigure());
  ensure_equals("selection", mview->selection().count(), 3U);

  wb.add_response_for_confirm_dialog(1);
  view->delete_selection(true);
  check_only_one_undo_added();

  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
  ensure_equals("rel count after undo", mview->connections().count(), 1U);
  
  check_redo();
  ensure_equals("rel count", mview->connections().count(), 0U);
  
  check_undo();
}


END_TESTS
