module Hoe::RakeHelpers

def ansi_code( *attributes )

## Create a string that contains the ANSI codes specified and return it
def ansi_code( *attributes )
	attributes.flatten!
	attributes.collect! {|at| at.to_s }
	# $stderr.puts "Returning ansicode for TERM = %p: %p" %
	#		[ ENV['TERM'], attributes ]
	return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
	attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
	# $stderr.puts "	attr is: %p" % [attributes]
	if attributes.empty?
		return ''
	else
		return "\e[%sm" % attributes
	end
end

def ask_for_confirmation( description, abort_on_decline=true )

## any non-'y' answer will fail with an error message.
## with 'y', yield to the block. If +abort_on_decline+ is +true+,
## for confirmation. If the user answers with anything that begins
## Display a description of a potentially-dangerous task, and prompt
def ask_for_confirmation( description, abort_on_decline=true )
	prompt = 'Continue?'
	# If the description looks like a question, use it for the prompt. Otherwise,
	# print it out and
	if description.strip.rindex( '?' )
		prompt = description
	else
		log description
	end
	answer = prompt_with_default( prompt, 'n' ) do |input|
		input =~ /^[yn]/i
	end
	if answer =~ /^y/i
		return yield
	elsif abort_on_decline
		error "Aborted."
		fail
	end
	return false
end

def colorize( *args )

## line-endings, color reset, etc.
## Colorize the given +string+ with the specified +attributes+ and return it, handling
def colorize( *args )
	string = ''
	if block_given?
		string = yield
	else
		string = args.shift
	end
	ending = string[/(\s)$/] || ''
	string = string.rstrip
	return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
end

def edit( filename )

## from doing so.
## Invoke the user's editor on the given +filename+ and return the exit code
def edit( filename )
	editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
	system editor, filename
	unless $?.success? || editor =~ /vim/i
		fail "Editor exited uncleanly."
	end
end

def error_message( msg, details='' )

## (white on red).
## Output the specified msg as an ANSI-colored error message
def error_message( msg, details='' )
	$stderr.puts colorize( 'bold', 'white', 'on_red' ) { msg } + details
end

def get_target_args

## Extract all the non Rake-target arguments from ARGV and return them.
def get_target_args
	args = ARGV.reject {|arg| arg =~ /^-/ || Rake::Task.task_defined?(arg) }
	return args
end

def humanize_file_list( list, indent=FILE_INDENT )

## Returns a human-scannable file list by joining and truncating the list if it's too long.
def humanize_file_list( list, indent=FILE_INDENT )
	listtext = list[0..5].join( "\n#{indent}" )
	if list.length > 5
		listtext << " (and %d other/s)" % [ list.length - 5 ]
	end
	return listtext
end

def log( *msg )

## Output a logging message
def log( *msg )
	output = colorize( msg.flatten.join(' '), 'cyan' )
	$stderr.puts( output )
end

def make_prompt_string( string )

## Make a prompt string that will always appear flush left.
def make_prompt_string( string )
	return CLEAR_CURRENT_LINE + colorize( 'bold', 'green' ) { string + ' ' }
end

def prompt( prompt_string, failure_msg="Try again." ) # :yields: response

:yields: response
## An optional failure message can also be passed in.
## test is provided, the prompt will repeat until the test returns true.
## return the user's input with leading and trailing spaces removed. If a
## Output the specified prompt_string as a prompt (in green) and
def prompt( prompt_string, failure_msg="Try again." ) # :yields: response
	prompt_string.chomp!
	prompt_string << ":" unless /\W$/.match( prompt_string )
	response = nil
	begin
		prompt = make_prompt_string( prompt_string )
		response = readline( prompt ) || ''
		response.strip!
		if block_given? && ! yield( response )
			error_message( failure_msg + "\n\n" )
			response = nil
		end
	end while response.nil?
	return response
end

def prompt_for_multiple_values( label, default=nil )

## Prompt for an array of values
def prompt_for_multiple_values( label, default=nil )
	$stderr.puts( MULTILINE_PROMPT % [label] )
	if default
		$stderr.puts "Enter a single blank line to keep the default:\n	%p" % [ default ]
	end
	results = []
	result = nil
	begin
		result = readline( make_prompt_string("> ") )
		if result.nil? || result.empty?
			results << default if default && results.empty?
		else
			results << result
		end
	end until result.nil? || result.empty?
	return results.flatten
end

def prompt_with_default( prompt_string, default, failure_msg="Try again." )

## returns true. An optional failure message can also be passed in.
## anything. If a test is provided, the prompt will repeat until the test
## substituting the given default if the user doesn't input
## Prompt the user with the given prompt_string via #prompt,
def prompt_with_default( prompt_string, default, failure_msg="Try again." )
	response = nil
	begin
		default ||= '~'
		response = prompt( "%s [%s]" % [ prompt_string, default ] )
		response = default.to_s if !response.nil? && response.empty?
		trace "Validating response %p" % [ response ]
		# the block is a validator.	 We need to make sure that the user didn't
		# enter '~', because if they did, it's nil and we should move on.	 If
		# they didn't, then call the block.
		if block_given? && response != '~' && ! yield( response )
			error_message( failure_msg + "\n\n" )
			response = nil
		end
	end while response.nil?
	return nil if response == '~'
	return response
end

def quotelist( *args )

## Return the specified args as a string, quoting any that have a space.
def quotelist( *args )
	return args.flatten.collect {|part| part =~ /\s/ ? part.inspect : part}
end

def read_command_output( cmd, *args )

## return anything written to its STDOUT.
## Run the given +cmd+ with the specified +args+ without interpolation by the shell and
def read_command_output( cmd, *args )
	# output = IO.read( '|-' ) or exec cmd, *args # No popen on some platforms. :(
	argstr = Shellwords.join( args )
	output = `#{cmd} #{argstr}`.chomp
	return output
end

def run( *cmd )

## fails. Doesn't invoke a subshell (unlike 'sh').
## Run the specified command +cmd+ with system(), failing if the execution
def run( *cmd )
	cmd.flatten!
	if cmd.length > 1
		trace( "Running:", quotelist(*cmd) )
	else
		trace( "Running:", cmd )
	end
	if Rake.application.options.dryrun
		log "(dry run mode)"
	else
		system( *cmd )
		unless $?.success?
			fail "Command failed: [%s]" % [cmd.join(' ')]
		end
	end
end

def trace( *msg )

## Output a logging message if tracing is on
def trace( *msg )
	return unless Rake.application.options.trace
	output = colorize( msg.flatten.join(' '), 'yellow' )
	$stderr.puts( output )
end