module Hoe::Mercurial

def define_mercurial_tasks

## Hoe hook -- Define Rake tasks when the plugin is loaded.
def define_mercurial_tasks
	return unless File.exist?( ".hg" ) &&
		!Rake::Task.task_defined?( 'hg:checkin' )
	file COMMIT_MSG_FILE do |task|
		edit_commit_log( task.name )
	end
	namespace :hg do
		desc "Prepare for a new release"
		task :prep_release do
			uncommitted_files = get_uncommitted_files()
			unless uncommitted_files.empty?
				log "Uncommitted files:\n",
					*uncommitted_files.map {|fn| "	#{fn}\n" }
				ask_for_confirmation( "\nRelease anyway?", true ) do
					log "Okay, releasing with uncommitted versions."
				end
			end
			tags = get_tags()
			rev = get_current_rev()
			pkg_version_tag = "#{hg_release_tag_prefix}#{version}"
			# Look for a tag for the current release version, and if it exists abort
			if tags.include?( pkg_version_tag )
				error "Version #{version} already has a tag."
				fail
			end
			# Ensure that the History file contains an entry for every release
			Rake::Task[ 'check_history' ].invoke if self.check_history_on_release
			# Sign the current rev
			if self.hg_sign_tags
				log "Signing rev #{rev}"
				run 'hg', 'sign'
			end
			# Tag the current rev
			log "Tagging rev #{rev} as #{pkg_version_tag}"
			run 'hg', 'tag', pkg_version_tag
			# Offer to push
			Rake::Task['hg:push'].invoke
		end
		desc "Check for new files and offer to add/ignore/delete them."
		task :newfiles do
			log "Checking for new files..."
			entries = get_unknown_files()
			unless entries.empty?
				files_to_add = []
				files_to_ignore = []
				files_to_delete = []
				entries.each do |entry|
					action = prompt_with_default( "	 #{entry}: (a)dd, (i)gnore, (s)kip (d)elete", 's' )
					case action
					when 'a'
						files_to_add << entry
					when 'i'
						files_to_ignore << entry
					when 'd'
						files_to_delete << entry
					end
				end
				unless files_to_add.empty?
					run 'hg', 'add', *files_to_add
				end
				unless files_to_ignore.empty?
					hg_ignore_files( *files_to_ignore )
				end
				unless files_to_delete.empty?
					delete_extra_files( files_to_delete )
				end
			end
		end
		task :add => :newfiles
		desc "Pull and update from the default repo"
		task :pull do
			paths = get_repo_paths()
			if origin_url = paths['default']
				ask_for_confirmation( "Pull and update from '#{origin_url}'?", false ) do
					Rake::Task['hg:pull_without_confirmation'].invoke
				end
			else
				trace "Skipping pull: No 'default' path."
			end
		end
		desc "Pull and update without confirmation"
		task :pull_without_confirmation do
			run 'hg', 'pull', '-u'
		end
		desc "Update to tip"
		task :update do
			run 'hg', 'update'
		end
		desc "Clobber all changes (hg up -C)"
		task :update_and_clobber do
			run 'hg', 'update', '-C'
		end
		task :precheckin do
			trace "Pre-checkin hooks"
		end
		desc "Check the current code in if tests pass"
		task :checkin => [:pull, :newfiles, :precheckin, COMMIT_MSG_FILE] do
			targets = get_target_args()
			$stderr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
			ask_for_confirmation( "Continue with checkin?" ) do
				run 'hg', 'ci', '-l', COMMIT_MSG_FILE, targets
				rm_f COMMIT_MSG_FILE
			end
			Rake::Task['hg:push'].invoke
		end
		task :commit => :checkin
		task :ci => :checkin
		CLEAN.include( COMMIT_MSG_FILE )
		desc "Push to the default origin repo (if there is one)"
		task :push do
			paths = get_repo_paths()
			if origin_url = paths['default']
				ask_for_confirmation( "Push to '#{origin_url}'?", false ) do
					Rake::Task['hg:push_without_confirmation'].invoke
				end
			else
				trace "Skipping push: No 'default' path."
			end
		end
		desc "Push to the default repo without confirmation"
		task :push_without_confirmation do
			run 'hg', 'push'
		end
	end
	# Add a top-level 'ci' task for checkin
	desc "Check in your changes"
	task :ci => 'hg:checkin'
	# Hook the release task and prep the repo first
	task :prerelease => 'hg:prep_release'
	desc "Check the history file to ensure it contains an entry for each release tag"
	task :check_history do
		log "Checking history..."
		missing_tags = get_unhistoried_version_tags()
		unless missing_tags.empty?
			abort "%s needs updating; missing entries for tags: %p" %
				[ self.history_file, missing_tags ]
		end
	end
rescue ::Exception => err
	$stderr.puts "%s while defining Mercurial tasks: %s" % [ err.class.name, err.message ]
	raise
end

def get_unhistoried_version_tags( include_pkg_version=true )

## in the history file.
## Read the list of tags and return any that don't have a corresponding section
def get_unhistoried_version_tags( include_pkg_version=true )
	prefix = self.hg_release_tag_prefix
	tag_pattern = /#{prefix}\d+(\.\d+)+/
	release_tags = get_tags().grep( /^#{tag_pattern}$/ )
	release_tags.unshift( "#{prefix}#{version}" ) if include_pkg_version
	IO.readlines( self.history_file ).each do |line|
		if line =~ /^(?:h\d\.|#+|=+)\s+(#{tag_pattern})\s+/
			trace "  found an entry for tag %p: %p" % [ $1, line ]
			release_tags.delete( $1 )
		else
			trace "  no tag on line %p" % [ line ]
		end
	end
	return release_tags
end

def initialize_mercurial

## Set up defaults
def initialize_mercurial
	# Follow semantic versioning tagging specification (http://semver.org/)
	self.hg_release_tag_prefix    = "v"
	self.hg_sign_tags             = false
	self.check_history_on_release = false
	minor_version = VERSION[ /^\d+\.\d+/ ]
	self.extra_dev_deps << ['hoe-mercurial', "~> #{minor_version}"] unless
		self.name == 'hoe-mercurial'
end