require 'webunit/converter'

module WebUnit

  class DomWalkerError < RuntimeError
  end

  class BaseDomWalker
    attr_reader :doc, :cur_path

    def initialize()
      @cur_path = '/'
    end

    def cur_value
      cur_values.join
    end

  end

  case USING_XMLPARSER
  when 'REXML'
    class REXMLDomWalker < BaseDomWalker
      
      ATTR_RS = %q|\s*(\w+)\s*=\s*(["'])(\w+)(['"])\s*|
        
      def initialize(xml)
        super()
        @doc = REXML::Document.new(xml)
        @cur_node = @doc.root
      end

      def change_node(path)
        xpath = path.dup
        cur_path = @cur_path
        if xpath =~ %r!^/!
          xpath[1,0] = @doc.root.name + '/'
          cur_path = '/'
        end
        xpath.gsub!(/(?!^\/)\//, '[0]/')
        xpath.gsub!(/\[\s*(\d+)\s*\]/) {|str| "[#{$1.succ}]"}
        xpath.gsub!(/\[#{ATTR_RS}(?:,#{ATTR_RS})*\]\z/) do |x|
          res = []
          attr_info = []
          $~.to_a.compact[1..-1].each do |m|
            attr_info << m
            if attr_info.size == 4
              raise DomWalkerError if attr_info[1] != attr_info[3]
              res << "@#{attr_info[0]}=#{attr_info[1..3]}"
              attr_info.clear
            end
          end
          raise DomWalkerError unless attr_info.empty?
          "[#{res.join(' and ')}]"
        end
        element = @cur_node.elements[xpath]
        raise DomWalkerError if element.nil?
        @cur_node = element
        @cur_path = "#{cur_path}/#{path}".gsub( %r!//*!, '/' )
      end
      
      def cur_values
        @cur_node.texts.collect {|text| text.to_s}
      end

      def cur_attrs
        @cur_node.attributes
      end
      
      def cur_name
        @cur_node.name
      end
      
      def size(name='')
        n = 0
        @cur_node.each_element do |element|
          n += 1 if name.empty? or name == element.name
        end
        n
      end
      
    end

    DomWalker = REXMLDomWalker

  when 'NQXML'
    class NQXMLDomWalker < BaseDomWalker
      
      def initialize(xml)
        super()
        @doc = NQXML::TreeParser.new( xml ).document
        @cur_node = @doc.rootNode
      end
      
      def change_node( path )
        
        if path =~ %r!^/!
          @cur_node = @doc.rootNode
          @cur_path = '/'
          path = path[1..-1]
        end
        @cur_path = "#{@cur_path}/#{path}".gsub( %r!//*!, '/' )
        
        path.split( '/' ).each do |name|
          
          nth = 0
          attrs = nil
          
          if name =~ %r!([^\[]*)\[([^\]]*)\]!
            name = $1
            if $2.to_i.to_s == $2
              nth = $2.to_i 
            else
              attrs = {}
              $2.split( ',' ).each do |pair|
                key, val = pair.split( '=' )
                val = val.split( '"' )[1]
                attrs[key] = val;
              end  
            end
          end
          
          changed = false
          @cur_node.children.each do |node|
            entity = node.entity
            if entity.instance_of?(NQXML::Tag) && entity.name == name
              if attrs
                matched = true
                attrs.each do |key,val|
                  matched = false if val != node.entity.attrs[key]
                end
                next unless matched
              end
              if nth == 0
                @cur_node = node
                changed = true
                break
              end
              nth -= 1
            end
          end
          
          raise DomWalkerError unless changed
          
        end
        
      end
      
      def cur_values
        @cur_node.children.collect{ |node|
          entity = node.entity
          entity.text if entity.instance_of?(NQXML::Text)
        }.compact
      end
      
      def cur_value
        cur_values.join
      end
      
      def cur_attrs
        @cur_node.entity.attrs
      end
      
      def cur_name
        @cur_node.entity.name
      end

      def size( name="" )
        n = 0
        @cur_node.children.each do |node|
          entity = node.entity
          if entity.instance_of?(NQXML::Tag)
            n += 1 if name == "" || name == entity.name
          end
        end
        return n
      end

    end

    DomWalker = NQXMLDomWalker
    
  end

end
