require 'rexml/validation/validationexception'

module REXML
  module Validation

    module Validator
      def reset
        @current = @root
        @attr_stack = []
        self
      end
      def validate( event ) 
        @attr_stack = [] unless defined? @attr_stack
        match = @current[ event ]
        raise ValidationException.new( "Validation error.  Expected: "+
          @current.keys.collect{|e| e.inspect }.join(" or ")+
          " but got #{Event.new( event[0], event[1] ).inspect}" ) unless match
        @current = match

        # Check for attributes
        case event[0]
        when :start_element
          @attr_stack << event[2]
          begin
            attr = [:has_attribute, nil]
            k,v = event[2].find { |k,v| 
              attr[1] = k
              m = @current[ attr ] 
              @current = m if m
            }
            event[2].delete(k) if k
          end while k
        when :end_element
          attrs = @attr_stack.pop
          raise ValidationException.new( "Validation error.  Illegal "+
            " attributes: #{attrs.inspect}") if attrs.length > 0
        end
      end
      def dump
        @root.dump
      end
    end

    class State
      include Enumerable

      attr_accessor :flag

      def initialize
        @states = []
      end

      def []=( event, state )
        @states << [event, state]
        return state
      end

      def []( event )
        rv = @states.find { |e,s| e.matches?( event ) }
        return rv[1] if rv
      end

      def keys
        @states.collect{ |e,s| e }
      end

      def each
        @states.each {|e,s| yield s}
      end
      
      def to_s
        "? #{@states.collect{ |e,s| e.to_s }.join(', ')} ?"
      end

      def inspect
        "? #{@states.collect{ |e,s| e.to_s }.join(', ')} ?"
        #%Q/[#{@states.collect{ |e,s| "  " + e.to_s + " -> " + s.to_s }.join("\n")}]/
      end
      def dump
        itterate_over = lambda {|x|
          p x
          x.each(&itterate_over)
        }
        itterate_over.call( self )
      end
    end

    class Event
      def initialize(event_type, event_arg=nil )
        @event_type = event_type
        @event_arg = event_arg
      end

      attr_reader :done?

      def matches?( event )
        #puts "#@event_type =? #{event[0]} && #@event_arg =? #{event[1]} "
        return false unless event[0] == @event_type
        case event[0]
        when :start_element
          return true if event[1] == @event_arg
        when :end_element
          return true
        when :has_attribute
          return true if event[1] == @event_arg
        when :end_document
          return true
=begin
        when :processing_instruction
          false
        when :xmldecl
          false
        when :start_doctype
          false
        when :end_doctype
          false
        when :externalentity
          false
        when :elementdecl
          false
        when :entity
          false
        when :attlistdecl
          false
        when :notationdecl
          false
        when :end_doctype
          false
=end
        else
          false
        end
      end

      def to_s
        inspect
      end

      def inspect
        "#{@event_type.inspect}( #@event_arg )"
      end
    end
  end
end
