#include "stdafx.h"

#include <grts/structs.db.h>

#include <grtpp_util.h>
#include <grtpp_undo_manager.h>

#include "grt/common.h"

//================================================================================
// db_Table

static void table_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value, db_Table *table)
{
  if (table->columns().valueptr() == list)
  {
    table->signal_refreshDisplay().emit("column");
  }
  else if (table->indices().valueptr() == list)
  {
    table->signal_refreshDisplay().emit("index");
  }
  else if (table->foreignKeys().valueptr() == list)
  {
    table->signal_refreshDisplay().emit("foreignKey");

    table->signal_foreignKeyChanged().emit(db_ForeignKeyRef::cast_from(value));
  }
}


void db_Table::init()
{
  _list_changed_signal.connect(sigc::bind<db_Table*>(sigc::ptr_fun(table_list_changed), this));
}


db_Table::~db_Table()
{
}


void db_Table::addColumn(const db_ColumnRef &column)
{
  _columns.insert(column);
  
}


db_ForeignKeyRef db_Table::createForeignKey(const std::string &name)
{
  db_ForeignKeyRef fk(get_grt()->create_object<db_ForeignKey>(_foreignKeys->content_type_spec().object_class));

  fk->owner(this);
  fk->name(name);

  _foreignKeys.insert(fk);

  return fk;
}


void db_Table::addIndex(const db_IndexRef &index)
{
  _indices.insert(index);
}


void db_Table::addPrimaryKeyColumn(const db_ColumnRef &column)
{
  db_IndexRef pkindex;

  if (isPrimaryKeyColumn(column))
    return;

  grt::AutoUndo undo(get_grt(), !is_global());

  std::string strname;
  
  pkindex= primaryKey();
  
  if (!pkindex.is_valid())
  {
    grt::MetaClass *meta= get_metaclass();
    
    // primaryKey is not overriden by the db specific structs
    //strname= table.get_member_struct_name("primaryKey");
    //strname= table.get_member_content_struct_name("indices");
    strname= meta->get_member_type("indices").content.object_class;
    pkindex= get_grt()->create_object<db_Index>(strname);
    pkindex->name("PRIMARY");
    pkindex->oldName("PRIMARY");
    pkindex->owner(this);
    pkindex->indexType("PRIMARY");
    
    indices().insert(pkindex);

    pkindex->isPrimary(1);

    primaryKey(pkindex);
  }

  strname= pkindex.get_metaclass()->get_member_type("columns").content.object_class;

  db_IndexColumnRef pkicolumn(get_grt()->create_object<db_IndexColumn>(strname));
  
  pkicolumn->owner(pkindex);
  
  pkicolumn->referencedColumn(column);
  
  column->isNotNull(1);
  
  // hack for handling auto_increment columns in MySQL. these should be the 1st column
  // in the index
  if (column.has_member("autoIncrement") && column.get_integer_member("autoIncrement"))
    pkindex->columns().insert(pkicolumn, 0);
  else
    pkindex->columns().insert(pkicolumn);

  _signal_refreshDisplay.emit("column");

  undo.end(_("Set Primary Key"));
}


grt::IntegerRef db_Table::isDependantTable()
{
  if (primaryKey().is_valid())
  {
    grt::ListRef<db_IndexColumn> columns(primaryKey()->columns());

    for (size_t c= columns.count(), i= 0; i < c; i++)
    {
      if (isForeignKeyColumn(columns.get(i)->referencedColumn()))
        return 1;
    }
  }
  return 0;
}


grt::IntegerRef db_Table::isForeignKeyColumn(const db_ColumnRef &column)
{
  grt::ListRef<db_ForeignKey> fklist(foreignKeys());

  for (size_t c= fklist.count(), i= 0; i < c; i++)
  {
    grt::ListRef<db_Column> columns= fklist[i]->columns();

    for (size_t d= columns.count(), j= 0; j < d; j++)
    {
      if (columns[j] == column)
        return 1;
    }
  }
  return 0;
}


grt::IntegerRef db_Table::isPrimaryKeyColumn(const db_ColumnRef &column)
{
  db_IndexRef pkindex= primaryKey();

  if (!pkindex.is_valid())
    return 0;

  size_t i, c;
  grt::ListRef<db_IndexColumn> column_list= pkindex->columns();
  c= column_list.count();
  for (i= 0; i < c; i++)
  {
    db_IndexColumnRef idxcolumn= column_list[i];

    if (idxcolumn->referencedColumn() == column)
      return 1;
  }

  return 0;
}


void db_Table::removeColumn(const db_ColumnRef &column)
{
  grt::AutoUndo undo(get_grt(), !is_global());

  // make sure it's no longer a PK
  removePrimaryKeyColumn(column);

  // remove column from indices
  grt::ListRef<db_Index> indices= this->indices();
  for (size_t index = 0; index < indices.count(); ++index)
  {
    grt::ListRef<db_IndexColumn> icolumns= indices[index]->columns();
    for (size_t icolumn = 0; icolumn < icolumns.count(); ++icolumn)
    {
      if ((icolumns[icolumn])->referencedColumn() == column)
        icolumns.remove(icolumn);
    }

    // if the index has no columns, we delete it
    if (icolumns.count() == 0)
      indices.remove(index);
  }
 
  // remove column from foreign keys
  grt::ListRef<db_ForeignKey> fks= foreignKeys();
  for (grt::ListRef<db_ForeignKey>::const_reverse_iterator fk= fks.rbegin(); fk != fks.rend(); ++fk)
  {
    grt::ListRef<db_Column> fcolumns((*fk)->columns());
    bool deleted= false;

    for (size_t col_index= 0; col_index < fcolumns.count(); ++col_index)
    {
      if (fcolumns[col_index] == column)
      {
        size_t idx= fcolumns.get_index(fcolumns[col_index]);
        if (idx != grt::BaseListRef::npos)
        {
          (*fk)->columns().remove(idx);
          (*fk)->referencedColumns().remove(idx);
        }
        deleted= true;
      }
    }

    // if the fks has no columns, we delete it
    if (fcolumns.count() == 0 && deleted)
      removeForeignKey(*fk, 0);
  }

  // remove from tables column list
  columns().remove_value(column);

  undo.end(strfmt(_("Remove Column '%s.%s'"), name().c_str(), (*column->name()).c_str()));
}


void db_Table::removeForeignKey(const db_ForeignKeyRef &fk, long removeColumns)
{
  // remove a fk from table and make sure its index is deleted too
  // if delete_columns is true, it will also delete the columns that form the FK

  grt::AutoUndo undo(get_grt(), !is_global());

  foreignKeys().remove_value(fk);

  // Find the index that corresponds to this FK.
  for (int i= indices().count() - 1; i > -1; i--)
  {
    db_IndexRef index(indices()[i]);

    if (*index->indexType() == "FOREIGN" && *index->name() == *fk->name())
    {
      indices().remove_value(index);
      break;
    }
  }
  if (removeColumns)
  {
    db_ColumnRef cl;
    for (int i= fk->columns().count() - 1; i > -1; i--)
    {
      cl = fk->columns().get(i);
      removeColumn(cl);
    }
  }

  undo.end(_("Remove Foreign Key"));
}


void db_Table::removeIndex(const db_IndexRef &index)
{
  throw std::logic_error("not implement");
  //QQQ
}


void db_Table::removePrimaryKeyColumn(const db_ColumnRef &column)
{
  db_IndexRef pkindex;

  if (!isPrimaryKeyColumn(column))
    return;

  grt::AutoUndo undo(get_grt(), !is_global());
  
  pkindex= primaryKey();
  
  if (pkindex.is_valid())
  {
    grt::ListRef<db_IndexColumn> pkColumns(pkindex->columns());
    
    for (int i= pkColumns.count() - 1; i > -1; i--)
    {
      db_ColumnRef pkcolumn(pkColumns.get(i)->referencedColumn());
      
      if (pkcolumn == column)
      {        
        pkColumns.remove(i);
        break;
      }
    }
    if (pkColumns.count() == 0)
    {
      // remove primary index
      indices().remove_value(pkindex);

      primaryKey(db_IndexRef()); // set to nil
    }
  }

  undo.end(_("Unset Primary Key"));

  _signal_refreshDisplay.emit("column");
}





