module Criteria
  class Blank
    mask    = ["__send__", "__id__", "inspect", "class", "is_a?", "dup"];
    methods = instance_methods(true)

    methods = methods - mask
    
    methods.each do
      | m |
      undef_method(m)
    end
  end #c:Blank

  class Table < Blank
    def initialize
      @criterion_type = Criterion
    end #m:initialize

    def method_missing(m, *args)
      c         = @criterion_type.new(m, *args)
      c._parent = self
      return c
    end #m:method_missing

    def x?; return Placeholder.new; end
  end #c:Table

  class Placeholder;
    def to_s; "?"; end
  end

  class Criterion < Blank
    attr_accessor :_parent

    def initialize(field, *args)
      @field = field
      @chain = args
    end #m:initialize


    def initialize_copy(c)
      @_parent = c._parent
    end #m:initialize_copy


    def method_missing(*args)
      if @chain.size > 0
        c         = self.class.new(self, *args)
        c._parent = self._parent
        return c
      end

      @chain = args
      self
    end #m:method_missing


    def [](*args)
      if @field.is_a?(Criterion)
        field = @field[*args]
        args.shift
      else
        field = @field
      end

      chain = @chain.map {
        | link |
        if link.is_a?(Placeholder)
          args.shift
        elsif link.is_a?(Criterion)
          link[*args]
        else
          link
        end
      }

      # This is somewhat evil, but not as evil as doing it manually
      q = self.dup
      q.instance_eval {
        @field = field
        @chain = chain
      }
      return q
    end #m:[]


    def inspect
      return @field.inspect unless @chain.size > 0

      s = "(#{@chain[0]} #{@field.inspect}"
      @chain[1...@chain.size].each {
        | item |
        s << " " << item.inspect
      }

      s << ")"
      return s
    end
  end #c:Criterion
end #M:Criteria
