
require 'nqxml/treeparser'

module WebUnit

  class DomWalkerError < RuntimeError
  end

  class DomWalker
    
    def initialize( xml )
      @doc = NQXML::TreeParser.new( xml ).document
      @cur_node = @doc.rootNode
      @cur_path = '/'
    end
    
    attr_reader :doc, :cur_path
  
    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

end
