require "rexml/validation/validation"

module REXML
  module Validation
    class RelaxNG
      include Validator

      INFINITY = 1.0 / 0.0
      attr_accessor :current

      def initialize source
        parser = REXML::Parsers::BaseParser.new( source )

        @root = @current = State.new
        previous = @current
        branches = []
        slinker = [ ]
        elinker = [ ]

        sequence = lambda {|e| previous = previous[ e ] = State.new }
        oneplus = lambda {|e|
          slinker.pop
          s = slinker[-1].call( e )
          branches << [e, s]
          s
        }
        zeroplus = lambda {|e| 
          branches.pop[e] = sequence.call( e ) 
          elinker.pop
        }
        schoice = lambda {|e| 
          previous = branches[-2]
          sequence.call( e ) 
        }  
        echoice = lambda {|e| previous[e] = branches[-1] }
        slinker << sequence
        elinker << sequence
        begin
          event = parser.pull
          case event[0]
          when :start_element
            case event[1]
            when "element"
              evt = Event.new( event[0], event[2]["name"] )
              slinker[-1].call( evt )
              slinker << sequence
            when "attribute"
              evt = Event.new( :has_attribute, event[2]["name"] )
              slinker[-1].call( evt )
            when "optional"
              branches << previous
              elinker << zeroplus
            when "choice"
              branches << previous
              branches << State.new
              slinker << schoice
              elinker << echoice
            when "oneOrMore"
              slinker << oneplus
            when "zeroOrMore"
              branches << previous
              slinker << oneplus
            when "empty"
            when "text"
              evt = Event.new( :text )
              slinker[-1].call(evt)
            end
          when :end_element
            case event[1]
            when "element"
              evt = Event.new( event[0] )
              elinker[-1].call( evt )
              slinker.pop
            when "attribute"
            when "zeroOrMore"
              e,s = branches.pop
              previous[e] = s
              elinker << zeroplus
            when "oneOrMore"
              e,s = branches.pop
              previous[ e ] = s
            when "choice"
              slinker.pop
              elinker.pop
              previous = branches.pop
              branches.pop
            when "optional"
            when "empty"
            end
          when :end_document
            previous[ Event.new( event[0] ) ] = State.new
          end
        end while event[0] != :end_document
      end

      def receive event
        validate( event )
      end
    end
  end
end
