require 'rexml/element'
require 'diff/lcs'
require 'diff/lcs/hunk'
require 'hpricot'

module Hpricot # :nodoc: all
  class STag < BaseEle
    # original    
    #     def attributes_as_html
    #       if @attributes
    #         @attributes.map do |aname, aval|
    #           " #{aname}" +
    #             (aval ? "=#{html_quote(aval)}" : "")
    #         end.join
    #       end
    #     end
    
    undef attributes_as_html
    def attributes_as_html
      if @raw_attributes
         @raw_attributes.keys.sort.map do |aname|
           aval = @raw_attributes[aname]
           " #{aname}" +
             (aval ? "=#{html_quote(aval)}" : "")
         end.join
      end
    end
  end
end

module REXML # :nodoc: all
  class Attributes < Hash
    # redefine each_attribute for unified order
    def each_attribute
      keys.sort.each do |key|
        val = get_attribute(key)
        if val.kind_of? Attribute
          yield val
        else
          val.each_value { |atr| yield atr }
        end
      end
    end
  end    
end



module Amrita2 
  module TestSupport # :nodoc: all
    def assert_equal_as_xml(expected, actual)
      #assert_equal(expected.as_normalized_xml, actual.as_normalized_xml)
      assert_equal(normalize(expected), normalize(actual))
    rescue REXML::ParseException
      puts expected
      puts actual
      puts msg = diff_as_string(expected, actual)
      raise
    rescue
      msg = diff_as_string(normalize(expected),normalize(actual))
      msg.gsub!('?', ' ')
      msg = build_message "not equal as xml ", msg
      raise Test::Unit::AssertionFailedError.new(msg)
    end
    
    # copied from rspec spec/expectations/differs/default.rb
    def diff_as_string(data_old, data_new)
      context_lines = 3
      #format = :context
      format = :unified
      data_old = data_old.split(/\n/).map! { |e| e.chomp }
      data_new = data_new.split(/\n/).map! { |e| e.chomp }
      output = ""
      diffs = Diff::LCS.diff(data_old, data_new)
      return output if diffs.empty?
      oldhunk = hunk = nil  
      file_length_difference = 0
      diffs.each do |piece|
        begin
          hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, context_lines,
                                     file_length_difference)
          file_length_difference = hunk.file_length_difference      
          next unless oldhunk      
          # Hunks may overlap, which is why we need to be careful when our
          # diff includes lines of context. Otherwise, we might print
          # redundant lines.
          if (context_lines > 0) and hunk.overlaps?(oldhunk)
            hunk.unshift(oldhunk)
          else
            output << oldhunk.diff(format)
          end
        ensure
          oldhunk = hunk
          output << "\n"
        end
      end  
      #Handle the last remaining hunk
      output << oldhunk.diff(format) << "\n"
    end  

    def normalize(x)
      case x
      when String
        normalize(Hpricot.make(x))
      when Hpricot::Text
        x.to_s.strip.gsub(/\s+/, " ")
      when Hpricot::Elem
        if x.empty?
          x.stag.output("", :style => :empty)
        else
          [
            x.stag.output(""),
            normalize(x.children),
            x.etag ? x.etag.output("", :style => :end) : ''
          ].flatten.join("\n").strip
        end
      when Array
        x.collect { |e| normalize(e) }.join("\n").strip.gsub(/\>(\n\s)+/, ">\n")
      when Hpricot::BogusETag
        x.to_s
      else
        x.to_s
      end
    end

    def should_be_samexml_as(expected)
      normalize(self).should == normalize(expected)
    end
  end

  class Template
    def test_with(value, b=nil)
      set_trace(log = "")
      result = render_with(value, b)
      yield(result.dup)
    rescue StandardError
      p $!, value, result
      puts log
      raise
    end
  end
end

class Object 
  include Amrita2::TestSupport
end

if Object::const_defined?(:ActionView)

  class ActionView::Base # :nodoc: all
    def self.unregister_template_handler(extension)
      self.cache_template_extensions = false
      @@template_handlers.delete(extension)
    end
  end

  module Amrita2
    module RailsTestHelper # :nodoc: all
      def compare_result
        ActionView::Base.register_template_handler "a2html", Amrita2View::Base
        amrita2_result = yield
        ActionView::Base.unregister_template_handler "a2html"
        erb_result = yield
        assert_equal_as_xml(erb_result, amrita2_result)
        #assert_dom_equal(amrita2_result, erb_result)
      end
    end
  end

end

