// -*- C++ -*- //

/* sqlbuilder.h
 *
 * Copyright 2006 libgdamm Development Team
 *
 * 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.1 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <libgdamm/statement.h>
#include <libgdamm/sqlexpr.h>
#include <libgdamm/sqlstatement.h>

_DEFS(libgdamm,libgda)
_PINCLUDE(glibmm/private/object_p.h)

namespace Gnome
{

namespace Gda
{

_WRAP_ENUM(SqlOperatorType, GdaSqlOperatorType)
_WRAP_ENUM(SqlSelectJoinType, GdaSqlSelectJoinType)
_WRAP_GERROR(SqlBuilderError, GdaSqlBuilderError, GDA_SQL_BUILDER_ERROR)
_WRAP_GERROR(SqlError, GdaSqlError, GDA_SQL_ERROR)

/** SqlBuilder can be used to build a Statement from its structural description, 
 * much in the same way a SqlParser can be used to build a Statement from a SQL string.
 *
 * You may, for instance, pass the SqlBuilder directly to 
 * Connection::statement_execute_select_builder() or 
 * Connection::statement_execute_non_select_builder(). Or you may instead call 
 * get_statement() and use the resulting SqlStatement.
 *
 * During the building process, some pieces of the statement are constructed 
 * and assembled into the final statement. Each of these pieces can be reused 
 * anytime in the same SqlBuilder object, and each is identified using a 
 * single ID. That ID is dynamically allocated by the object.
 * 
 * This simple example builds the equivalent of this SQL query:
 * "SELECT employees.name_first, employees.name_last FROM employees WHERE employees.employee_id = 123".
 *
 * @code
 * Glib::RefPtr<Gnome::Gda::SqlBuilder> builder =
 *   Gnome::Gda::SqlBuilder::create(Gnome::Gda::SQL_STATEMENT_SELECT);
 * builder->select_add_field("name_fist", "employees");
 * builder->select_add_field("name_last", "employees");
 * builder->select_add_target("employees");
 * builder->set_where(
 *   builder->add_cond(Gnome::Gda::SQL_OPERATOR_TYPE_EQ,
 *     builder->add_field_id("employee_id", "employees"),
 *     builder->add_expr(123)));
 * @endcode
 *
 * Note that, in this simple example, it's not necessary to specify the extra 
 * "employees" table name parameter to select_add_field() and add_field_id(),
 * though it's often a good idea to avoid ambiguity in larger queries. 
 */
class SqlBuilder : public Glib::Object
{
  _CLASS_GOBJECT(SqlBuilder, GdaSqlBuilder, GDA_SQL_BUILDER, Glib::Object, GObject)
protected:
  SqlBuilder(SqlStatementType type);
public:
  _WRAP_CREATE(SqlStatementType type)

#m4 _CONVERSION(`const Glib::ustring& ', `const gchar*', `string_or_null($3)')

  _WRAP_METHOD(Glib::RefPtr<Statement> get_statement() const, gda_sql_builder_get_statement, errthrow)

#m4 _CONVERSION(`GdaSqlStatement*', `SqlStatement', `Glib::wrap($3, true /* take_copy */)')
  _WRAP_METHOD(SqlStatement get_sql_statement() const, gda_sql_builder_get_sql_statement, errthrow)

  _IGNORE(gda_sql_builder_add_expr, gda_sql_builder_add_param, gda_sql_builder_add_cond)
  _IGNORE(gda_sql_builder_add_cond_v, gda_sql_builder_add_id)
  _IGNORE(gda_sql_builder_add_expr_value)

  #m4 _EQUAL(GdaSqlBuilderId, Id)
  //A duplicate of the GdsSqlBuilderId typedef in libgda's header, to avoid including the header here.
  typedef guint Id;
  
  // Expression API:

  _WRAP_METHOD(Id add_id(const Glib::ustring& string), gda_sql_builder_add_id)
  
  _WRAP_METHOD(Id add_field_id(const Glib::ustring& field_name, const Glib::ustring& table_name = Glib::ustring()), gda_sql_builder_add_field_id)

  /** Defines an expression in Builder which may be reused to build other parts of a statement.
   * @param dh The datahandler to use
   * @param value: value to set the expression to
   *
   * See expr() except that no custom datahandler is given
   *
   * @return The ID of the new expression, or 0 if there was an error.
   */
  template <class ValueType>
  Id add_expr(const Glib::RefPtr<DataHandler>& dh, const ValueType& value);

  /** Defines an expression in Builder which may be reused to build other parts of a statement.
   * @param dh The datahandler to use
   * @param value: value to set the expression to
   *
   * See expr() except that no custom datahandler is given
   *
   * @return The ID of the new expression, or 0 if there was an error.
   */
  Id add_expr_as_value(const Glib::RefPtr<DataHandler>& dh, const Value& value);

  /** Defines an expression in Builder which may be reused to build other parts of a statement.
   * @param value: value to set the expression to
   *
   * See expr() except that no custom datahandler is given
   *
   * @return The ID of the new expression, or 0 if there was an error.
   */
  template <class ValueType>
  Id add_expr(const ValueType& value);

  /** Defines an expression in Builder which may be reused to build other parts of a statement.
   * @param value: value to set the expression to
   *
   * See expr() except that no custom datahandler is given
   *
   * @return The ID of the new expression, or 0 if there was an error.
   */
  Id add_expr_as_value(const Value& value);

  /** Defines a parameter which may be reused to build other parts of a statement.
   * @param: param_name: name of the parameter
   * @type: GType of the parameter
   * @nullok True if the parameter can be set to %NULL
   *
   * @return The ID of the new expression, or 0 if there was an error.
   */
  Id add_param(const Glib::ustring& param_name, GType type, bool nullok = false);

  /** Builds a new expression which reprenents a condition (or operation).
   * @param op Type of condition
   * @param op1 The ID of the 1st argument (not 0)
   * @param op2 The ID of the 2st argument (maybe 0 if op only requires one argument)
   * @param op3 The ID of the 3st argument (maybe 0 if op only requires one or two arguments)
   *
   * @return The ID of the new expression, or 0 if there was an error.
   */
  Id add_cond(SqlOperatorType op, Id op1, Id op2 = 0, Id op3 = 0);

  /** Builds a new expression which reprenents a condition (or operation).
   * @param op Type of condition
   * @param op_ids: list of IDs of the operands of the condition
   *
   * @return The ID of the new expression, or 0 if there was an error.
   */
  Id add_cond(SqlOperatorType op, const std::vector<Id>& op_ids);

  _IGNORE(gda_sql_builder_add_function, gda_sql_builder_add_function_v)
   _IGNORE(gda_sql_builder_select_join_targets)

  /** Builds a new expression which represents a function applied to some arguments.
    *
    * @param function_name The name of the function to embed (e.g. MAX, MIN, ...)
    * @param args: list of ids of the arguments to the function
    *
    * @return The ID of the new target, or 0 if there was an error.
    */
  Id add_function(const Glib::ustring& function_name, const std::vector<Id>& args);

  /** Builds a new expression which represents a function applied to an argument.
   *
   * @param function_name The name of the function to embed (e.g. MAX, MIN, ...)
   * @param arg: The ids of the argument to the function
   *
   * @return The ID of the new target, or 0 if there was an error.
   */
  Id add_function(const Glib::ustring& function_name, Id arg);


  /** Adds an expression which is a subselect.
   *
   * @param sqlst A SqlStatement, which must be a SELECT or compound SELECT.
   * @return The ID of the new expression, or 0 if there was an error.
   */
  Id add_sub_select(const SqlStatement& sqlst);
  _IGNORE(gda_sql_builder_add_sub_select)

//TODO:  _WRAP_METHOD(Id gda_sql_builder_add_case (GdaSqlBuilder *builder, Id id,  Id test_expr, Id else_expr, ...);
//TODO:  _WRAP_METHOD(Id gda_sql_builder_add_case_v (GdaSqlBuilder *builder, Id id,  Id test_expr, Id else_expr,  const Id *when_array, const Id *then_array, gint args_size);

  //TODO: Id add_case(Id id, Id test_expr, Id else_expr, const type_case_pairs& case_pairs);
  //_IGNORE(gda_sql_builder_add_case)

   

  _WRAP_METHOD(Id select_add_field(const Glib::ustring& field_name, const Glib::ustring& table_name, const Glib::ustring& alias = Glib::ustring()), gda_sql_builder_select_add_field)

  // Statement API
  /** Adds a new target to a SELECT statement.
   *
   * @param table_id The ID of the expression holding a table reference.
   * @param alias The alias to give to the target.
   *
   * @return The ID of the new target, or 0 if there was an error.
   */
  Id select_add_target(const Glib::ustring& table_name, const Glib::ustring& alias = Glib::ustring());
  _IGNORE(gda_sql_builder_select_add_target)

  _WRAP_METHOD(Id select_add_target_id(Id table_id, const Glib::ustring& alias = Glib::ustring()), gda_sql_builder_select_add_target_id)

  /** Joins two targets in a SELECT statement.
   *
   * @param left_target_id The ID of the left target to use (not %0)
   * @param right_target_id The ID of the right target to use (not %0)
   * @param join_type The type of join
   * @param join_expr Joining expression's ID, or %0
   *
   * @return The ID of the new join, or 0 if there was an error.
   */
  Id select_join_targets(Id left_target_id, Id right_target_id, SqlSelectJoinType join_type, Id join_expr = 0);

  _WRAP_METHOD(void join_add_field(Id join_id, const Glib::ustring& field_name), gda_sql_builder_join_add_field)
  _WRAP_METHOD(void select_order_by(Id expr_id, bool asc = true, const Glib::ustring& collation_name = Glib::ustring()), gda_sql_builder_select_order_by)

  /** Adds or removes a DISTINCT clause for a SELECT statement.
   * @param distinct Whether a DISTINCT clause should be in the SELECT statement.
   * @newin4p2
   */
  void select_set_distinct(bool distinct = true);

  _WRAP_METHOD(void select_set_distinct(bool distinct, Id expr_id), gda_sql_builder_select_set_distinct)

  /** Defines the maximum number of rows in the DataModel resulting from the execution of the built statement.
   *
   * @param limit_count The number of rows that should be returned.
   * @param The offset of the first row that should appear in the result. (note that
   * this feature may not be supported by all the database providers.)
   */
  void select_set_limit(Id limit_count, Id offset = 0);

  _WRAP_METHOD(void select_set_limit_id(Id limit_count_expr_id, Id limit_offest_expr_id = 0), gda_sql_builder_select_set_limit)

  _WRAP_METHOD(void select_set_having(Id cond_id), gda_sql_builder_select_set_having)

  _WRAP_METHOD(void select_group_by(Id expr_id), gda_sql_builder_select_group_by)
  //TODO: Add select_group_by_field()?

  // General Statement API
  _WRAP_METHOD(void set_table(const Glib::ustring& table_name), gda_sql_builder_set_table)
  _WRAP_METHOD(void set_where(Id cond_id), gda_sql_builder_set_where)

  //The documentation is hand-written to use doxygen's list/item syntax.
  /** Valid only for: INSERT, UPDATE, SELECT statements.
   * - For UPDATE: specifies that the field represented by @a field_id will be set to the value identified
   * by @a value_id.
   * - For SELECT: add a selected item to the statement, and if @a value_id is not %0, then use it as an
   * alias
   * - For INSERT: if @a field_id represents an SQL identifier (obtained using add_id()): then if
   *  @a value_id is not %0 then specifies that the field represented by @a field_id will be set to the
   * value identified by @a value_id, otherwise just specifies a named field to be given a value.
   * If @a field_id represents a sub SELECT (obtained using add_sub_select()), then
   * this method call defines the sub SELECT from which values to insert are taken.
   * 
   * See also add_field_value() and add_field_value_as_gvalue().
   * 
   * @newin{4,2}
   * @param field_id The ID of the field's name or definition.
   * @param value_id The ID of the value to set the field to, or %0.
   */
  _WRAP_METHOD(void add_field_value_id(Id field_id, Id value_id = 0), gda_sql_builder_add_field_value_id)

  /** Specifies that the field represented by @a field_name will be set to the @a value.
   * See gadd_expr() for more information.
   * This is valid only for INSERT and UPDATE statements.
   *
   * @parm field_name A field name.
   * @param value The value to set the field to.
   */
  template <class ValueType>
  void add_field_value(const Glib::ustring& field_name, const ValueType& value);

  _WRAP_METHOD(void add_field_value_as_value(const Glib::ustring& field_name, const Value& value), gda_sql_builder_add_field_value_as_gvalue)
  _IGNORE(gda_sql_builder_add_field_value)


/* COMPOUND SELECT Statement API */
  //TODO: Wrap the enum when we wrap GdaSqlStatementCompound _WRAP_METHOD(void compound_set_type(SqlStatementCompoundType compound_type), gda_sql_builder_compound_set_type)
  _WRAP_METHOD(void compound_add_sub_select(const SqlStatement& sqlst), gda_sql_builder_compound_add_sub_select)

  _WRAP_METHOD(SqlExpr export_expression(Id id) const, gda_sql_builder_export_expression)

  //TODO: This is a crappy description.
  /** Imports the expression.
   *
   * @param expr A SqlExpr obtained using export_expression().
   * @return The ID of the new expression, or 0 if there was an error.
   */
  Id import_expression(const SqlExpr& expr);
  _IGNORE(gda_sql_builder_import_expression)
};

#ifndef DOXYGEN_SHOULD_SKIP_THIS

template <class ValueType> inline
void SqlBuilder::add_field_value(const Glib::ustring& field_name, const ValueType& value)
{
  Gnome::Gda::Value gdavalue(value);
  this->add_field_value_as_value(field_name, gdavalue);
}

template <class ValueType> inline
SqlBuilder::Id SqlBuilder::add_expr(const Glib::RefPtr<DataHandler>& dh, const ValueType& value)
{
  Gnome::Gda::Value gdavalue(value);
  return this->add_expr_as_value(dh, gdavalue);
}

template <class ValueType> inline
SqlBuilder::Id SqlBuilder::add_expr(const ValueType& value)
{
  Gnome::Gda::Value gdavalue(value);
  return this->add_expr_as_value(gdavalue);
}

#endif //DOXYGEN_SHOULD_SKIP_THIS

} // namespace Gda
} // namespace Gnome
