/* 
 * Copyright (c) 2009, 2010, 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 "stdafx.h"

#include "workbench/wb_context.h"
#include "wb_user_datatypes.h"
#include "grtdb/db_object_helpers.h"
#include "base/string_utilities.h"

using namespace wb;
using namespace base;

UserDatatypeListBE::UserDatatypeListBE(WBContext *wb)
: _wb(wb)
{
}


void UserDatatypeListBE::set_rdbms(const db_mgmt_RdbmsRef &rdbms)
{
  _rdbms= rdbms;
}


void UserDatatypeListBE::set_catalog(const db_CatalogRef &catalog)
{
  _catalog= catalog;
}


int UserDatatypeListBE::count()
{
  if (_catalog.is_valid() && _catalog->userDatatypes().is_valid())
    return _catalog->userDatatypes().count();//+1;
  return 0;
}


bool UserDatatypeListBE::is_editable(const bec::NodeId &node) const
{
  if (node[0] >= (int)_catalog->userDatatypes().count())
    return true;
  
  if (g_str_has_prefix(_catalog->userDatatypes()[node[0]].id().c_str(), "com.mysql."))
    return false;
  return true;
}


bool UserDatatypeListBE::delete_node(const bec::NodeId &node)
{
  if (node[0] >= (int)_catalog->userDatatypes().count())
    return false;

  db_UserDatatypeRef type(_catalog->userDatatypes()[node[0]]);
  bool found= false;

  // check if the type is used by any columns
  for (size_t c= _catalog->schemata().count(), i= 0; i < c && !found; i++)
  {
    grt::ListRef<db_Table> tables(_catalog->schemata()[i]->tables());

    for (size_t d= tables.count(), j= 0; j < d && !found; j++)
    {
      grt::ListRef<db_Column> columns(tables[j]->columns());

      for (size_t e= columns.count(), k= 0; k < e; k++)
      {
        if (columns[k]->userType() == type)
        {
          found= true;
          break;
        }
      }
    }
  }

  if (found)
  {
    mforms::Utilities::show_error(_("Delete User Type"), strfmt(_("Cannot delete type '%s' because one or more tables are using it."), 
      type->name().c_str()), _("Close"));
    return false;
  }
  
  grt::AutoUndo undo(_wb->get_grt());
  
  _catalog->userDatatypes().remove(node[0]);

  undo.end(strfmt(_("Remove user datatype '%s'"), type->name().c_str()));

  return true;
}


bool UserDatatypeListBE::get_field(const bec::NodeId &node, int column, std::string &value)
{
  if (node[0] >= (int)_catalog->userDatatypes().count())
  {
    value= "";
    return true;
  }

  switch ((Columns)column)
  {
  case Name:
    value= _catalog->userDatatypes()[node[0]]->name();
    return true;
  case Definition:
    value= _catalog->userDatatypes()[node[0]]->sqlDefinition();
    return true;
  case Flags:
    value= _catalog->userDatatypes()[node[0]]->flags();
    return true;
  }
  return false;
}


void UserDatatypeListBE::refresh()
{
}


// for editable lists only
grt::Type UserDatatypeListBE::get_field_type(const bec::NodeId &node, int column)
{
  return grt::StringType;
}


grt::ValueRef UserDatatypeListBE::get_grt_value(const bec::NodeId &node, int column)
{
  if (node[0] >= (int)_catalog->userDatatypes().count())
    return grt::ValueRef();

  return _catalog->userDatatypes()[node[0]];
}


bool UserDatatypeListBE::set_field(const bec::NodeId &node, int column, const std::string &value)
{
  db_UserDatatypeRef udata;

  return false; // disable editing, should be done from the standalone editor
  
  if (node[0] >= (int)_catalog->userDatatypes().count())
  {
    udata= db_UserDatatypeRef(_catalog->get_grt());

    if (column == Name)
    {
      udata->owner(_catalog);
      udata->name(value);

      grt::AutoUndo undo(_wb->get_grt());
      _catalog->userDatatypes().insert(udata);
      undo.end(_("Add New User Type"));
    }
    else
      return false;
  }
  else
    udata= _catalog->userDatatypes()[node[0]];

  switch ((Columns)column)
  {
  case Name:
    {
      grt::AutoUndo undo(_wb->get_grt());
      udata->name(value);
      undo.end(_("Change User Type Name"));
    }
    return true;
  case Definition:
    {
      std::string::size_type pos= value.find('(');
      std::string type= value;

      if (pos != std::string::npos)
        type= value.substr(0, pos);
      pos= type.find(' ');
      if (pos != std::string::npos)
        type= type.substr(0, pos);

      db_SimpleDatatypeRef simpletype(bec::CatalogHelper::get_datatype(_rdbms->simpleDatatypes(), type));

      if (simpletype.is_valid())
      {
        grt::AutoUndo undo(_wb->get_grt());
        udata->sqlDefinition(value);
        udata->actualType(simpletype);
        undo.end(_("Change User Type Definition"));
        return true;
      }
    }
    break;
  case Flags:
    if (udata->actualType().is_valid())
    {
      gchar **tokens= g_strsplit(value.c_str(), " ", 0);
      std::string flags;
      if (tokens)
      {
        for (size_t i= 0; tokens[i]!=NULL; i++)
        {
          for (size_t c= udata->actualType()->flags().count(), f= 0; f < c; f++)
          {
            if (strcasecmp(udata->actualType()->flags().get(f).c_str(), tokens[i])==0)
            {
              if (!flags.empty())
                flags.append(" ");
              flags.append(udata->actualType()->flags().get(f));
              break;
            }
          }
        }
      }
      grt::AutoUndo undo(_wb->get_grt());
      udata->flags(flags);
      undo.end(_("Change User Type Flags"));
      g_strfreev(tokens);
      return true;
    }
    return false;
  }
  return false;
}


static struct TypeIcon {
  const char *group;
  const char *icon;
} type_icons[]= {
  {"numeric", "db.DatatypeGroup.numeric.16x16.png"},
  {"string", "db.DatatypeGroup.text.16x16.png"},
  {"text", "db.DatatypeGroup.text.16x16.png"},
  {"blob", "db.DatatypeGroup.blob.16x16.png"},
  {"datetime", "db.DatatypeGroup.datetime.16x16.png"},
  {"gis", "db.DatatypeGroup.geo.16x16.png"},
  {"various", "db.DatatypeGroup.userdefined.16x16.png"},
  {"userdefined", "db.DatatypeGroup.userdefined.16x16.png"},
  {"structured", "db.DatatypeGroup.userdefined.16x16.png"},
  {NULL, NULL}
};

bec::IconId UserDatatypeListBE::get_field_icon(const bec::NodeId &node, int column, bec::IconSize size)
{
  if (node[0] >= (int)_catalog->userDatatypes().count())
    return 0;

  db_UserDatatypeRef utype(_catalog->userDatatypes()[node[0]]);

  if (utype->actualType().is_valid() && utype->actualType()->group().is_valid())
  {
    const char *group= utype->actualType()->group()->name().c_str();

    for (size_t i= 0; type_icons[i].group; i++)
    {
      if (strcmp(type_icons[i].group, group)==0)
        return bec::IconManager::get_instance()->get_icon_id(type_icons[i].icon, size);
    }
    return bec::IconManager::get_instance()->get_icon_id("db.DatatypeGroup.userdefined.16x16.png", size);
  }

  return 0;
}
