#!/usr/bin/env ruby

require 'maruku'
require 'maruku/textile2'

$marutest_language = :markdown

#MARKER = "\n***EOF***\n"
SPLIT = %r{\n\*\*\*[^\*]+\*\*\*\n}m

def marker(x)
	"\n*** Output of #{x} ***\n"
end

def write_lines(i, j, lines, prefix, i_star)
	i = [i, 0].max
	j = [j, lines.size-1].min
	for a in i..j
		l = lines[a].gsub(/\t/,'  ')
		puts( ("%s %3d" % [prefix, a]) + 
			(a==i_star ? " -->" : "    ")+lines[a])
	end
end

# a = expected   b = found 
def equals(a, b)
	a = a.split("\n")
	b = b.split("\n")
	
	for i in 0..([a.size-1,b.size-1].max)
		la = a[i]
		lb = b[i]
		if la != lb
			puts "\n"
			
			
			write_lines(i-3, i+3, a, "expected", i )
			write_lines(i-3, i+3, b, "   found", i )
			return false
		end
	end
	return true
end

TOTEST = [:inspect,:to_html,:to_latex,:to_md,:to_s]

def run_test(filename, its_ok, verbose=true)
	# read file content
	input =  (f=File.open(filename,'r')).read; f.close
	
	output_html = File.join(File.dirname(filename), 
		File.basename(filename, File.extname(filename)) + ".html")	
	
	# split the input in sections
	
	stuff = input.split(SPLIT)
	if stuff.size == 1
		stuff[2] = stuff[0]
		stuff[0] = "Write a comment here"
		stuff[1] = "{} # params "
	end

	comment   = stuff.shift
	params_s = stuff.shift
	
	params = eval(params_s||'{}')
	if params == nil
		raise "Null params? #{params_s.inspect}"
	end
	
	markdown  = stuff.shift

#	puts "comment: #{markdown.inspect}"	
#	puts "markdown: #{markdown.inspect}"
	
	failed = []
	relaxed = []
	crashed = []
	actual = {}
	
	doc = 
		if $marutest_language == :markdown
			Maruku.new(markdown, params)
		else
			MaRuKu.textile2(markdown, params)
		end

	for s in TOTEST
		begin
			if s==:to_html
				actual[s] = doc.to_html
			else
				actual[s] = doc.send s
				raise "Methods #{s} gave nil" if not actual[s]
			end
		rescue Exception => e
			crashed << s
			actual[s] = e.inspect+ "\n"+ e.backtrace.join("\n")
			puts actual[s]
		end
	end

	File.open(output_html, 'w') do |f|
		f.write doc.to_html_document
	end

		begin
			m = Maruku.new
			d = m.instance_eval(actual[:inspect])
		rescue Exception => e
			s = e.inspect + e.backtrace.join("\n")
			raise "Inspect is not correct:\n ========\n#{actual[:inspect]}"+
			"============\n #{s}"
		end

	expected = {}
	if (stuff.size < TOTEST.size)
		$stdout.write " (first time!) "
		TOTEST.each do |x| expected[x] = actual[x] end
	else
		TOTEST.each_index do |i|
			symbol = TOTEST[i]
			expected[symbol] = stuff[i]
#			puts "symbol: #{symbol.inspect} = #{stuff[i].inspect}"
		end
	end

	m = Maruku.new
	
	
	if not its_ok.include? :inspect
		begin
			d = m.instance_eval(expected[:inspect])
	#		puts "Eval: #{d.inspect}"
			expected[:inspect] = d.inspect
		rescue Exception => e
			s = e.inspect + e.backtrace.join("\n")
			raise "Cannot eval user-provided string:\n #{expected[:inspect].to_s}"+
			"\n #{s}"
		end
	end

#	 m.instance_eval(actual[:inspect]) != m.instance_eval(expected[:inspect])	

#	actual[:inspect] = m.instance_eval(actual[:inspect]) 
#	expected[:inspect] =  m.instance_eval(expected[:inspect])


	TOTEST.each do |x|
		expected[x].strip!
		actual[x].strip!
		if not equals(expected[x], actual[x])
			if its_ok.include? x
				expected[x] = actual[x]
				$stdout.write " relax:#{x} "
				relaxed << x
			else
				actual[x] = "-----| WARNING | -----\n" + actual[x].to_s
				failed << x
			end
		end
	end
	
	f = File.open(filename, 'w')
	
	f.write comment
	f.write "\n*** Parameters: ***\n"
	f.write params_s
	f.write "\n*** Markdown input: ***\n"
	f.write markdown

	TOTEST.each do |x|
		f.write marker(x) 
		f.write expected[x]
	end
	f.write "\n*** EOF ***\n"
	
	if not failed.empty? or not crashed.empty? 
		
		f.puts "\n\n\n\nFailed tests:   #{failed.inspect} \n" 
	
		TOTEST.each do |x|
			f.write marker(x) 
			f.write actual[x]
		end

	else
		f.puts "\n\n\n\tOK!\n\n\n"
	end

	
	if false
		md_pl = markdown_pl(markdown)
		
		f.write "\n*** Output of Markdown.pl ***\n" 
		f.write md_pl
	
		f.write "\n*** Output of Markdown.pl (parsed) ***\n"
		begin 
			doc = REXML::Document.new("<div>#{md_pl}</div>",{
				:compress_whitespace=>['div','p'],
				:ignore_whitespace_nodes=>['div','p'],
				:respect_whitespace=>['pre','code']
			})
			div = doc.root
			xml =""
			div.write_children(xml,indent=1,transitive=true,ie_hack=false)
			f.write xml
		rescue Exception=>e
			f.puts "Error: #{e.inspect}"
		end
		f.close
	else
		f.write "\n*** Output of Markdown.pl ***\n" 
		f.write "(not used anymore)"
	
		f.write "\n*** Output of Markdown.pl (parsed) ***\n"
		f.write "(not used anymore)"		
	end
	
	return failed, relaxed, crashed
end

def markdown_pl(markdown)
	tmp1 = "/tmp/marutest1"
	tmp2 = "/tmp/marutest2"
	File.open(tmp1,'w') do |f| f.puts markdown end
	system "Markdown.pl < #{tmp1} > #{tmp2}"
	f = File.open(tmp2,'r')
	s = f.read
	f.close
	s
end

def passed?(args, arg)
	if args.include? arg
		args.delete arg
		true
	else
		false
	end
end

def marutest(args)
	dont_worry = []
	TOTEST.each do |x|
		arg = "ok:#{x}"
	#	puts arg
		if passed?(args, arg)
			dont_worry << x
		end
	end
	
	if passed?(args, 'ok')
		dont_worry = TOTEST.clone
	end
	
	if dont_worry.size > 0
		puts "Relaxed on #{dont_worry.inspect}"
	end
	

	failed = {}
	relaxed = {}
	
	args.each do |f|
		$stdout.write  f + ' '*(50-f.size) + " "
		$stdout.flush
		tf, tr, tcrashed = run_test(f, dont_worry)
		
		tf = tf + tcrashed

		
		if tr.size > 0
			$stdout.write " relax #{tr.inspect} "
		end
		
		if tf.size>0
			$stdout.write " failed on #{tf.inspect} "
		else
			$stdout.write " OK "
		end

		if tcrashed.size > 0
			$stdout.write "    CRASHED on #{tcrashed.inspect}"
		end

			$stdout.write "\n"		
			
		failed[f]  = tf
		relaxed[f]  = tr	
	end
	
	num_failed = 0
	failed_cat = {}

	puts "\n\n\n**** FINAL REPORT ****\n\n"

	
	if failed.size > 0
		failed.each do |file, fl|
			num_failed += fl.size
			if fl.size > 0
				puts "\t#{file}\tfailed on #{fl.inspect}"
			end
			fl.each do |x|
				failed_cat[x] = failed_cat[x] || 0
				failed_cat[x] = failed_cat[x]  + 1
			end
		end
	end

	if dont_worry.size > 0
		puts "Relaxed on #{dont_worry.inspect}"
	end

	if relaxed.size > 0
		relaxed.each do |file, r|
			if r.size > 0
				puts "\t#{file}\t\trelaxed on #{r.inspect}"
			end
		end
	end
	
	if failed_cat.size > 0
		puts "\nCategories:\n"
	
		failed_cat.each do |x, num|
			puts "\t#{x.inspect} \tfailed #{num}/#{args.size}"
		end
	end
	
	return num_failed == 0
end

if File.basename(__FILE__) == 'marutest'
	if ARGV.empty?
		puts "marutest is a tool for running Maruku's unittest."
		exit 1
	end
	ok = marutest(ARGV.clone)
	
	exit ok ? 0 : -1 
end