module Hpricot
  # :stopdoc:

  class Doc
    attr_accessor :children
    def initialize(children)
      @children = children ? children.each { |c| c.parent = self }  : []
    end
    def output(out, opts = {})
      @children.each do |n|
        n.output(out, opts)
      end
      out
    end
    def altered!; end
  end

  class BaseEle
    attr_accessor :raw_string, :parent
    def html_quote(str)
      "\"" + str.gsub('"', '\\"') + "\""
    end
    def if_output(opts)
      if opts[:preserve] and not @raw_string.nil?
        @raw_string
      else
        yield opts
      end
    end
    def pathname; self.name end
    def altered!
      @raw_string = nil
    end
    def self.alterable(*fields)
      attr_accessor(*fields)
      fields.each do |f|
        define_method("#{f}=") do |v|
          altered!
          instance_variable_set("@#{f}", v)
        end
      end
    end
  end

  class Elem
    attr_accessor :stag, :etag, :children
    def initialize(stag, children=nil, etag=nil)
      @stag, @etag = stag, etag
      @children = children ? children.each { |c| c.parent = self }  : []
    end
    def empty?; @children.empty? end
    [:name, :attributes, :parent, :altered!].each do |m|
      [m, "#{m}="].each { |m2| define_method(m2) { |*a| [@etag, @stag].inject { |_,t| t.send(m2, *a) if t and t.respond_to?(m2) } } }
    end
    def to_plain_text
      if self.name == 'br'
        "\n"
      elsif self.name == 'p'
        "\n\n" + super + "\n\n"
      elsif self.name == 'a' and self.has_attribute?('href')
        "#{super} [#{self['href']}]"
      elsif self.name == 'img' and self.has_attribute?('src')
        "[img:#{self['src']}]"
      else
        super
      end
    end
    def pathname; self.name end
    def output(out, opts = {})
      if empty? and ElementContent[@stag.name] == :EMPTY
        @stag.output(out, opts.merge(:style => :empty))
      else
        @stag.output(out, opts)
        @children.each { |n| n.output(out, opts) }
        if @etag
          @etag.output(out, opts)
        elsif !opts[:preserve]
          ETag.new(@stag.name).output(out, opts)
        end
      end
      out
    end
  end

  class STag < BaseEle
    def initialize(name, attributes=nil)
      @name = name.downcase
      @attributes = {}
      if attributes
        @attributes = attributes.inject({}) { |hsh,(k,v)| hsh[k.downcase] = v; hsh }
      end
    end
    alterable :name, :attributes
    def attributes_as_html
      if @attributes
        @attributes.map do |aname, aval|
          " #{aname}" +
            (aval ? "=#{html_quote(aval)}" : "")
        end.join
      end
    end
    def output(out, opts = {})
      out <<
        if_output(opts) do
          "<#{@name}#{attributes_as_html}" +
            (opts[:style] == :empty ? " /" : "") +
            ">"
        end
    end
  end

  class ETag < BaseEle
    def initialize(qualified_name)
      @name = qualified_name
    end
    alterable :name
    def output(out, opts = {})
      out <<
        if_output(opts) do
          "</#{@name}>"
        end
    end
  end

  class BogusETag < ETag
    def output(out, opts = {}); out << if_output(opts) { '' }; end
  end

  class Text < BaseEle
    def initialize(text)
      @content = text
    end
    alterable :content
    def pathname; "text()" end
    alias_method :inner_text, :content
    alias_method :to_plain_text, :content
    def output(out, opts = {})
      out <<
        if_output(opts) do
          @content
        end
    end
  end

  class CData < Text
    alias_method :inner_text, :content
    alias_method :to_plain_text, :content
    def output(out, opts = {})
      out <<
        if_output(opts) do
          "<![CDATA[#@content]]>"
        end
    end
  end

  class XMLDecl < BaseEle
    def initialize(version, encoding, standalone)
      @version, @encoding, @standalone = version, encoding, standalone
    end
    alterable :version, :encoding, :standalone
    def pathname; "xmldecl()" end
    def output(out, opts = {})
      out <<
        if_output(opts) do
          "<?xml version=\"#{@version}\"" +
            (@encoding ? " encoding=\"#{encoding}\"" : "") +
            (@standalone != nil ? " standalone=\"#{standalone ? 'yes' : 'no'}\"" : "") +
            "?>"
        end
    end
  end

  class DocType < BaseEle
    def initialize(target, pubid, sysid)
      @target, @public_id, @system_id = target, pubid, sysid
    end
    alterable :target, :public_id, :system_id
    def pathname; "doctype()" end
    def output(out, opts = {})
      out <<
        if_output(opts) do
          "<!DOCTYPE #{@target} " +
            (@public_id ? "PUBLIC \"#{@public_id}\"" : "SYSTEM") +
            (@system_id ? " #{html_quote(@system_id)}" : "") + ">"
        end
    end
  end

  class ProcIns < BaseEle
    def initialize(target, content)
      @target, @content = target, content
    end
    def pathname; "procins()" end
    alterable :target, :content
    def output(out, opts = {})
      out << 
        if_output(opts) do
          "<?#{@target}" +
           (@content ? " #{@content}" : "") +
           "?>"
        end
    end
  end

  class Comment < BaseEle
    def initialize(content)
      @content = content
    end
    def pathname; "comment()" end
    alterable :content
    def output(out, opts = {})
      out <<
        if_output(opts) do
          "<!--#{@content}-->"
        end
    end
  end

  # :startdoc:
end
