require 'rexml/document' 
require 'rexml/parsers/sax2parser'
require 'rexml/sax2listener'

class Element
  attr_reader :parent, :name
  attr_accessor :children, :xmlbase

  def initialize parent, name, attrs
    @parent = parent
    @xmlbase = parent.xmlbase
    @children = []
    @name = name
    @attrs = attrs
  end

  def log message, args={}
    if not @attrs
      args[:attr] = @name
    elsif not args[:element]
      args[:element] = @name
    elsif not args[:parent]
      args[:parent] = @name
    end

    @parent.send :log, message, args
  end

  def new_rule rule, name, attrs
    Rules[rule].new(self,name,attrs)
  end

  def attribute_rules name
    self.class.attribute_rules name
  end

  def element_rules *args
    self.class.element_rules *args
  end

  include REXML::SAX2Listener
  # attlistdecl(element, pairs, contents)
  # cdata(content)
  # characters(text)
  # comment(comment)
  # doctype(name, pub_sys, long_name, uri)
  # elementdecl(content)
  # end_document()
  # end_element(uri, localname, qname)
  # end_prefix_mapping(prefix)
  # entitydecl(content)
  # notationdecl(content)
  # processing_instruction(target, data)
  # start_document()
  # start_element(uri, localname, qname, attributes)
  # start_prefix_mapping(prefix, uri)
  # xmldecl(version, encoding, standalone)

end

class << Element
  def inherited sub; sub.class_eval {initialize}; end

  def initialize
    @element = {}
    @attribute = {}

    attribute "xml:base", :setxmlbase
    attribute "xml:lang", :iso639
  end

  def element symbol, *decls
    prefix = "#{caller[0].scan /(\w+)\./}:"
    @element[prefix + symbol.to_s] = decls
  end

  def attribute symbol, *decls
    @attribute[symbol.to_s] = decls
  end

  def element_rules uri, localname, qname, attributes
    prefix = Xmlns[uri]
    if prefix
      @element["#{prefix}:#{localname}"] or [:undefined_element]
    else
      [:unknown_namespace]
    end
  end

  def attribute_rules name
    @attribute[name] or [:unexpected_attribute]
  end
end

########################################################################
#                              Subclasses                              #
########################################################################

class TextElement < Element
  def initialize parent, name, attrs
    super
    @value = ''
  end

  def characters text
    @value += text
  end

  def cdata content
    @value += content
  end

  def end_element uri, localname, qname
    validate REXML::Text.unnormalize(@value)
  end

  def self.attribute_rules name
    @attribute[name] or []
  end
end

class DataElement < TextElement
  def end_element uri, localname, qname
    if @value != @value.strip
      log :UnexpectedWhitespace
      @value.strip!
    end
    validate @value
  end
end

# stub
class Cardinality < Element
  def element_rules uri, localname, qname, attributes
    []
  end

  def validate value
  end
end

REQUIRED = Cardinality.clone
MANY = Cardinality.clone

class DiscriminatedUnion < Element
  def initialize parent, name, attrs
    super
    @rules = select(attrs).map {|rule| parent.new_rule(rule, name, attrs)}
  end

  REXML::SAX2Listener.public_instance_methods.each do |method|
    define_method(method) do |*args|
      @rules.each {|rule| rule.send(method, *args)}
    end
  end  

  def element_rules *args
    @rules.collect {|rule| rule.element_rules(*args)}.flatten.uniq
  end
end
