// Copyright (c) 2011, Jens Peter Secher <jpsecher@gmail.com>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

// This implementation was inspired by John Goerzen's hg-buildpackage.

class MercurialBuildPackage
{
	public static function main()
	{
		logger = new Script();
		var usage = "Usage: " + Constants.buildPackage() + " [-V|--version]"
		+ " [-v|--verbose] [-d|--no-check-dependencies]"
		+ " [-s|-sa|--include-source] [-f|--from-version changelogversion]"
		+ " [-c|--configfile pbuilderconfigfile]"
		;
		var options = new GetPot( Sys.args() );
		Util.maybeReportVersion( options, usage );
		// Collect verbosity options.
		while( options.got( ["--verbose","-v"] ) ) ++logger.verbosity;
		// Create a build-log file.
		var dpkgArch = Process.runButExitOnError
		(
			"dpkg-architecture", ["-qDEB_HOST_ARCH"], logger
		);
		if( ! dpkgArch.hasNext() )
		{
			Util.die( "Could not determine architecture." );
		}
		var arch = dpkgArch.next();
		// Check for presence of Mercurial directory.
		Mercurial.exitIfNoWorkingDirectory( "." );
		// Extract package information from the changelog file.
		var changeLog = DebianChangeLog.parseCurrentChangeLog( logger );
		var fileNameBase = "../" + changeLog.source + "_"
			+ changeLog.version.archiveVersion();
		var archFileName = fileNameBase + "_" + arch;
		var logFileName = archFileName + ".build";
		logger.setLogFile( logFileName );
		// Find out which branch we are on.
		var branch = Process.runButExitOnError( "hg", ["branch"], logger );
		if( ! branch.hasNext() ) Util.die( "No branch information?" );
		logger.info( 1, "Branch " + branch.next() );
		logger.info( 2, "Building for architecture " + arch );
		// Use pbuilder?
		var sourceOnly = options.got( ["-S","--source-only"] );
		// Use pbuilder?
		var pbuilderConfig : String = null;
		if( options.got( ["-c","--configfile"] ) )
		{
			pbuilderConfig = options.next();
			if( pbuilderConfig == null ) Util.die( usage );
			// No point in building if source only
			if( sourceOnly ) pbuilderConfig = null;
		}
		// Check for proper build dependencies.
		var checkDepends = ! options.got( ["-d","--no-check-dependencies"] );
		if( pbuilderConfig != null && ! checkDepends )
		{
			Util.die
			(
				"--no-check-dependencies does not work with pbuilder."
			);
		}
		if( pbuilderConfig == null && checkDepends )
		{
			Process.runButExitOnError( "dpkg-checkbuilddeps", [], logger );
		}
		var includeSource = options.got( ["-s","-sa","--include-source"] );
		var fromVersion : String = null;
		if( options.got( ["-f","--from-version"] ) )
		{
			fromVersion = options.next();
			if( fromVersion == null ) Util.die( usage );
		}
		Util.exitOnExtraneousArguments( options, usage );
		// Exit if there is no upstream source tarball(s).
		checkUpstreamConsistency( changeLog );
		// Make sure debian/rules is executable.
		Process.runButExitOnError( "chmod", ["+x","debian/rules"], logger );
		// Call clean target.
		logger.info( 1, "Starting debian/rules clean." );
		Process.runButExitOnError
		(
			Constants.fakeroot(), ["debian/rules", "clean"], logger, 1
		);
		// TODO: make the above say this on error:
		//Util.die( "All build-depends for clean need to be installed." );
		// Build source package excluding Mercurial stuff.  dpkg-source itself
		// will exclude Quilt directories.
		var cwd = Sys.getCwd();
		var dpkgArgs = Constants.tarExclude().concat( [ "-i.hg", "-b", cwd ] );
		logger.info( 1, "Starting dpkg-source " + dpkgArgs.join(" ") + " in .." );
		Process.runButExitOnError ( "dpkg-source", dpkgArgs, logger, 0, ".." );
		// Use pbuilder?
		if( sourceOnly )
		{
			// nothing to do
		}
		else if( pbuilderConfig != null )
		{
			pbuilderBuild( pbuilderConfig, fileNameBase + ".dsc" );
		}
		else
		{
			regularBuild( includeSource, fromVersion, archFileName);
		}
		Sys.exit( 0 );
	}

	//
	// Exits the program if the changelog says that this package should have an
	// upstream source but there is no corresponding tarball.
	//
	static function checkUpstreamConsistency( changeLog : DebianChangeLog )
	{
		if( changeLog.version.debianRevision == null )
		{
			logger.info( 2, "Building native package." );
			return;
		}
		// Check existance of an upstream main tarball.
		var mainTarballStem = "../" + changeLog.source + "_" +
			changeLog.version.upstream + ".orig.tar.";
		for( extension in ["gz","bz2","lzma","xz"] )
		{
			var tarball = mainTarballStem + extension;
			if( FileUtil.fileExists( tarball ) )
			{				
				logger.info( 3, "Located upstream tarball " + tarball );
				return;
			}
		}
		Util.dies
		( [
			"No upstream tarball " + mainTarballStem  + "{gz,bz2,lzma,xz} located.",
			"Use " + Constants.pristineTar() + " to extract the tarball(s)."
		] );
	}
	
	//
	// Use pbuilder to build the package.
	//
	static function pbuilderBuild( configFileName : String, dscFileName : String )
	{
		var pbuild =
		[
			"/usr/sbin/pbuilder", "--build", "--configfile", configFileName, dscFileName
		];
		logger.info( 0, "Starting " + pbuild.join(" ") );
		Process.runButExitOnError( Constants.sudo(), pbuild, logger, 0 );
	}
	
	//
	// Use debian/rules and dpkg-genchanges to build the package.
	//
	static function regularBuild
	(
		includeSource : Bool,
		fromVersion : String,
		archFileName : String
	) {
		logger.info( 1, "Starting debian/rules build." );
		Process.runButExitOnError( "debian/rules", ["build"], logger, 0 );
		logger.info( 1, "Starting debian/rules binary." );
		Process.runButExitOnError
		(
			Constants.fakeroot(), ["debian/rules","binary"], logger, 0
		);
		// Use dpkg-genchanges to generate changes file.
		var changesFileName = archFileName + ".changes";
		var changesArgs = [];
		if( includeSource ) changesArgs.push( "-sa" );
		if( fromVersion != null ) changesArgs.push( "-v" + fromVersion );
		logger.info( 1, "Starting dpkg-genchanges " + changesArgs.join(" ") );
		var changes = Process.runButExitOnError
		(
			"dpkg-genchanges", changesArgs, logger
		);
		var changesFile = sys.io.File.write( changesFileName, false );
		for( line in changes ) changesFile.writeString( line + "\n" );
	}

	static var logger : Script;
}
