require 'html5lib/treebuilders/base'
require 'hpricot'
require 'forwardable'

module HTML5lib
module TreeBuilders
module Hpricot

class Node < Base::Node

    extend Forwardable

    def_delegators :@hpricot, :name

    attr_accessor :hpricot

    def initialize(name)
        super(name)
        @hpricot = self.class.hpricot_class.new name
    end

    def appendChild(node)
        if node.kind_of?(TextNode) and childNodes.any? and childNodes.last.kind_of?(TextNode)
            childNodes[-1].hpricot.content = childNodes[-1].hpricot.to_s + node.hpricot.to_s
        else
            childNodes << node
            hpricot.children << node.hpricot
        end
        node.parent = self
    end

    def removeChild(node)
       childNodes.delete(node)
       hpricot.children.delete_at(hpricot.children.index(node.hpricot))
       node.parent = nil
    end

    def insertText(data, before = nil)
        if before
            insertBefore(TextNode.new(data), before)
        else
            appendChild(TextNode.new(data))
        end
    end

    def insertBefore(node, refNode)
        index = childNodes.index(refNode)
        if node.kind_of?(TextNode) and index > 0 and childNodes[index-1].kind_of?(TextNode)
            childNodes[index-1].hpricot.content = childNodes[index-1].hpricot.to_s + node.hpricot.to_s
        else
            childNodes.insert(index, node)
        end
    end

    def hasContent
        childNodes.any?
    end
end

class Element < Node
    def self.hpricot_class
        ::Hpricot::Elem
    end

    def initialize(name)
        super(name)

        @hpricot = ::Hpricot::Elem.new(::Hpricot::STag.new(name))
    end

    def name
        @hpricot.stag.name
    end

    def cloneNode
        attributes.inject(self.class.new(name)) do |node, (name, value)|
            node.hpricot[name] = value
            node
        end
    end

    # A call to Hpricot::Elem#raw_attributes is built dynamically,
    # so alterations to the returned value (a hash) will be lost.
    #
    # AttributeProxy works around this by forwarding :[]= calls
    # to the raw_attributes accessor on the element start tag.
    #
    class AttributeProxy
        def initialize(hpricot)
            @hpricot = hpricot
        end
        def []=(k, v)
            @hpricot.stag.send(stag_attributes_method)[k] = v
        end
        def stag_attributes_method
            # STag#attributes changed to STag#raw_attributes after Hpricot 0.5
            @hpricot.stag.respond_to?(:raw_attributes) ? :raw_attributes : :attributes
        end
        def method_missing(*a, &b)
            @hpricot.attributes.send(*a, &b)
        end
    end

    def attributes
        AttributeProxy.new(@hpricot)
    end

    def attributes=(attrs)
        attrs.each { |name, value| @hpricot[name] = value }
    end

    def printTree(indent = 0)
        tree = "\n|#{' ' * indent}<#{name}>"
        indent += 2
        attributes.each do |name, value|
            next if name == 'xmlns'
            tree += "\n|#{' ' * indent}#{name}=\"#{value}\""
        end
        childNodes.inject(tree) { |tree, child| tree + child.printTree(indent) }
    end
end

class Document < Node
    def self.hpricot_class
        ::Hpricot::Doc
    end

    def initialize
        super(nil)
    end

    def printTree(indent = 0)
        childNodes.inject('#document') { |tree, child| tree + child.printTree(indent + 2) }
    end
end

class DocumentType < Node
    def self.hpricot_class
        ::Hpricot::DocType
    end

    def initialize(name)
        begin
            super(name)
        rescue ArgumentError # needs 3...
        end

        @hpricot = ::Hpricot::DocType.new(name, nil, nil)
    end

    def printTree(indent = 0)
        "\n|#{' ' * indent}<!DOCTYPE #{hpricot.target}>"
    end
end

class DocumentFragment < Element
    def initialize
        super('')
    end

    def printTree(indent = 0)
        childNodes.inject('') { |tree, child| tree + child.printTree(indent+2) }
    end
end

class TextNode < Node
    def initialize(data)
        @hpricot = ::Hpricot::Text.new(data)
    end

    def printTree(indent = 0)
        "\n|#{' ' * indent}\"#{hpricot.content}\""
    end
end

class CommentNode < Node
    def self.hpricot_class
        ::Hpricot::Comment
    end

    def printTree(indent = 0)
        "\n|#{' ' * indent}<!-- #{hpricot.content} -->"
    end
end

class TreeBuilder < Base::TreeBuilder
    def initialize
        @documentClass = Document
        @doctypeClass = DocumentType
        @elementClass = Element
        @commentClass = CommentNode
        @fragmentClass = DocumentFragment
    end

    def testSerializer(node)
        node.printTree
    end

    def getDocument
        @document.hpricot
    end

    def getFragment
        @document = super
        return @document.hpricot.children
    end
end

end
end
end