module Sunspot
  module Query
    class CompositeFulltext
      def initialize
        @components = []
        @dismax = nil
      end

      def add_fulltext(keywords)
        @components << @dismax = Dismax.new(keywords)
        @dismax
      end

      def add_boost_query(factor)
        @dismax ||= Dismax.new("*:*")
        @components << @dismax unless @components.include?(@dismax)
        @dismax.add_boost_query(factor)
      end

      def add_boost_function(function)
        @dismax ||= Dismax.new("*:*")
        @components << @dismax unless @components.include?(@dismax)
        @dismax.add_additive_boost_function(function)
      end

      def add_multiplicative_boost_function(function)
        @dismax ||= Dismax.new("*:*")
        @components << @dismax unless @components.include?(@dismax)
        @dismax.add_multiplicative_boost_function(function)
      end

      def add_join(keywords, target, from, to)
        @components << join = Join.new(keywords, target, from, to)
        join
      end
      
      def add_location(field, lat, lng, options)
        @components << location = Geo.new(field, lat, lng, options)
        location
      end

      def add_disjunction
        @components << disjunction = Disjunction.new
        disjunction
      end

      def add_conjunction
        @components << conjunction = Conjunction.new
        conjunction
      end

      def to_params
        if @components.length == 0
          {}
        elsif @components.length > 1 or @components.find { |c| c.is_a?(Join) }
          to_subquery.merge(:fl => '* score')
        else
          @components.first.to_params.merge(:fl => '* score')
        end
      end

      def to_subquery
        return {} unless @components.any?

        params = @components.map(&:to_subquery).inject({:q => []}) do |res, subquery|
          res[:q] << subquery.delete(:q) if subquery[:q]
          res.merge(subquery)
        end

        params[:q] = params[:q].size > 1 ? "(#{params[:q].join(" #{connector} ")})" : params[:q].join
        params
      end
    end

    class Disjunction < CompositeFulltext
      #
      # No-op - this is already a disjunction
      #
      def add_disjunction
        self
      end

      private

      def connector
        'OR'
      end
    end

    class Conjunction < CompositeFulltext
      #
      # No-op - this is already a conjunction
      #
      def add_conjunction
        self
      end

      private

      def connector
        'AND'
      end
    end
  end
end