require 'arel/visitors/postgresql'

module Arel
  module Visitors
    class PostgreSQL
      private

      def visit_Array o, a
        column = case a.try(:relation)
                 when Arel::Nodes::TableAlias, NilClass
                 # noop Prevent from searching for table alias name in schema cache
                 # which won't exist for aliased table when used with Single Table
                 # Inheritance. see dockyard/postgres_ext#154
                 else
                   a.relation.engine.connection.schema_cache.columns(a.relation.name)
                     .find { |col| col.name == a.name.to_s }
                 end
        
        if column && column.respond_to?(:array) && column.array
          quoted o, a
        else
          o.empty? ? 'NULL' : o.map { |x| visit x }.join(', ')
        end
      end

      def visit_Arel_Nodes_Contains o, a = nil
        left_column = o.left.relation.engine.columns.find { |col| col.name == o.left.name.to_s }
        
        if left_column && (left_column.type == :hstore || (left_column.respond_to?(:array) && left_column.array))
          "#{visit o.left, a} @> #{visit o.right, o.left}"
        else
          "#{visit o.left, a} >> #{visit o.right, o.left}"
        end
      end
      
      def visit_Arel_Nodes_ContainedWithin o, a = nil
        "#{visit o.left, a} << #{visit o.right, o.left}"
      end

      def visit_Arel_Nodes_ContainedWithinEquals o, a = nil
        "#{visit o.left, a} <<= #{visit o.right, o.left}"
      end

      def visit_Arel_Nodes_ContainsArray o, a = nil
        "#{visit o.left, a} @> #{visit o.right, o.left}"
      end

      def visit_Arel_Nodes_ContainsHStore o, a = nil
        "#{visit o.left, a} @> #{visit o.right, o.left}"
      end

      def visit_Arel_Nodes_ContainsINet o, a = nil
        "#{visit o.left, a} >> #{visit o.right, o.left}"
      end

      def visit_Arel_Nodes_ContainsEquals o, a = nil
        "#{visit o.left, a} >>= #{visit o.right, o.left}"
      end

      def visit_Arel_Nodes_Overlap o, a = nil
        "#{visit o.left, a} && #{visit o.right, o.left}"
      end

      def visit_IPAddr value, a = nil
        "'#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}'"
      end
    end
  end
end
