/* 
 * © 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 "grtdb/db_object_helpers.h"

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

#include "grtpp.h"

#include "diff/diffchange.h"
#include "diff_dbobjectmatch.h"

//#define _DB_OBJECT_MATCH_DEBUG_

using namespace grt;


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;
} 

static std::string get_qualified_schema_object_name(GrtNamedObjectRef object)
{
  char *name_up= g_utf8_strup(
    object->name().c_str(), 
    g_utf8_strlen(object->name().c_str(), -1));

  char *parent_up= g_utf8_strup(
    object->owner()->name().c_str(), 
    g_utf8_strlen(object->owner()->name().c_str(), -1));

  std::string s("`");
  s.append(parent_up).append("`.`").append(name_up).append("`");

  g_free(name_up);
  g_free(parent_up);
  return s;
}

static std::string get_qualified_schema_object_old_name(GrtNamedObjectRef object)
{
  char *old_name_up= g_utf8_strup(
    object->oldName().c_str(), 
    g_utf8_strlen(object->oldName().c_str(), -1));

  const char *parent_name= NULL;
  if(GrtNamedObjectRef::can_wrap(object->owner()))
  {
    GrtNamedObjectRef pr= GrtNamedObjectRef::cast_from(object->owner());

    parent_name= pr->oldName().empty() ? 
      pr->name().c_str() : pr->oldName().c_str();
  }
  else
  {
    parent_name=
      object->owner()->name().c_str();
  }

  char *parent_up= g_utf8_strup(
    parent_name, g_utf8_strlen(parent_name, -1));

  std::string s("`");
  s.append(parent_up).append("`.`").append(old_name_up).append("`");

  g_free(old_name_up);
  g_free(parent_up);
  return s;
}

bool grt::DbObjectMatchRecreateOmf::less(const grt::ValueRef& l, const grt::ValueRef& r) const
{
  if (l.type() == r.type() && l.type() == ObjectType)
  {
    if(db_IndexColumnRef::can_wrap(l) && db_IndexColumnRef::can_wrap(r))
    {
      db_IndexColumnRef lc= db_IndexColumnRef::cast_from(l);
      db_IndexColumnRef rc= db_IndexColumnRef::cast_from(r);
      return less(lc->referencedColumn(), rc->referencedColumn());
    }
    else if (db_mysql_RoutineParamRef::can_wrap(l) && db_mysql_RoutineParamRef::can_wrap(r))
    {
      GrtObjectRef left = GrtObjectRef::cast_from(l);
      GrtObjectRef right = GrtObjectRef::cast_from(r);

      if (left.valueptr() && right.valueptr())
      {
        return (strcmp(left->name().c_str(), right->name().c_str()) < 0);
      }
    }
    else if (GrtNamedObjectRef::can_wrap(l) && GrtNamedObjectRef::can_wrap(r))
    {
      GrtNamedObjectRef left = GrtNamedObjectRef::cast_from(l);
      GrtNamedObjectRef right = GrtNamedObjectRef::cast_from(r);

      if (left.valueptr() && right.valueptr() 
        && left.valueptr() && right.valueptr())
      {
        size_t l_len= strlen(left->oldName().c_str());
        size_t r_len= strlen(right->oldName().c_str());

        if((l_len == 0) && (r_len == 0))
          return left.id() < right.id();
        if(l_len == 0)
          return true;
        if(r_len == 0)
          return false;

        return get_qualified_schema_object_old_name(left) < get_qualified_schema_object_old_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.class_name() == right.class_name())
        && left.has_member("oldName"))
      {
        size_t l_len= strlen(left.get_string_member("oldName").c_str());
        size_t r_len= strlen(right.get_string_member("oldName").c_str());

        if((l_len == 0) && (r_len == 0))
          return left.id() < right.id();
        if(l_len == 0)
          return true;
        if(r_len == 0)
          return false;

        return left.get_string_member("oldName") < right.get_string_member("oldName");
      }
    }
  }
  return pless(l, r);
}

/////////////////////////////////////////////////////////////////////////////////////////////
// Recreate OMF
/////////////////////////////////////////////////////////////////////////////////////////////

bool grt::DbObjectMatchRecreateOmf::equal(const ValueRef& l, const ValueRef& r) const
{
  if (l.type() == r.type() && l.type() == ObjectType)
  {
    if(db_IndexColumnRef::can_wrap(l) && db_IndexColumnRef::can_wrap(r))
    {
      db_IndexColumnRef lc= db_IndexColumnRef::cast_from(l);
      db_IndexColumnRef rc= db_IndexColumnRef::cast_from(r);
      return equal(lc->referencedColumn(), rc->referencedColumn());
    }
    else if (db_mysql_RoutineParamRef::can_wrap(l) && db_mysql_RoutineParamRef::can_wrap(r))
    {
      GrtObjectRef left = GrtObjectRef::cast_from(l);
      GrtObjectRef right = GrtObjectRef::cast_from(r);

      if (left.valueptr() && right.valueptr())
      {
        return (strcmp(left->name().c_str(), right->name().c_str()) == 0);
      }
    }
    else if (GrtNamedObjectRef::can_wrap(l) && GrtNamedObjectRef::can_wrap(r))
    {
      GrtNamedObjectRef left = GrtNamedObjectRef::cast_from(l);
      GrtNamedObjectRef right = GrtNamedObjectRef::cast_from(r);
      
      if (left.valueptr() && right.valueptr() 
        && left.valueptr() && right.valueptr())
      {
        if((strlen(left->oldName().c_str()) == 0) || (strlen(right->oldName().c_str()) == 0))
          return false;
        return get_qualified_schema_object_old_name(left) == get_qualified_schema_object_old_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((strlen(left.get_string_member("oldName").c_str()) == 0) && (strlen(right.get_string_member("oldName").c_str()) == 0))
        return false;

      if ((left.class_name() == right.class_name())
        && left.has_member("oldName"))
      {
        return left.get_string_member("oldName") == right.get_string_member("oldName");
      }
    }
  }

  return peq(l, r);
}

/////////////////////////////////////////////////////////////////////////////////////////////
// Alter OMF
/////////////////////////////////////////////////////////////////////////////////////////////

bool grt::DbObjectMatchAlterOmf::less(const grt::ValueRef& l, const grt::ValueRef& r) const
{
  if (l.type() == r.type() && l.type() == ObjectType)
  {
    if(db_IndexColumnRef::can_wrap(l) && db_IndexColumnRef::can_wrap(r))
    {
      db_IndexColumnRef lc= db_IndexColumnRef::cast_from(l);
      db_IndexColumnRef rc= db_IndexColumnRef::cast_from(r);
      return less(lc->referencedColumn(), rc->referencedColumn());
    }
    else if (GrtNamedObjectRef::can_wrap(l) && GrtNamedObjectRef::can_wrap(r))
    {
      GrtNamedObjectRef left = GrtNamedObjectRef::cast_from(l);
      GrtNamedObjectRef right = GrtNamedObjectRef::cast_from(r);

      if (left.valueptr() && right.valueptr())
      {
        std::string l_str, r_str;

        if(strlen(left->oldName().c_str()) > 0)
          l_str= get_qualified_schema_object_old_name(left);
        else
          l_str= get_qualified_schema_object_name(left);

        if(strlen(right->oldName().c_str()) > 0)
          r_str= get_qualified_schema_object_old_name(right);
        else
          r_str= get_qualified_schema_object_name(right);

        int result= l_str.compare(r_str);

#ifdef _DB_OBJECT_MATCH_DEBUG_
        std::cout << std::endl << "DbObjectMatchAlterOmf::less(" << l_str << ", " << r_str << ") = " << result << std::endl;
#endif
        return result < 0;
      } 
    }
    else if (GrtObjectRef::can_wrap(l) && GrtObjectRef::can_wrap(r))
    {
      GrtObjectRef left = GrtObjectRef::cast_from(l);
      GrtObjectRef right = GrtObjectRef::cast_from(r);

      if (left.valueptr() && right.valueptr())
      {
        return (strcmp(left->name().c_str(), right->name().c_str()) < 0);
      }
    }
    else if (ObjectRef::can_wrap(l) && ObjectRef::can_wrap(r))
    {
      ObjectRef left = ObjectRef::cast_from(l);
      ObjectRef right = ObjectRef::cast_from(r);
      if ((left.class_name() == right.class_name())
        && left.has_member("oldName"))
      {
        const char *l_str= NULL;
        const char *r_str= NULL;

        if(strlen(left.get_string_member("oldName").c_str()) > 0)
          l_str= left.get_string_member("oldName").c_str();
        else
          l_str= left.get_string_member("name").c_str();

        if(strlen(right.get_string_member("oldName").c_str()) > 0)
          r_str= right.get_string_member("oldName").c_str();
        else
          r_str= right.get_string_member("name").c_str();

        return strcmp(l_str, r_str) < 0;
      }
    }
  }
  return pless(l, r);
}

bool grt::DbObjectMatchAlterOmf::equal(const ValueRef& l, const ValueRef& r) const
{
  if (l.type() == r.type() && l.type() == ObjectType)
  {
    if(db_IndexColumnRef::can_wrap(l) && db_IndexColumnRef::can_wrap(r))
    {
      db_IndexColumnRef lc= db_IndexColumnRef::cast_from(l);
      db_IndexColumnRef rc= db_IndexColumnRef::cast_from(r);
      return equal(lc->referencedColumn(), rc->referencedColumn());
    }
    else if (GrtNamedObjectRef::can_wrap(l) && GrtNamedObjectRef::can_wrap(r))
    {
      GrtNamedObjectRef left = GrtNamedObjectRef::cast_from(l);
      GrtNamedObjectRef right = GrtNamedObjectRef::cast_from(r);
      
      if (left.valueptr() && right.valueptr())
      {
        std::string l_str, r_str;

        if(strlen(left->oldName().c_str()) > 0)
          l_str= get_qualified_schema_object_old_name(left);
        else
          l_str= get_qualified_schema_object_name(left);

        if(strlen(right->oldName().c_str()) > 0)
          r_str= get_qualified_schema_object_old_name(right);
        else
          r_str= get_qualified_schema_object_name(right);

        int result= l_str.compare(r_str);

#ifdef _DB_OBJECT_MATCH_DEBUG_
        std::cout << std::endl << "DbObjectMatchAlterOmf::equal(" << l_str << ", " << r_str << ") = " << result << std::endl;
#endif
        return result == 0;
      } 
    }
    else if (GrtObjectRef::can_wrap(l) && GrtObjectRef::can_wrap(r))
    {
      GrtObjectRef left = GrtObjectRef::cast_from(l);
      GrtObjectRef right = GrtObjectRef::cast_from(r);

      if (left.valueptr() && right.valueptr())
      {
        return (strcmp(left->name().c_str(), right->name().c_str()) == 0);
      }
    }
    else if (ObjectRef::can_wrap(l) && ObjectRef::can_wrap(r))
    {
      ObjectRef left = ObjectRef::cast_from(l);
      ObjectRef right = ObjectRef::cast_from(r);
      if (left.valueptr() && right.valueptr()
        && (left.class_name() == right.class_name())
        && left.has_member("oldName"))
      {
        ObjectRef left = ObjectRef::cast_from(l);
        ObjectRef right = ObjectRef::cast_from(r);

        const char *l_str= NULL;
        const char *r_str= NULL;

        if(strlen(left.get_string_member("oldName").c_str()) > 0)
          l_str= left.get_string_member("oldName").c_str();
        else
          l_str= left.get_string_member("name").c_str();

        if(strlen(right.get_string_member("oldName").c_str()) > 0)
          r_str= right.get_string_member("oldName").c_str();
        else
          r_str= right.get_string_member("name").c_str();

        return strcmp(l_str, r_str) == 0;
      }
    }
  }

  return peq(l, r);
}
