require 'rexml/document'
require 'rexml/streamlistener'

require 'nitro/element'
require "glue/html"

module Nitro

# A compiler that handles the processing of Elements

class Elements # :nodoc: all
  
  class Listener # :nodoc: all
    include REXML::StreamListener
    
    attr_accessor :buffer
    attr_accessor :stack
    
    def initialize
      super
      @buffer = ''
      @stack = []
    end
    
    PREFIX_RE = /^#{Element.prefix}:/
    CAPITALIZED_RE = /^[A-Z]/
    
    def tag_start(name, attributes)    
      # check if the name starts with the element prefix, or
      # is capitalized.
      
      if name =~ PREFIX_RE or name =~ CAPITALIZED_RE    
        name = name.split(':')[1].camelize if name =~ PREFIX_RE

        # First try to use Nitro::Element::xxx then ::xxx
                
        if klass = Nitro::Element.const_get(name) || Object.const_get(name)
          unless klass.ancestors.include? Nitro::Element
            if Element.auto_extend
              klass.send(:include, Nitro::ElementMixin)
            else
              Logger.error "Invalid element class '#{name}', does not extend Nitro::Element"
            end
          end
        else
          Logger.error "The class of this element tag '#{name}' does not exist"
        end
                        
        if klass and obj = klass.new        
          attributes.each do | k, v | 
            obj.instance_variable_set("@#{k}", v)
          end        
          
          @stack.push [obj, @buffer, @parent]
          
          @buffer = obj._text
          @parent.add_child(obj) if @parent
          
          @parent = obj
        end
      else # This is a static element.
        attrs = []
        
        attributes.each do | k, v | 
          attrs << %|#{k}="#{v}"|
        end
        
        attrs = attrs.empty? ? nil : " #{attrs.join(' ')}"
        
        @buffer << "<#{name}#{attrs}>"
      end
    end

    def tag_end(name)    
      # check if the name starts with the element prefix, or
      # is capitalized.
      if name =~ PREFIX_RE or name =~ CAPITALIZED_RE    
         name = name.split(':')[1].camelize if name =~ PREFIX_RE
        obj, @buffer, @parent = @stack.pop
        @buffer << obj.render
      else
        @buffer << "</#{name}>"
      end
    end
    
    def text(str)
      @buffer << str
    end
    
    def instruction(name, attributes)
      @buffer << "<?#{name}#{attributes}?>"
    end

    def comment(c)
      unless Template.strip_xml_comments
        @buffer << "<!--#{c}-->"
      end
    end
  end
  
  class << self
    def parse(source)
      self.new.parse(source)
    end
    
    def transform(source, compiler)
      self.new.transform(source, compiler)
    end
  end

  # Expand the elemens found in source.
  #--
  # gmosx, FIXME: optimize this, how?
  # gmosx, FIXME: this is a hack fix, improve.
  # TODO:farms why is cleanup called this many times?!?!? ... waste of gsubs
  #++
    
  def transform(source, compiler)
    listener = Listener.new

    REXML::Document.parse_stream(source, listener)
    
    return listener.buffer
  end
end

# An (old) alias.

unless const_defined? :ElementProcessor
   ElementProcessor = Elements
   ElementCompiler = Elements
end

end

# * George Moschovitis <gm@navel.gr>
# * Chris Farmiloe <chris.farmiloe@farmiloe.com>