# This application is free software; you can redistribute it and/or
# modify it under the terms of the Ruby license defined in the
# COPYING file.
#
# Copyright (C) 2005 Martin DeMello. All rights reserved.
#
# This class is mostly copy & paste from ri_formatter.rb.
# except that it always returns a string and does not print anything.
class FoxTextFormatter
	# define all possible styles
	STYLE_NORMAL = :STYLE_NORMAL
	STYLE_BOLD = :STYLE_BOLD
	STYLE_CLASS = :STYLE_CLASS
	STYLE_H1 = :STYLE_H1
	STYLE_H2 = :STYLE_H2
	STYLE_H3 = :STYLE_H3
	STYLE_TELETYPE = :STYLE_TELETYPE
	STYLE_CODE = :STYLE_CODE
	STYLE_EMPHASIS = :STYLE_EMPHASIS
	
	attr_reader :indent
	attr_accessor :width
	
	# whenever text should be printed/added/shown, proc is called with the text as the argument.
	def initialize(width, indent, &proc)
		@width = width
		@indent = indent
		@proc = proc
	end
	
	######################################################################
	
	def draw_line(label=nil)
		len = @width
		len -= (label.size+1) if label
		len = [0, len].max
		@proc.call("-"*len)
		if label
			@proc.call(" ")
			@proc.call(label, STYLE_CLASS)
		end
		@proc.call("\n")
	end
	
	def display_params(method)
		params = method.params
		
		if params[0] == ?(
			if method.is_singleton
				params = method.full_name + params
			else
				params = method.name + params
			end
		end
		params.split(/\n/).each do |param|
			@proc.call(param+"\n", STYLE_BOLD)
		end
	end	
	
	def wrap(txt,  prefix=@indent, linelen=@width)
		return if !txt || txt.empty?
		@proc.call(prefix, STYLE_EMPHASIS)
		conv_markup(txt, prefix, linelen)
=begin		
		textLen = linelen - prefix.length
		patt = Regexp.new("^(.{0,#{textLen}})[ \n]")
		next_prefix = prefix.tr("^ ", " ")
		
		res = []
		
		while work.length > textLen
			if work =~ patt
				res << $1
				work.slice!(0, $&.length)
			else
				res << work.slice!(0, textLen)
			end
		end
		res << work if work.length.nonzero?
		@proc.call(prefix +  res.join("\n" + next_prefix) + "\n")
=end
	end
	
	######################################################################
	
	def blankline
		@proc.call("\n")
	end
	
	######################################################################
	
	# called when we want to ensure a nbew 'wrap' starts on a newline
	# Only needed for HtmlFormatter, because the rest do their
	# own line breaking
	
	def break_to_newline
	end
	
	######################################################################
	
	def bold_print(txt)
		@proc.call(txt, STYLE_BOLD)
	end
	
	######################################################################
	
	def raw_print_line(txt)
		@proc.call(txt)
	end
	
	######################################################################
	
	# convert HTML entities back to ASCII
	def conv_html(txt)
		txt.
		gsub(/&gt;/, '>').
		gsub(/&lt;/, '<').
		gsub(/&quot;/, '"').
		gsub(/&amp;/, '&')	
	end
	
	# convert markup into display form
	def conv_markup(txt, prefix, linelen)
		
		# this code assumes that tags are not used inside tags
		pos = 0
		old_pos = 0
		style = STYLE_NORMAL
		current_indent = prefix.size
		while pos = txt.index(%r{(<tt>|<code>|<b>|<em>|</tt>|</code>|</b>|</em>)}, old_pos)			
			new_part = txt[old_pos...pos]
			@proc.call(new_part, style)
			
			# get tag name
			old_pos = txt.index(">", pos) + 1
			style = case txt[(pos+1)...(old_pos-1)]
				when "tt"
					STYLE_TELETYPE
				when "code"
					STYLE_CODE
				when "b"
					STYLE_BOLD
				when "em"
					STYLE_EMPHASIS
				else
					# closing or unknown tags
					STYLE_NORMAL
				end
		end
		@proc.call(txt[old_pos...txt.size], style)
		@proc.call("\n")
	end
	
	######################################################################
	
	def display_list(list)
		case list.type		
		when SM::ListBase::BULLET 
			prefixer = proc { |ignored| @indent + "*   " }
		
		when SM::ListBase::NUMBER,
			SM::ListBase::UPPERALPHA,
			SM::ListBase::LOWERALPHA
		
			start = case list.type
				when SM::ListBase::NUMBER      then 1
				when SM::ListBase::UPPERALPHA then 'A'
				when SM::ListBase::LOWERALPHA then 'a'
				end
			prefixer = proc do |ignored|
				res = @indent + "#{start}.".ljust(4)
				start = start.succ
				res
			end
		
		when SM::ListBase::LABELED
			prefixer = proc do |li|
				li.label
			end
			
		when SM::ListBase::NOTE
			longest = 0
			list.contents.each do |item|
				if item.kind_of?(SM::Flow::LI) && item.label.length > longest
					longest = item.label.length
				end
			end
			
			prefixer = proc do |li|
				@indent + li.label.ljust(longest+1)
			end
			
		else
			fail "unknown list type"
		end
			
		list.contents.each do |item|
			if item.kind_of? SM::Flow::LI
				prefix = prefixer.call(item)
				display_flow_item(item, prefix)
			else
				display_flow_item(item)
			end
		end
	end
	
	######################################################################
	
	def display_flow_item(item, prefix=@indent)
		case item
		when SM::Flow::P, SM::Flow::LI
			wrap(conv_html(item.body), prefix)
			blankline
		
		when SM::Flow::LIST
			display_list(item)
		
		when SM::Flow::VERB
			display_verbatim_flow_item(item, @indent)
		
		when SM::Flow::H
		        # bug #2634: it looks like in 1.8.3 a H element is sometimes
                        # passed as a string instead of an array so check that case
		        if item.text.kind_of? Array
			  h_text = item.text.join
			else
			  h_text = item.text
			end
			display_heading(conv_html(h_text), item.level, @indent)
		
		when SM::Flow::RULE
			draw_line
			
		when String
			wrap(conv_html(item), prefix)
		
		else
			fail "Unknown flow element: #{item.class}"
		end
	end
	
	######################################################################
	
	def display_verbatim_flow_item(item, prefix=@indent)
		item.body.split(/\n/).each do |line|
			@proc.call(@indent)
			@proc.call(conv_html(line))
			@proc.call("\n")
		end
		blankline
	end
	
	######################################################################
	
	def display_heading(text, level, indent)
		case level
		when 1
			@proc.call(text, STYLE_H1)
			@proc.call("\n")
		
		when 2
			@proc.call(text, STYLE_H2)
			@proc.call("\n")
			
		else
			@proc.call(indent)
			@proc.call(text, STYLE_H3)
			@proc.call("\n")
		end
	end
	
	
	def display_flow(flow)
		flow.each do |f|
			display_flow_item(f)
		end
	end
end
