require "rexml/validation/validation"
require "rexml/parsers/baseparser"

module REXML
  module Validation
    # Implemented:
    # * empty
    # * element
    # * attribute
    # * text
    # * optional
    # * choice
    # * oneOrMore
    # * zeroOrMore
    # * group
    # * value
    #
    # Not implemented:
    # * ref
    # * interleave
    # * mixed
    # * grammar
    # * start
    # * define
    # * data
    # * param
    # * include
    class RelaxNG
      include Validator

      INFINITY = 1.0 / 0.0
      EMPTY = Event.new( nil )
      attr_accessor :current

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

        @root = @current = State.new(0)
        previous = @current
        branches = []
        slinker = [ ]
        elinker = [ ]
        zps = nil
        zpe = nil

        ssequence = lambda {|e| 
          zps = previous = previous[ e ] = State.new 
        }
        esequence = lambda {|e| 
          zpe = e
          previous = previous[ e ] = State.new 
        }
        oneplus = lambda {|e|
          slinker.pop
          s = slinker[-1].call( e )
          branches << [e, s]
          s
        }
        schoice = lambda {|e| 
          if e.single?
            branches[-2][e] = branches[-1]
          else
            previous = branches[-2]
            ssequence.call( e ) 
          end
        }  
        echoice = lambda {|e| 
          previous[e] = branches[-1] 
          previous = branches[-2]
        }
        eoptional = lambda {|e|
          ns = State.new
          elinker.pop
          previous = branches.pop[e] = previous[e] = ns
        }
        slinker << ssequence
        elinker << esequence
        element_names = []
        valevt = nil
        begin
          event = parser.pull
          case event[0]
          when :start_element
            case event[1]
            when "empty"
            when "element", "attribute"
              element_names << event[2]["name"]
              etype = event[1] == "attribute" ? :start_attribute : event[0]
              evt = Event.new( etype, event[2]["name"] )
              slinker[-1].call( evt )
              slinker << ssequence
              elinker << esequence
            when "text"
              evt = Event.new( :text )
              slinker[-1].call(evt)
            when "optional"
              branches << previous
            when "choice"
              branches << previous
              branches << State.new
              slinker << schoice
              elinker << echoice
            when "oneOrMore"
              slinker << oneplus
            when "zeroOrMore"
              branches << previous
              slinker << ssequence
              elinker << esequence
            when "group"
              slinker << ssequence
              elinker << esequence
            when "value"
              valevt = evt = Event.new( :text )
              slinker[-1].call(evt)
            when "ref"
            when "interleave"
            when "mixed"
            when "grammar"
            when "start"
            when "define"
            when "data"
            when "param"
            when "include"
            end
          when :end_element
            case event[1]
            when "attribute", "element"
              slinker.pop
              elinker.pop
              etype = event[1] == "attribute" ? :end_attribute : event[0]
              evt = Event.new( etype, element_names.pop )
              elinker[-1].call( evt )
            when "zeroOrMore"
              slinker.pop
              elinker.pop
              zps[zpe] = previous = branches.pop
            when "oneOrMore"
              e,s = branches.pop
              previous[ e ] = s
            when "choice"
              slinker.pop
              elinker.pop
              previous = branches.pop
              branches.pop
            when "optional"
              elinker << eoptional
              elinker << nil
            when "empty"
            when "group"
              slinker.pop
              elinker.pop
              previous = zps
              elinker[-1].call( zpe )
            end
          when :end_document
            previous[ Event.new( event[0] ) ] = State.new
          when :text
            if valevt
              valevt.event_arg = event[1]
              valevt = nil
            end
          end
        end while event[0] != :end_document
      end

      def receive event
        validate( event )
      end
    end
  end
end
