/* Copyright (C) 2014 InfiniDB, Inc.

   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 Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/***********************************************************************
 *   $Id: tabledef.cpp 9210 2013-01-21 14:10:42Z rdempsey $
 *
 *
 ***********************************************************************/

#include <iostream>
#define DDLPKG_DLLEXPORT
#include "ddlpkg.h"
#undef DDLPKG_DLLEXPORT

namespace ddlpackage
{
using namespace std;

TableDef::~TableDef()
{
  for (auto* column : fColumns)
  {
    delete column;
  }

  for (auto* constraint : fConstraints)
  {
    delete constraint;
  }

  delete fQualifiedName;
}

TableDef::TableDef(QualifiedName* name, TableElementList* elements, TableOptionMap* options)
 : fQualifiedName(name)
{
  if (options)
  {
    fOptions = *options;
    delete options;
  }

  ColumnDef* column;
  TableConstraintDef* constraint;

  /* When parsing, it is necessary to collect ColumnDefs and
     TableConstraintDefs as TableElements.  Here we separate
     them out into separately typed lists.
   */
  TableElementList::iterator itr;

  for (itr = elements->begin(); itr != elements->end(); ++itr)
  {
    column = dynamic_cast<ColumnDef*>(*itr);

    if (column)
    {
      fColumns.push_back(column);
    }
    else
    {
      constraint = dynamic_cast<TableConstraintDef*>(*itr);

      if (constraint)
      {
        fConstraints.push_back(constraint);
      }
    }
  }

  delete elements;
}

/** \brief Put to ostream. */
ostream& operator<<(ostream& os, const TableDef& tableDef)
{
  os << "CreateTable ";

  if (tableDef.fQualifiedName->fSchema != "")
    // cout << tableDef.fQualifiedName->fSchema << ".";
    os << tableDef.fQualifiedName->fName << " " << tableDef.fConstraints.size() << " table constraints"
       << endl;

  {
    ColumnDefList::const_iterator itr;

    for (itr = tableDef.fColumns.begin(); itr != tableDef.fColumns.end(); ++itr)
    {
      ColumnDef* col = *itr;
      os << *col << endl;
    }
  }

  {
    TableConstraintDefList::const_iterator itr;

    for (itr = tableDef.fConstraints.begin(); itr != tableDef.fConstraints.end(); ++itr)
    {
      os << (**itr);
    }
  }

  pair<string, string> oval;
  TableOptionMap::const_iterator oitr;
  os << "Table Options" << endl;

  if (!tableDef.fOptions.empty())
  {
    TableOptionMap::const_iterator oitr;

    for (oitr = tableDef.fOptions.begin(); oitr != tableDef.fOptions.end(); ++oitr)
    {
      oval = *oitr;
      os << "   " << oval.first << "=" << oval.second << endl;
    }
  }

  return os;
}

ostream& operator<<(ostream& os, const TableConstraintDef& constraint)
{
  return constraint.put(os);
}

std::ostream& TableConstraintDef::put(std::ostream& os) const
{
  os << "No!!!" << endl;

  return os;
}

TableConstraintDef::TableConstraintDef(DDL_CONSTRAINTS cType) : fConstraintType(cType)
{
}

TableConstraintDef::TableConstraintDef() : fConstraintType(DDL_INVALID_CONSTRAINT)
{
}

TableCheckConstraintDef::TableCheckConstraintDef(const char* check)
 : TableConstraintDef(DDL_CHECK), fCheck(check)
{
}
std::ostream& TableCheckConstraintDef::put(std::ostream& os) const
{
  os << "Constraint: " << ConstraintString[fConstraintType] << " ";
  os << "\"" << fCheck << "\"";
  os << endl;

  return os;
}

TableUniqueConstraintDef::TableUniqueConstraintDef(ColumnNameList* columns)
 : TableConstraintDef(DDL_UNIQUE), fColumnNameList(*columns)
{
  delete columns;
}
std::ostream& TableUniqueConstraintDef::put(std::ostream& os) const
{
  os << "Constraint: " << fName << " " << ConstraintString[fConstraintType] << " ";

  ColumnNameList::const_iterator itr;
  os << "(";

  for (itr = fColumnNameList.begin(); itr != fColumnNameList.end(); ++itr)
  {
    os << *itr << " ";
  }

  os << ")";
  return os;
}

TablePrimaryKeyConstraintDef::TablePrimaryKeyConstraintDef(ColumnNameList* columns)
 : TableConstraintDef(DDL_PRIMARY_KEY), fColumnNameList(*columns)
{
  delete columns;
}
std::ostream& TablePrimaryKeyConstraintDef::put(std::ostream& os) const
{
  os << "Constraint: " << fName << " " << ConstraintString[fConstraintType] << " ";

  ColumnNameList::const_iterator itr;
  os << "(";

  for (itr = fColumnNameList.begin(); itr != fColumnNameList.end(); ++itr)
  {
    os << *itr << " ";
  }

  os << ")";

  return os;
}

TableReferencesConstraintDef::TableReferencesConstraintDef(ColumnNameList* columns, QualifiedName* tableName,
                                                           ColumnNameList* foreignColumns,
                                                           DDL_MATCH_TYPE matchType,
                                                           ReferentialAction* refAction)
 : TableConstraintDef(DDL_REFERENCES)
 , fColumns(*columns)
 , fTableName(tableName)
 , fForeignColumns(*foreignColumns)
 , fMatchType(matchType)
 , fRefAction(refAction)
{
  delete columns;
  delete foreignColumns;
}
std::ostream& TableReferencesConstraintDef::put(std::ostream& os) const
{
  os << "Constraint: " << fName << " " << ConstraintString[fConstraintType] << " ";

  ColumnNameList::const_iterator itr;
  os << "lcols (";

  for (itr = fColumns.begin(); itr != fColumns.end(); ++itr)
  {
    os << *itr << " ";
  }

  os << ")";

  os << " ftable=" << *fTableName;

  os << " ";

  os << "fcols (";

  for (itr = fForeignColumns.begin(); itr != fForeignColumns.end(); ++itr)
  {
    os << *itr << " ";
  }

  os << ")";

  return os;
}
std::ostream& operator<<(std::ostream& os, const ColumnNameList& columnNames)
{
  ColumnNameList::const_iterator itr;
  os << '(';

  for (itr = columnNames.begin(); itr != columnNames.end(); ++itr)
  {
    os << *itr << " ";
  }

  os << ')';
  return os;
}

TableReferencesConstraintDef::~TableReferencesConstraintDef()
{
  delete fTableName;
  delete fRefAction;
}

}  // namespace ddlpackage
