/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 *
 *  Copyright (C) 2010 Brian Aker
 *  Copyright (C) 2008 Sun Microsystems, 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 */


/* Function with list databases, tables or fields */
#include <config.h>

#include <drizzled/data_home.h>
#include <drizzled/error.h>
#include <drizzled/internal/my_sys.h>
#include <drizzled/plugin/storage_engine.h>
#include <drizzled/session.h>
#include <drizzled/show.h>
#include <drizzled/sql_select.h>
#include <drizzled/statement/show.h>
#include <drizzled/statement/show_errors.h>
#include <drizzled/statement/show_warnings.h>
#include <drizzled/sql_lex.h>
#include <drizzled/table_ident.h>
#include <drizzled/catalog/instance.h>

#include <sys/stat.h>

#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>

using namespace std;

namespace drizzled {

inline const char* str_or_nil(const char *str)
{
  return str ? str : "<nil>";
}

/*
  Get the quote character for displaying an identifier.

  SYNOPSIS
    get_quote_char_for_identifier()

  IMPLEMENTATION
    Force quoting in the following cases:
      - name is empty (for one, it is possible when we use this function for
        quoting user and host names for DEFINER clause);
      - name is a keyword;
      - name includes a special character;
    Otherwise identifier is quoted only if the option OPTION_QUOTE_SHOW_CREATE
    is set.

  RETURN
    EOF	  No quote character is needed
    #	  Quote character
*/

int get_quote_char_for_identifier()
{
  return '`';
}

namespace show {

bool buildSchemas(Session *session)
{
  session->lex().sql_command= SQLCOM_SELECT;
  session->lex().statement= new statement::Show(session);

  std::string column_name= "Database";
  if (session->lex().wild)
  {
    column_name.append(" (");
    column_name.append(session->lex().wild->ptr());
    column_name.append(")");
  }

  if (prepare_new_schema_table(session, session->lex(), session->lex().current_select->where ? "SCHEMAS" : "SHOW_SCHEMAS"))
    return false;

  Item_field *my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "SCHEMA_NAME");
  my_field->is_autogenerated_name= false;
  my_field->set_name(column_name);

  session->add_item_to_list(my_field);
  session->add_order_to_list(my_field, true);
  return true;
}

bool buildTables(Session *session, const char *ident)
{
  session->lex().sql_command= SQLCOM_SELECT;

  drizzled::statement::Show *select= new statement::Show(session);
  session->lex().statement= select;

  std::string column_name= "Tables_in_";

  util::string::ptr schema(session->schema());
  if (ident)
  {
    identifier::Schema identifier(session->catalog().identifier(),
                                  str_ref(ident));
    column_name.append(ident);
    session->lex().select_lex.db= ident;
    if (not plugin::StorageEngine::doesSchemaExist(identifier))
    {
      my_error(ER_BAD_DB_ERROR, identifier);
    }
    select->setShowPredicate(ident, "");
  }
  else if (schema and not schema->empty())
  {
    column_name.append(*schema);
    select->setShowPredicate(*schema, "");
  }
  else
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    return false;
  }


  if (session->lex().wild)
  {
    column_name.append(" (");
    column_name.append(session->lex().wild->ptr());
    column_name.append(")");
  }

  if (prepare_new_schema_table(session, session->lex(), "SHOW_TABLES"))
    return false;

  Item_field *my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "TABLE_NAME");
  my_field->is_autogenerated_name= false;
  my_field->set_name(column_name);

  session->add_item_to_list(my_field);
  session->add_order_to_list(my_field, true);
  return true;
}

bool buildTemporaryTables(Session *session)
{
  session->lex().sql_command= SQLCOM_SELECT;
  session->lex().statement= new statement::Show(session);

  if (prepare_new_schema_table(session, session->lex(), "SHOW_TEMPORARY_TABLES"))
    return false;

  session->add_item_to_list( new Item_field(&session->lex().current_select->context, NULL, NULL, "*"));
  session->lex().current_select->with_wild++;
  return true;
}

bool buildTableStatus(Session *session, const char *ident)
{
  session->lex().sql_command= SQLCOM_SELECT;
  drizzled::statement::Show *select= new statement::Show(session);
  session->lex().statement= select;

  std::string column_name= "Tables_in_";

  util::string::ptr schema(session->schema());
  if (ident)
  {
    session->lex().select_lex.db= ident;

    identifier::Schema identifier(session->catalog().identifier(),
                                  str_ref(ident));
    if (not plugin::StorageEngine::doesSchemaExist(identifier))
    {
      my_error(ER_BAD_DB_ERROR, identifier);
    }

    select->setShowPredicate(ident, "");
  }
  else if (schema)
  {
    select->setShowPredicate(*schema, "");
  }
  else
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    return false;
  }

  if (prepare_new_schema_table(session, session->lex(), "SHOW_TABLE_STATUS"))
    return false;

  session->add_item_to_list( new Item_field(&session->lex().current_select->context, NULL, NULL, "*"));
  session->lex().current_select->with_wild++;
  return true;
}

bool buildEngineStatus(Session *session, str_ref)
{
  session->lex().sql_command= SQLCOM_SELECT;
  drizzled::statement::Show *select= new statement::Show(session);
  session->lex().statement= select;

  my_error(ER_USE_DATA_DICTIONARY);
  return false;
}

bool buildColumns(Session *session, const char *schema_ident, Table_ident *table_ident)
{
  session->lex().sql_command= SQLCOM_SELECT;

  drizzled::statement::Show *select= new statement::Show(session);
  session->lex().statement= select;

  util::string::ptr schema(session->schema());
  if (schema_ident)
  {
    select->setShowPredicate(schema_ident, table_ident->table.data());
  }
  else if (table_ident->db.data())
  {
    select->setShowPredicate(table_ident->db.data(), table_ident->table.data());
  }
  else if (schema)
  {
    select->setShowPredicate(*schema, table_ident->table.data());
  }
  else
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    return false;
  }

  {
    drizzled::identifier::Table identifier(session->catalog().identifier(),
                                           select->getShowSchema(),
                                           table_ident->table.data());
    if (not plugin::StorageEngine::doesTableExist(*session, identifier))
    {
      my_error(ER_TABLE_UNKNOWN, identifier);
    }
  }

  if (prepare_new_schema_table(session, session->lex(), "SHOW_COLUMNS"))
    return false;

  session->add_item_to_list( new Item_field(&session->lex().current_select->context, NULL, NULL, "*"));
  session->lex().current_select->with_wild++;
  return true;
}

void buildSelectWarning(Session *session)
{
  (void) create_select_for_variable(session, "warning_count");
  session->lex().statement= new statement::Show(session);
}

void buildSelectError(Session *session)
{
  (void) create_select_for_variable(session, "error_count");
  session->lex().statement= new statement::Show(session);
}

void buildWarnings(Session *session)
{
  session->lex().statement= new statement::ShowWarnings(session);
}

void buildErrors(Session *session)
{
  session->lex().statement= new statement::ShowErrors(session);
}

bool buildIndex(Session *session, const char *schema_ident, Table_ident *table_ident)
{
  session->lex().sql_command= SQLCOM_SELECT;
  drizzled::statement::Show *select= new statement::Show(session);
  session->lex().statement= select;

  util::string::ptr schema(session->schema());
  if (schema_ident)
  {
    select->setShowPredicate(schema_ident, table_ident->table.data());
  }
  else if (table_ident->db.data())
  {
    select->setShowPredicate(table_ident->db.data(), table_ident->table.data());
  }
  else if (schema)
  {
    select->setShowPredicate(*schema, table_ident->table.data());
  }
  else
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    return false;
  }

  {
    drizzled::identifier::Table identifier(session->catalog().identifier(),
                                           select->getShowSchema(),
                                           table_ident->table.data());
    if (not plugin::StorageEngine::doesTableExist(*session, identifier))
    {
      my_error(ER_TABLE_UNKNOWN, identifier);
    }
  }

  if (prepare_new_schema_table(session, session->lex(), "SHOW_INDEXES"))
    return false;

  session->add_item_to_list(new Item_field(&session->lex().current_select->context, NULL, NULL, "*"));
  session->lex().current_select->with_wild++;
  return true;
}

bool buildStatus(Session *session, const drizzled::sql_var_t is_global)
{
  session->lex().sql_command= SQLCOM_SELECT;
  session->lex().statement= new statement::Show(session);

  if (prepare_new_schema_table(session, session->lex(), is_global == OPT_GLOBAL ? "GLOBAL_STATUS" : "SESSION_STATUS"))
    return false;

  Item_field *my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "VARIABLE_NAME");
  my_field->is_autogenerated_name= false;
  my_field->set_name("Variable_name");
  session->add_item_to_list(my_field);
  my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "VARIABLE_VALUE");
  my_field->is_autogenerated_name= false;
  my_field->set_name("Value");
  session->add_item_to_list(my_field);
  return true;
}

bool buildCreateTable(Session *session, Table_ident *ident)
{
  session->lex().sql_command= SQLCOM_SELECT;
  statement::Show *select= new statement::Show(session);
  session->lex().statement= select;

  if (session->lex().statement == NULL)
    return false;

  if (prepare_new_schema_table(session, session->lex(), "TABLE_SQL_DEFINITION"))
    return false;

  util::string::ptr schema(session->schema());
  if (ident->db.data())
  {
    select->setShowPredicate(ident->db.data(), ident->table.data());
  }
  else if (schema)
  {
    select->setShowPredicate(*schema, ident->table.data());
  }
  else
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    return false;
  }

  Item_field *my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "TABLE_NAME");
  my_field->is_autogenerated_name= false;
  my_field->set_name("Table");
  session->add_item_to_list(my_field);
  my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "TABLE_SQL_DEFINITION");
  my_field->is_autogenerated_name= false;
  my_field->set_name("Create Table");
  session->add_item_to_list(my_field);
  return true;
}

bool buildProcesslist(Session *session)
{
  session->lex().sql_command= SQLCOM_SELECT;
  session->lex().statement= new statement::Show(session);

  if (prepare_new_schema_table(session, session->lex(), "PROCESSLIST"))
    return false;

  session->add_item_to_list( new Item_field(&session->lex().current_select->context, NULL, NULL, "*"));
  session->lex().current_select->with_wild++;
  return true;
}

bool buildVariables(Session *session, const drizzled::sql_var_t is_global)
{
  session->lex().sql_command= SQLCOM_SELECT;
  session->lex().statement= new statement::Show(session);

  if (is_global == OPT_GLOBAL)
  {
    if (prepare_new_schema_table(session, session->lex(), "GLOBAL_VARIABLES"))
      return false;
  }
  else
  {
    if (prepare_new_schema_table(session, session->lex(), "SESSION_VARIABLES"))
      return false;
  }

  Item_field *my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "VARIABLE_NAME");
  my_field->is_autogenerated_name= false;
  my_field->set_name("Variable_name");
  session->add_item_to_list(my_field);
  my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "VARIABLE_VALUE");
  my_field->is_autogenerated_name= false;
  my_field->set_name("Value");

  session->add_item_to_list(my_field);
  return true;
}

bool buildCreateSchema(Session *session, str_ref ident)
{
  session->lex().sql_command= SQLCOM_SELECT;
  drizzled::statement::Show *select= new statement::Show(session);
  session->lex().statement= select;

  if (prepare_new_schema_table(session, session->lex(), "SCHEMA_SQL_DEFINITION"))
    return false;

  util::string::ptr schema(session->schema());
  if (ident.data())
  {
    select->setShowPredicate(ident.data());
  }
  else if (schema)
  {
    select->setShowPredicate(*schema);
  }
  else
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    return false;
  }

  Item_field *my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "SCHEMA_NAME");
  my_field->is_autogenerated_name= false;
  my_field->set_name("Database");
  session->add_item_to_list(my_field);

  my_field= new Item_field(&session->lex().current_select->context, NULL, NULL, "SCHEMA_SQL_DEFINITION");
  my_field->is_autogenerated_name= false;
  my_field->set_name("Create Database");
  session->add_item_to_list(my_field);
  return true;
}

bool buildDescribe(Session *session, Table_ident *ident)
{
  session->lex().lock_option= TL_READ;
  init_select(&session->lex());
  session->lex().current_select->parsing_place= SELECT_LIST;
  session->lex().sql_command= SQLCOM_SELECT;
  drizzled::statement::Show *select= new statement::Show(session);
  session->lex().statement= select;
  session->lex().select_lex.db= 0;

  util::string::ptr schema(session->schema());
  if (ident->db.data())
  {
    select->setShowPredicate(ident->db.data(), ident->table.data());
  }
  else if (schema)
  {
    select->setShowPredicate(*schema, ident->table.data());
  }
  else
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    return false;
  }

  {
    drizzled::identifier::Table identifier(session->catalog().identifier(),
                                           select->getShowSchema(),
                                           ident->table.data());
    if (not plugin::StorageEngine::doesTableExist(*session, identifier))
    {
      my_error(ER_TABLE_UNKNOWN, identifier);
    }
  }

  if (prepare_new_schema_table(session, session->lex(), "SHOW_COLUMNS"))
  {
    return false;
  }
  session->add_item_to_list( new Item_field(&session->lex().current_select->context, NULL, NULL, "*"));
  session->lex().current_select->with_wild++;
  return true;
}

}
} /* namespace drizzled */
