#
# Ronin SQL - A Ruby DSL for crafting SQL Injections.
#
# Copyright (c) 2007-2013 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This file is part of Ronin SQL.
#
# 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; either version 2 of the License, or
# (at your option) any later version.
#
# 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
#

module Ronin
  module SQL
    #
    # Methods for creating common SQL {Clause Clauses}.
    #
    # @api public
    #
    module Clauses
      #
      # The defined clauses of the statement.
      #
      # @return [Array<Clause>]
      #   The clauses defined thus far.
      #
      def clauses
        @clauses ||= []
      end

      #
      # Appends an arbitrary clause.
      #
      # @param [Symbol] keyword
      #   The name of the clause.
      #
      # @param [Object] argument
      #   Additional argument for the clause.
      #
      # @yield [(clause)]
      #   If a block is given, the return value will be used as the argument.
      #
      # @yieldparam [Clause] clause
      #   If the block accepts an argument, it will be passed the new clause.
      #   Otherwise the block will be evaluated within the clause.
      #
      # @return [self]
      #
      def clause(keyword,argument=nil,&block)
        clauses << Clause.new(keyword,argument,&block)
        return self
      end

      #
      # Appends a `FROM` clause.
      #
      # @param [Field, Symbol] table
      #   The table to select from.
      #
      # @return [self]
      #
      def from(table=nil,&block)
        clause(:FROM,table,&block)
      end

      #
      # Appends an `INTO` clause.
      #
      # @param [Field, Symbol] table
      #   The table to insert into.
      #
      # @return [self]
      #
      def into(table=nil,&block)
        clause(:INTO,table,&block)
      end

      #
      # Appends a `WHERE` clause.
      #
      # @return [self]
      #
      def where(&block)
        clause(:WHERE,&block)
      end

      #
      # Appends a `JOIN` clause.
      #
      # @param [Field, Symbol] table
      #   The table to join.
      #
      # @return [self]
      #
      def join(table=nil,&block)
        clause(:JOIN,table,&block)
      end

      #
      # Appends a `INNER JOIN` clause.
      #
      # @param [Field, Symbol] table
      #   The table to join.
      #
      # @return [self]
      #
      def inner_join(table=nil,&block)
        clause([:INNER, :JOIN],table,&block)
      end

      #
      # Appends a `LEFT JOIN` clause.
      #
      # @param [Field, Symbol] table
      #   The table to join.
      #
      # @return [self]
      #
      def left_join(table=nil,&block)
        clause([:LEFT, :JOIN],table,&block)
      end

      #
      # Appends a `RIGHT JOIN` clause.
      #
      # @param [Field, Symbol] table
      #   The table to join.
      #
      # @return [self]
      #
      def right_join(table=nil,&block)
        clause([:RIGHT, :JOIN],table,&block)
      end

      #
      # Appends a `FULL JOIN` clause.
      #
      # @param [Field, Symbol] table
      #   The table to join.
      #
      # @return [self]
      #
      def full_join(table=nil,&block)
        clause([:FULL, :JOIN],table,&block)
      end

      #
      # Appends a `ON` clause.
      #
      # @return [self]
      #
      def on(&block)
        clause(:ON,&block)
      end

      #
      # Appends a `UNION` clause.
      #
      # @return [self]
      #
      def union(&block)
        clause(:UNION,&block)
      end

      #
      # Appends a `UNION ALL` clause.
      #
      # @return [self]
      #
      # @since 1.1.0
      #
      def union_all(&block)
        clause([:UNION, :ALL],&block)
      end

      #
      # Appends a `GROUP BY` clause.
      #
      # @param [Array<Field, Symbol>] columns
      #   The columns for `GROUP BY`.
      #
      # @return [self]
      #
      def group_by(*columns,&block)
        clause([:GROUP, :BY],columns,&block)
      end

      #
      # Appends a `HAVING` clause.
      #
      # @return [self]
      #
      def having(&block)
        clause(:HAVING,&block)
      end

      #
      # Appends a `LIMIT` clause.
      #
      # @param [Integer] value
      #   The maximum number of rows to select.
      #
      # @return [self]
      #
      def limit(value,&block)
        clause(:LIMIT,value,&block)
      end

      #
      # Appends a `OFFSET` clause.
      #
      # @param [Integer] value
      #   The index to start selecting at within the result set.
      #
      # @return [self]
      #
      def offset(value,&block)
        clause(:OFFSET,value,&block)
      end

      #
      # Appends a `TOP` clause.
      #
      # @param [Integer] value
      #   The number of top rows to select. 
      #
      # @return [self]
      #
      def top(value,&block)
        clause(:TOP,value,&block)
      end

      #
      # Appends a `INTO` clause.
      #
      # @param [Field, Symbol] table
      #   The table to insert/replace into.
      #
      # @return [self]
      #
      def into(table)
        clause(:INTO,table)
      end

      #
      # Appends a `VALUES` clause.
      #
      # @param [Array] values
      #   The values to insert.
      #
      # @return [self]
      #
      def values(*values)
        clause(:VALUES,values)
      end

      #
      # Appends a `DEFAULT VALUES` clause.
      #
      # @return [self]
      #
      def default_values
        clause([:DEFAULT, :VALUES])
      end

      #
      # Appends a `SET` clause.
      #
      # @param [Hash{Field,Symbol => Object}] values
      #   The columns and values to update.
      #
      # @return [self]
      #
      def set(values={})
        clause(:SET,values)
      end

      #
      # Appends a `INDEXED BY` clause.
      #
      # @param [Field, Symbol] name
      #   The name of the index.
      #
      # @return [self]
      #
      def indexed_by(name,&block)
        clause([:INDEXED, :BY],name,&block)
      end

      #
      # Appends a `NOT INDEXED` clause.
      #
      # @return [self]
      #
      def not_indexed
        clause([:NOT, :INDEXED])
      end
    end
  end
end