module ThinkingSphinx
  class Source
    module SQL
      # Generates the big SQL statement to get the data back for all the fields
      # and attributes, using all the relevant association joins. If you want
      # the version filtered for delta values, send through :delta => true in the
      # options. Won't do much though if the index isn't set up to support a
      # delta sibling.
      # 
      # Examples:
      # 
      #   source.to_sql
      #   source.to_sql(:delta => true)
      #
      def to_sql(options={})
        sql = <<-SQL
SELECT #{ sql_select_clause options[:offset] }
FROM #{ @model.quoted_table_name }
  #{ all_associations.collect { |assoc| assoc.to_sql }.join(' ') }
WHERE #{ sql_where_clause(options) }
GROUP BY #{ sql_group_clause }
        SQL

        sql += " ORDER BY NULL" if adapter.sphinx_identifier == "mysql"
        sql
      end

      # Simple helper method for the query range SQL - which is a statement that
      # returns minimum and maximum id values. These can be filtered by delta -
      # so pass in :delta => true to get the delta version of the SQL.
      # 
      def to_sql_query_range(options={})
        min_statement = adapter.convert_nulls(
          "MIN(#{quote_column(@model.primary_key)})", 1
        )
        max_statement = adapter.convert_nulls(
          "MAX(#{quote_column(@model.primary_key)})", 1
        )

        sql = "SELECT #{min_statement}, #{max_statement} " +
              "FROM #{@model.quoted_table_name} "
        if self.delta? && !@index.delta_object.clause(@model, options[:delta]).blank?
          sql << "WHERE #{@index.delta_object.clause(@model, options[:delta])}"
        end

        sql
      end

      # Simple helper method for the query info SQL - which is a statement that
      # returns the single row for a corresponding id.
      # 
      def to_sql_query_info(offset)
        "SELECT * FROM #{@model.quoted_table_name} WHERE " +
        "#{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
      end

      def sql_select_clause(offset)
        unique_id_expr = ThinkingSphinx.unique_id_expression(offset)

        (
          ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} "] + 
          @fields.collect     { |field|     field.to_select_sql     } +
          @attributes.collect { |attribute| attribute.to_select_sql }
        ).compact.join(", ")
      end

      def sql_where_clause(options)
        logic = [
          "#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} >= $start",
          "#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} <= $end"
        ]

        if self.delta? && !@index.delta_object.clause(@model, options[:delta]).blank?
          logic << "#{@index.delta_object.clause(@model, options[:delta])}"
        end

        logic += (@conditions || [])

        logic.join(" AND ")
      end

      def sql_group_clause
        internal_groupings = []
        if @model.column_names.include?(@model.inheritance_column)
           internal_groupings << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}"
        end

        (
          ["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}"] + 
          @fields.collect     { |field|     field.to_group_sql     }.compact +
          @attributes.collect { |attribute| attribute.to_group_sql }.compact +
          @groupings + internal_groupings
        ).join(", ")
      end

      def sql_query_pre_for_core
        if self.delta? && !@index.delta_object.reset_query(@model).blank?
          [@index.delta_object.reset_query(@model)]
        else
          []
        end
      end

      def sql_query_pre_for_delta
        [""]
      end

      def quote_column(column)
        @model.connection.quote_column_name(column)
      end

      def crc_column
        if @model.column_names.include?(@model.inheritance_column)
          adapter.cast_to_unsigned(adapter.convert_nulls(
            adapter.crc(adapter.quote_with_table(@model.inheritance_column), true),
            @model.to_crc32
          ))
        else
          @model.to_crc32.to_s
        end
      end
    end
  end
end