
#include "stdafx.h"

#ifndef _WIN32
#include <sstream>
#include <fstream>
#endif

#include "test.h"
#include "testgrt.h"
#include "grt_test_utility.h"
#include "grts/structs.db.mgmt.h"
#include "grts/structs.db.mysql.h"
#include "grts/structs.workbench.h"
#include "grts/structs.workbench.physical.h"
//#include "wbvalidationmysql.h"
#include "grt/grt_manager.h"
#include "wb_helpers.h"

#include "..\..\..\library\grt\src\diff\changelistobjects.h"

using namespace std;

BEGIN_TEST_DATA_CLASS(test_grtdifflistactions)
public:
  GRTManagerTest grtm;
  db_mgmt_RdbmsRef rdbms;
  GRT *grt;
END_TEST_DATA_CLASS

TEST_MODULE(test_grtdifflistactions, "test_grtdifflistactions");

std::string gen_move_alter(GrtListDifference &actions, std::vector<std::string> &res)
{
  GrtListDifference::MoveActionSet &moved= actions.moved;

  GrtListDifference::MoveActionSet::iterator iter= moved.begin();
  for(; iter != moved.end(); ++iter)
  {
    int x= actions.apply_moved(*iter).second;
    ObjectRef v= ObjectRef::cast_from(*actions.get_id(x - 1));
    ObjectRef other= ObjectRef::cast_from(iter->second.first);

    if (v.is_valid() && v.type() == grt::ObjectType && v->has_member("name")
      && other.type() == grt::ObjectType  && other.has_member("name"))
    {
      std::string after= v.get_string_member("name");
      std::string curr= other.get_string_member("name");
      res.push_back(curr);      
      res.push_back(after);
    }
  }
  return "";
}

std::string gen_insert_alter(GrtListDifference &actions, std::vector<std::string> &res)
{
  GrtListDifference::AddActionSet &added= actions.added;

  GrtListDifference::AddActionSet::iterator iter= added.begin();
  for(; iter != added.end(); ++iter)
  {
    int x= actions.apply_added(*iter);
    ObjectRef v= ObjectRef::cast_from(*actions.get_id(x-1));
    ObjectRef other= ObjectRef::cast_from(iter->first);

    if (v.is_valid() && v.type() == grt::ObjectType && v.has_member("name")
      && other.type() == grt::ObjectType  && other.has_member("name"))
    {
      std::string after= v.get_string_member("name");
      std::string curr= other.get_string_member("name");
      res.push_back(curr);      
      res.push_back(after);
    }
  }

  return "";
}

static std::string get_qualified_schema_object_name(GrtNamedObjectRef object)
{
  static std::string backtick= "`";
  static std::string double_backtick= "`.`";

  std::string object_name= object->name();
  std::string owner_name= "";
  if (object->owner().is_valid())
  {
    owner_name= object->owner()->name();
    std::string owner_owner_name= object->owner()->owner()->name();
    if ((object->static_class_name() == "db.Trigger") && object->owner()->owner().is_valid())
      return backtick + owner_owner_name + double_backtick + object_name + backtick;
    else 
      if ((object->static_class_name() == "db.Index") && object->owner()->owner().is_valid())
        return backtick + owner_owner_name + double_backtick + owner_name + double_backtick + object_name + backtick;
      else
        owner_name = backtick + owner_name + "`.";
  }

  return owner_name + backtick + object_name + backtick;
}

static bool pless(const grt::ValueRef l, const grt::ValueRef r)
{
  return l < r;
}

static bool peq(const grt::ValueRef l, const grt::ValueRef r)
{
  return l == r;
} 

bool grt_named_less(const ValueRef& l, const ValueRef& r)
{
  if (l.type() == r.type() && l.type() == grt::ObjectType)
  {
    if (GrtNamedObjectRef::can_wrap(l) && GrtNamedObjectRef::can_wrap(r))
    {
      GrtNamedObjectRef left = GrtNamedObjectRef::cast_from(l);
      GrtNamedObjectRef right = GrtNamedObjectRef::cast_from(r);

      if (left.is_valid() && right.is_valid())
        return get_qualified_schema_object_name(left) < get_qualified_schema_object_name(right);
    }
    else 
      if (ObjectRef::can_wrap(l) && ObjectRef::can_wrap(r))
      {
        ObjectRef left = ObjectRef::cast_from(l);
        ObjectRef right = ObjectRef::cast_from(r);
        if ((left.get_metaclass() == right.get_metaclass()) && left.has_member("name"))
          return left.get_string_member("name") < right.get_string_member("name");
      }
  }

  return pless(l, r);
}

bool grt_named_equal(const ValueRef& l, const ValueRef& r)
{
  if (l.type() == r.type() && l.type() == grt::ObjectType)
  {
    if (GrtNamedObjectRef::can_wrap(l) && GrtNamedObjectRef::can_wrap(r))
    {
      GrtNamedObjectRef left = GrtNamedObjectRef::cast_from(l);
      GrtNamedObjectRef right = GrtNamedObjectRef::cast_from(r);
      
      if (left.is_valid() && right.is_valid())
        return get_qualified_schema_object_name(left) == get_qualified_schema_object_name(right);
    }
    else 
      if (ObjectRef::can_wrap(l) && ObjectRef::can_wrap(r))
      {
        ObjectRef left = ObjectRef::cast_from(l);
        ObjectRef right = ObjectRef::cast_from(r);
        if ((left.get_metaclass() == right.get_metaclass()) && left.has_member("name"))
          return left.get_string_member("name") == right.get_string_member("name");
      }
  }

  return peq(l, r);
}

bool named_less(const ValueRef &l, const ValueRef &r)
{
  return grt_named_less(l, r);
}

bool named_equal(const ValueRef &l, const ValueRef &r)
{
  return grt_named_equal(l, r);
}

TEST_FUNCTION(1)
{
  db_mgmt_ManagementRef mgmt(grtm.get_grt()); 
  grt= grtm.get_grt();

  ListRef<db_DatatypeGroup> grouplist= 
    ListRef<db_DatatypeGroup>::cast_from(grtm.get_grt()->unserialize("../../res/grtdata/db_datatype_groups.xml"));

  // unfortunately we have to do these lines to make sure that references (links) will be resolved during import
  ValueRef root= grt->root();
  assure(root.type() == ::grt::DictType);
  DictRef::cast_from(root).set("grouplist", grouplist);

  rdbms= db_mgmt_RdbmsRef::cast_from(grt->unserialize("../../modules/db.mysql/res/mysql_rdbms_info.xml"));
  ensure("db_mgmt_RdbmsRef initialization", rdbms.is_valid());

  db_DatatypeGroupRef group= rdbms->simpleDatatypes().get(0)->group();
}


TEST_FUNCTION(2)
{
  db_ColumnRef s0(grt);
  s0->name("s0");
  db_ColumnRef s1(grt);
  s1->name("s1");
  db_ColumnRef s2(grt);
  s2->name("s2");
  db_ColumnRef s3(grt);
  s3->name("s3");
  db_ColumnRef s4(grt);
  s4->name("s4");
  
  ListRef<db_Column> list(grt);
  list.insert(s0);
  list.insert(s1);
  list.insert(s4);

  ListRef<db_Column> list_after(grt);
  list_after.insert(s0);
  list_after.insert(s2); // added
  list_after.insert(s3); // added
  list_after.insert(s4);
  list_after.insert(s1); // moved

  GrtListDifference actions;

  actions.parse_structure(list->raw_begin(), list->raw_end(), list_after->raw_begin(), list_after->raw_end(), 
    named_equal, named_less);

  std::vector<std::string> moved;
  gen_move_alter(actions, moved);
  std::vector<std::string> inserted;
  gen_insert_alter(actions, inserted);

  std::string mres[]= {"s1", "s4"};
  std::string ires[]= {"s2", "s0", "s3", "s2"};

  assure_equal(moved.size(), UPPER_BOUND(mres));
  assure_equal(inserted.size(), UPPER_BOUND(ires));

  try
  {
    assure_containers_equal(moved.begin(), moved.end(), mres, mres + UPPER_BOUND(mres));
  }
  catch(...)
  {
    std::vector<std::string>::iterator iter= moved.begin();
    std::cout << mres[0] << " - " << *iter << std::endl;
    assure_equal(mres[0], *iter++);
    std::cout << mres[1] << " - " << *iter << std::endl;
    assure_equal(mres[1], *iter++);
    throw;
  }
  try
  {
    assure_containers_equal(inserted.begin(), inserted.end(), ires, ires + UPPER_BOUND(ires));
  }
  catch(...)
  {
    std::vector<std::string>::iterator iter= inserted.begin();
    std::cout << ires[0] << " - " << *iter << std::endl;
    assure_equal(ires[0], *iter++);
    assure_equal(ires[1], *iter++);
    assure_equal(ires[2], *iter++);
    assure_equal(ires[3], *iter++);
    throw;  
  }
}

END_TESTS