#!/usr/bin/env ruby
#
#	Create and run the ant script to deploy a launchpad.
#
#	The build.xml file is updated if:
#	- the payload configuration is changed
#	- the serverSlot definition changes (ports, etc)
#	- the launchpad definition changes (ports, etc)
#	- a branch is specified, and a commit has been made to the branch (i.e. the has for the HEAD has changed)
#
#	To determine these things, a summary of all these things is written to the launchpad
#	directory. If the summary file needs to change, then the build.xml is recreated.
#
#	This program is also responsible for creating and updating cached GIT repositories for each
#	project by fetching from remote	git repositories. This would be done in build.xml, except we
#	need to know when the repository changes, so we can recreate build.xml. The cached repositories
#	are bare repsitories (no working area) named such as extension-project/myproject/git-repository.
#
#
# Prerequisite  environment variables:
#       TTC_CONFIG, something like /tooltwist/tooltwist_X_Z/devel/ttc/config
#       TTC_DEPLOY, something like /ControllerV8
#
#
require 'net/http'
require 'rubygems'
require 'xmlsimple'
require 'fileutils'
require "#{File.dirname(__FILE__)}/lib_trollop"   # A tidy little command line argument parser
require 'net/http'
require 'socket'

#print File.dirname(__FILE__)

# Include the plugins for each payload type and each server type
#require "#{File.dirname(__FILE__)}/payloadType_webdesign"
#require "#{File.dirname(__FILE__)}/payloadType_tomcat6"
#require "#{File.dirname(__FILE__)}/payloadType_tomcat7"
#require "#{File.dirname(__FILE__)}/payloadType_jars"
#require "#{File.dirname(__FILE__)}/payloadType_jarsInGit"
#require "#{File.dirname(__FILE__)}/payloadType_tooltwist"
#require "#{File.dirname(__FILE__)}/payloadType_extension"
#require "#{File.dirname(__FILE__)}/payloadType_extension_fulljar"
#require "#{File.dirname(__FILE__)}/payloadType_extension_jar"
require "#{File.dirname(__FILE__)}/controller_mail"
require "#{File.dirname(__FILE__)}/controller_misc"
require "#{File.dirname(__FILE__)}/controller_phase"
require "#{File.dirname(__FILE__)}/controller_launchpad"

$OLD_VERSION = false;

# If there's less than 'NOT_MANY_NAVPOINTS' navpoints to be generated, do the entire generate in a single thread.
NOT_MANY_NAVPOINTS = 50

# Try to process 'NAVPOINTS_PER_THREAD' navpoints in each thread, however if 'MAX_THREADS'
# would be exceeded, it may be necessary to handle more navpoints in each thread.
NAVPOINTS_PER_THREAD = 50
MAX_THREADS = 50

# This value is replaced in the filter template, with a list of navpoints. 
FILTER_TEMPLATE_NAVPOINT_VARIABLE = "@NAVPOINTS@"

#
#	Prepare a launchpad for building. This will check:
# - git repositories are up to date.
# - the build file exists, and matches the current configuration. 
#
def preBuild(launchpad_name)
  
  ControllerMisc.setupGlobals(launchpad_name)

	#
	#	Get the modification time of build.xml
	#
	#if File.exist?($BUILD_FILE)
	#	buildfileTime = File.mtime($BUILD_FILE)
	#else
	#	buildfileTime = 0
	#end
	#print "buildfileTime=#{buildfileTime}\n"
	summary =  "WARNING: DO NOT MODIFY THIS FILE\n"
	summary << "\n"
	summary << "--------------------------------------------------------------------------------------------------\n"
	summary << "This file is created by #{$THIS_SCRIPT} and provides a summary of the configuration supported\n"
	summary << "by build.xml, based on details from the launchpadSlot, serverSlot, the launchpad config\n"
	summary << "and the required versions of the projects. Any time this file gets changed, the build.xml\n"
	summary << "file will be recreated at the same time.\n"
	summary << "\n"
	summary << "If you wish to force build.xml to be recreated, just delete this file and run #{$THIS_SCRIPT}.\n"
	summary << "--------------------------------------------------------------------------------------------------\n"

#  launchpadDef = 
  #ControllerMisc.loadLaunchpadDetails(launchpad_name)
  $LAUNCHPAD_TYPE = 'test'
  summary << "\n"
  summary << "Launchpad #{launchpad_name}:\n"
  summary << " $LAUNCHPAD_TYPE=#{$LAUNCHPAD_TYPE}\n"

	#
	#	Get details of the generator server
	#
  ControllerMisc.loadGeneratorSlotDetails(launchpad_name) or bomb "Launchpad '#{launchpad_name}' does not have a generator slot assigned"
  summary << "\n"
  summary << "Generator slot #{$GENERATOR_SLOT_NAME}:\n"
  summary << " description=#{$GENERATOR_DESCRIPTION}\n"
  summary << " httpPort=#{$GENERATOR_HTTP_PORT}\n"
  summary << " httpsPort=#{$GENERATOR_HTTPS_PORT}\n"
  summary << " serverPort=#{$GENERATOR_SERVER_PORT}\n"
  summary << " ajpPort=#{$GENERATOR_AJP_PORT}\n"
  summary << " disable=#{$GENERATOR_DISABLE}\n"
  summary << " installTomcat=#{$GENERATOR_INSTALL_TOMCAT}\n"
  summary << " imagemagickHome=#{$GENERATOR_IMAGEMAGICK_HOME}\n"

	#
	#	Get details of the server
	#
  ControllerMisc.loadServerSlotDetails(launchpad_name) or bomb "Launchpad '#{launchpad_name}' does not have a server slot assigned"
	#print "serverDef=#{serverDef}\n"
  summary << "\n"
  summary << "Server slot #{$PRODUCTION_SLOT_NAME}:\n"
  summary << " description = #{$PRODUCTION_DESCRIPTION}\n"
  summary << " serverHome = #{$PRODUCTION_SERVER_HOME}\n"
  summary << " webappName = #{$PRODUCTION_WEBAPP_NAME}\n"
  summary << " httpPort = #{$PRODUCTION_HTTP_PORT}\n"
  summary << " httpsPort = #{$PRODUCTION_HTTPS_PORT}\n"
  summary << " serverPort = #{$PRODUCTION_SERVER_PORT}\n"
  summary << " ajpPort = #{$PRODUCTION_AJP_PORT}\n"
  summary << " webappsDir = #{$PRODUCTION_WEBAPPS_DIR}\n"
  summary << " tomcatHome = #{$PRODUCTION_TOMCAT_HOME}\n"
  summary << " installCmd = #{$PRODUCTION_INSTALL_CMD}\n"
  summary << " installMethod = #{$PRODUCTION_INSTALL_METHOD}\n"
  summary << " server = #{$PRODUCTION_SERVER}\n"
  summary << " username = #{$PRODUCTION_USERNAME}\n"
  summary << " password = #{$PRODUCTION_PASSWORD}\n"
  summary << " installTomcat = #{$PRODUCTION_INSTALL_TOMCAT}\n"
  summary << " imagemagickHome = #{$PRODUCTION_IMAGEMAGICK_HOME}\n"
  summary << " sshPort = #{$PRODUCTION_SSH_PORT}\n"
  summary << " fipPort = #{$PRODUCTION_FIP_PORT}\n"

	#
	#	Load the deployment configuration (i.e. which payloads run on the server)
	#
	deploymentConfig = ControllerMisc.loadPayloadDetails(launchpad_name)
  config_file = ControllerMisc.deploymentConfigFile(launchpad_name)
  print "\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n"
  print "\#\#\#\#\n"
  print "\#\#\#\#     PAYLOAD VERSIONS\n"
  print "\#\#\#\#\n"
	deploymentConfig['payload'].each do |payload|
		layer = xmlValue(config_file, payload, 'layer')
		name = xmlValue(config_file, payload, 'name')
		version = xmlValue(config_file, payload, 'version', true)
    versionType = xmlValue(config_file, payload, 'versionType', true)
		summary << "\n"
		summary << "Payload #{layer}/#{name}:\n"
		summary << " version=#{version}\n"
		summary << " versionType=#{versionType}\n"
    print "\#\#\#\#     #{layer}/#{name}:\n"
    print "\#\#\#\#         #{versionType} '#{version}'\n"
    print "\#\#\#\#\n"
	end
	print "\#\#\#\#\n"
	print "\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n"

	#
	#	Check the payloads
	#
	checkPayloads deploymentConfig

	#
	#	See if the existing summary file is different
	#
	need_to_create_build_xml = false
	if not File.exists?($BUILD_FILE)
		# no build file
		need_to_create_build_xml = true
		print "Creating #{$BUILD_FILE} (no build file)\n\n"
	elsif not File.exists?($SUMMARY_FILE)
		# no summary file
		need_to_create_build_xml = true
		print "Creating #{$BUILD_FILE} (no summary file)\n\n"
		print "#{$SUMMARY_FILE}\n"
	else
		# have build and summary file
		# see if the summary file is different
		existingSummary = File.read($SUMMARY_FILE)
		if existingSummary != summary
			need_to_create_build_xml = true
			print "Creating #{$BUILD_FILE} (summary file changed)\n\n"
		else
			print "Existing #{$BUILD_FILE} is okay.\n\n"
		end
	end


	#
	#	Prepare the payloads - update the git repositories, etc
	#
	checkPayloads deploymentConfig
	preparePayloads deploymentConfig

	#
	#	See if we need to create a new build.xml
	#
	#if need_to_create_build_xml
	if true
		#print "Creating build.xml"

		#	Write the summary file
		sFile = File.new($SUMMARY_FILE, "w")
		sFile.write(summary)
		sFile.close

		#	Create the build.xml file
		script = createBuildXml deploymentConfig
		bFile = File.new($BUILD_FILE, "w")
		bFile.write(script)
		bFile.close
	else
		#print "build.xml not changed"
	end

	#data['generatorSlot'].each do |item|
	#	print item['httpPort']
	#   item.sort.each do |k, v|
	#      if ["Title", "Url"].include? k
	#         print "#{v[0]}" if k=="Title"
	#         print " => #{v[0]}\n" if k=="Url"
	#      end
	#   end
	#end
end

def xmlValue(filename, xml, element_name, is_optional=true)
	val = xml[element_name]
	if val == nil and not is_optional
		bomb("'#{element_name}' not defined in #{filename}")
	end
	val = val[0].to_s.strip
	if val == "" and not is_optional
		bomb("Empty value for '#{element_name}' in #{filename}")
	end
	return val
end

#
# Similar to 'checkPayload', but for each payload in the deploymentConfig.
#
def checkPayloads(deploymentConfig)
	deploymentConfig['payload'].each do |payload|
	  checkPayload(payload)
	end
end

#
#   1. Check the payload exists.
#   2. Get the payload type.
#   3. Add extra details to the payload definition.
#
# Before calling this function, the following values must be set in the payload object:
#      payload['layer']
#      payload['name']
#
def checkPayload(payload)
  layer = payload['layer'].to_s
  name = payload['name'].to_s
  #version = payload['version'].to_s
  #versionType = payload['versionType'].to_s

  payload_dir = getPayloadDirectory(payload)

  # Check the payload directory exists
  if not File.exist?(payload_dir)
    bomb "Unknown payload #{layer}/#{name}."
  end

  # Get the payload type from payload.xml
  payload_def_file = "#{payload_dir}/payload.xml"
  if not File.exist?(payload_def_file)
    bomb "Missing #{payload_def_file}."
  end
  payload_def = XmlSimple.xml_in(payload_def_file)
  payload_type = payload_def['type'].to_s
  if payload_type == ""
    bomb "No <type> defined in #{payload_def_file}."
  end

  # Remember these values in the payload record
  #print "#{name} => #{payload_dir} => #{payload_def_file} =>  #{payload_def['remoteUrl']}\n"
  payload['payloadDir'] = payload_dir;
  payload['payloadDef'] = payload_def;
  payload['payloadDefFile'] = payload_def_file;
  payload['type'] = payload_type;
end

#
#	Do any required preparation work for each of the payloads.
#	This calls a plugin to do the work, based upon the type of the payload.
#
def preparePayloads(deploymentConfig)

	# We've collected all the definitions - now lets do any preparation work,
	# depending upon the type of the payload (pull from git, etc).
	deploymentConfig['payload'].each do |payload|
		name = payload['name'].to_s
		type = payload['type'].to_s
		#payload_dir = payload['payloadDir'].to_s
		#payload_def_file = payload['payloadDefFile'].to_s
		#version = payload['version'].to_s

		ntype = type.gsub("-", "_")
		class_name = "PayloadType_#{ntype}"
		#print "class_name=#{class_name}\n"
		type_plugin = Object::const_get(class_name).new
		type_plugin.preparePayload payload
	end
end

#
#	Check that the git cache repository for a payload is up to date:
#		1. Check the repository exists, clone if not, else
#		2. Check it has the right remote. Clone a new repository if not, else
#		3. Fetch all updates from the remote.
#	The cache directories live somewhere like: payloads/extension-projects/tooltwist/git-repository
#
def checkCachedGit(payload)
  #print "Check cached Git\n\n"
    
	name = payload['name'].to_s
	layer = payload['layer'].to_s
	longname = "#{layer}/#{name}"
	payload_dir = payload['payloadDir'].to_s
	remote_url = payload['payloadDef']['remoteUrl'].to_s.strip

  cache_repo = "#{payload_dir}/git-repository"
	payload['cache_repo'] = cache_repo	  
  if $command_line_options.skip_cache_checks or $command_line_options.skip_git_checks
    return
  end

  print "\#\#\#\#  Checking payload cache\n"
  print "\#\#\#\#    cache: #{cache_repo}\n"
  print "\#\#\#\#    remote: #{remote_url}\n"
	if remote_url == "" or remote_url =~ /\s/
		bomb "Payload #{longname} does not define a valid 'remoteUrl' in it's project.xml file"
	end

	# Temporarily change to the payload directory. It will return back
	# automatically when the block completes.
	Dir.chdir(payload_dir) do

		# Check the repository directory exists
		if not File.exist?(cache_repo)
			# clone a new directory
		  # See http://stackoverflow.com/questions/3959924/whats-the-difference-between-git-clone-mirror-and-git-clone-bare
			print "repository not found\n"
      cmd = "git clone --mirror #{remote_url} #{cache_repo}"
			print "$ #{cmd}\n"
      STDOUT.flush
			reply = `#{cmd}`; if not $?.success? then bomb "Failed to clone GIT repository: #{reply}" end
			print "#{reply}"
		else
			# check the remote
			Dir.chdir(cache_repo) do
        STDOUT.flush
				reply = `git remote -v`; if not $?.success? then bomb "Could not list GIT remotes: #{reply}" end
				#print "REMOTES:\n#{reply}"
				if $command_line_options.skip_remote_checks or reply =~ /^origin.*#{remote_url}.*\(fetch\)$/
					# Has the correct remote - do a fetch to update the contents
					#print "remote is set correctly\n"
					cmd = "git fetch --all"
					print "$ #{cmd}\n"
          STDOUT.flush
					reply = `#{cmd}`; if not $?.success? then bomb "Could not update GIT repository: #{reply}" end
					print "#{reply}"
				else
					# Does not have the correct remote - move the repository aside and clone a new one
					print "wrong remote\n"

					# move directory
					i = 1
					while File.exist?("#{cache_repo}.#{i}")
						i += 1
					end
					print "Move repository to #{cache_repo}.#{i}\n"
					FileUtils.mv("#{cache_repo}", "#{cache_repo}.#{i}")

					# new clone
					cmd = "git clone --mirror #{remote_url} #{cache_repo}"
					print "$ #{cmd}\n"
          STDOUT.flush
					reply = `#{cmd}`; if not $?.success? then bomb "Failed to clone GIT repository: #{reply}" end
					print "#{reply}"
				end
			end # return to payload directory
		end

		# Decide which hash to use
	end # will return to previous directory
end

#
#	Check that the git repository for a project is up to date, and checkout the correct branch/tag.
#
def checkProjectGit(payload)
  #print "Check project Git\n\n"
  if $command_line_options.skip_git_checks
    return
  end
    
	name = payload['name'].to_s
	layer = payload['layer'].to_s
	longname = "#{layer}/#{name}"
	payload_dir = payload['payloadDir'].to_s
	remote_url = payload['payloadDef']['remoteUrl'].to_s.strip
	cache_repo = payload['cache_repo']

	# Decide which directory, etc to use
	if layer == "extension" or layer == "technology"
		project_dir = "#{$IMAGE_DIR}/extension-projects"
	elsif layer == "webdesign"
		project_dir = "#{$IMAGE_DIR}/webdesign-projects"
	elsif layer == "webserver"
		project_dir = "#{$IMAGE_DIR}/webserver-projects"
	else
		bomb("Unknown layer type (#{layer}) for payload #{name}");
	end
	FileUtils.mkdir_p project_dir
	project_repo = "#{project_dir}/#{name}"

	# See if this project/git directory exists
	print "\#\#\#\#  Update project from cache\n"
	print "\#\#\#\#    repository: #{project_repo}/.git\n"
	if not File.exist?(project_repo)
		print "creating\n"
		Dir.chdir(project_dir) do
			# clone a new project directory
      repo = "#{payload_dir}/git-repository"
      cmd = "git clone #{cache_repo} #{name}"
			print "$ #{cmd}\n"
      STDOUT.flush
			reply = `#{cmd}`; if not $?.success? then bomb "Failed to clone GIT repository: #{reply}" end
			print "#{reply}"
		end
	else
		#print "need to update.\n"
	end

	#
	#	Decide which branch/tag/hash to checkout
	#
	branchTagOrHash = payload['version'];
	Dir.chdir(project_repo) do
    # Pull any changes from the cache
	  # Note: This first fetch gets any new branches, because we can't switch to a branch we don't know about.
    cmd = "git fetch --all"
    print "$ #{cmd}\n"
    STDOUT.flush
    reply = `#{cmd}`; if not $?.success? then bomb "Failed to fetch updates from cached GIT repository: #{reply}" end
    print "#{reply}"

    # Set the branch
    cmd = "git checkout --force #{branchTagOrHash}"
    print "$ #{cmd}\n"
    STDOUT.flush
    reply = `#{cmd}`; if not $?.success? then bomb "Failed to set the branch of GIT repository: #{reply}" end
    print "#{reply}"

    # Set the branch
    cmd = "git pull origin"
    print "$ #{cmd}\n"
    STDOUT.flush
    reply = `#{cmd}`; if not $?.success? then bomb "Failed to update branch of GIT repository: #{reply}" end
    print "#{reply}"

#    # Pull (update changes on the branch)
#    cmd = "git pull --all"
#    print "$ #{cmd}\n"
#    STDOUT.flush
#    reply = `#{cmd}`; if not $?.success? then bomb "Failed to 'pull' GIT repository: #{reply}" end
#    print "#{reply}"
	end

=begin
	# Temporarily change to the payload directory. It will return back
	# automatically when the block completes.
	Dir.chdir(payload_dir) do

		# Check the repository directory exists
		repo = "#{payload_dir}/git-repository"
		print "\#\#\#\#  #{repo}..."
		if not File.exist?(repo)
			# clone a new directory
			print "not found\n"
			cmd = "git clone --bare #{remote_url} #{repo}"
			print "\n$ #{cmd}"
			reply = `#{cmd}`; if not $?.success? then bomb "Failed to clone GIT repository: #{reply}" end
			print "#{reply}\n"
		else
			# check the remote
			Dir.chdir(repo) do
				reply = `git remote -v`; if not $?.success? then bomb "Could not list GIT remotes: #{reply}" end
				#print "REMOTES:\n#{reply}"
				if reply =~ /^origin.*#{remote_url}.*\(fetch\)$/
					# Has the correct remote - do a fetch to update the contents
					print "remote is set correctly\n"
					cmd = "git fetch --all"
					print "\n$ #{cmd}\n"
					reply = `#{cmd}`; if not $?.success? then bomb "Could not update GIT repository: #{reply}" end
					print "#{reply}\n"
				else
					# Does not have the correct remote - move the repository aside and clone a new one
					print "wrong remote\n"

					# move directory
					i = 1
					while File.exist?("#{repo}.#{i}")
						i += 1
					end
					print "\#\#\#\#  Moving repository to #{repo}.#{i}\n"
					FileUtils.mv("#{repo}", "#{repo}.#{i}")

					# new clone
					cmd = "git clone --bare #{remote_url} #{repo}"
					print "\n$ #{cmd}\n"
					reply = `#{cmd}`; if not $?.success? then bomb "Failed to clone GIT repository: #{reply}" end
					print "#{reply}\n"
				end
			end # return to payload directory
		end
	
		# Decide which hash to use
	end # will return to previous directory
=end

	end

#
#	Create the build.xml script for Ant
#
#	This occurs in multiple phases:
#
#	1. generic script 
#	2. payload-related script for each payload, based upon the payload type
#	3. server-related, based upon the server type
#
def createBuildXml(deploymentConfig)
	
	#
	#	Create the generic script
	#
	#tmp_build_file = "#{$BUILD_FILE}.tmp"
	#File.rm tmp_build_file

	code = ""



=begin
#!/bin/bash
#
#	Create the main Ant script for a launchpad. This script is called by the
#	bootstrap.xml Ant script, if the config file is changed. The config file
#	gets created by ttcSync.java, if (a) the current schedule changes, or
#	(b) a new payload version matches the defined acceptable statuses for
#	the configuration.
#
#	Note that the config files in the configs directory are derived from
#	the configurations which are defined in the database.
#
#	usage: createLaunchpadBuildScript servername bundle1 bundle2 bundle3....
#
#	Bundles are defined with a relative path. e.g. layer-3-technology/ttStd-20090704
#
BIN_DIR=`dirname $0`
. ${BIN_DIR}/mkScript-inc

# Check the parameters
cmd=$0
if [ -z ${2} ] ; then
	${ECHO} usage: ${cmd} pad config
	exit 1
fi
LAUNCHPAD_NAME=$1; shift
CONFIG_ID=$1; shift

# Load the Controller configuration
loadControllerConfig

# Set the launchpad configuration variables
loadLaunchpadDetails

# Display a nice message
${ECHO} "==============================================================="
${ECHO} "===    Creating construct.xml for server ${LAUNCHPAD_NAME}"
${ECHO} "===    Using configuration '${CONFIG_ID}'"
${ECHO} "==============================================================="

# Create the launch pad directory
ANTFILE=${LAUNCHPAD_DIR}/construct.xml
TMPFILE=${LAUNCHPAD_DIR}/construct.xml.tmp
export LAUNCHPAD_DIR=$LAUNCHPAD_HOME/${LAUNCHPAD_NAME}
${ECHO} Installing server to ${LAUNCHPAD_DIR}
mkdir -p -v ${LAUNCHPAD_DIR}
TMPFILE=${ANTFILE}.tmp
${ECHO} Creating ${TMPFILE}
rm -f ${TMPFILE}

# Get a list of payloads (grep lines, get type element, trim front and back)
loadListOfPayloads;

=end



	#
	#	Get a list of extension projects - they'll need to be added to tooltwist.conf (ZOZ)
	#
	$EXTENSION_PAYLOADS = ""
	deploymentConfig['payload'].each do |payload|
		name = payload['name'].to_s
		type = payload['type'].to_s
#print "            #{name} => #{type}\n"
		if type == "extension" or type == "extension_jar" or type == "extension_fulljar"
			$EXTENSION_PAYLOADS << " " << name
		end
	end
print "$EXTENSION_PAYLOADS = #{$EXTENSION_PAYLOADS}\n"
	
	template = "#{$TTC_CONFIG}/ant-templates/v8-construct-1.xml"
  code << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  code << "<!--\n"
  code << "\n"
  code << "\n"
  code << "\n"
  code << "        WARNING:   This file is generated by the Tooltwist Controller using #{$THIS_SCRIPT}.\n"
  code << "        IF YOU MODIFY THIS FILE, YOUR CHANGES WILL BE OVERWRITTEN NEXT TIME THE CONTROLLER RUNS.\n"
  code << "\n"
  code << "\n"
  code << "\n"
  code << "******************************************************************************************************\n"
  code << "*****\n"
  code << "*****  Definitions for launchpad '#{$LAUNCHPAD_NAME}'\n"
  code << "*****\n"
  code << "*****  launchpad type: #{$LAUNCHPAD_TYPE}\n"
  code << "*****  template: #{template}\n"
  code << "*****\n"
  code << "******************************************************************************************************\n"
  code << "\n"
  code << "-->\n"
  code << "\n"
  print "\#\#\#\#  Adding header script\n"
	if not File.exists?(template)
		bomb "Missing template: #{template}"
	end
	tmp = File.read(template)
	code << replaceLaunchpadParams(tmp)


	#
	# Add the default task to the ant file
	#
	print "\#\#\#\#  Adding default tasks\n"
  code << "<!--\n"
  code << "******************************************************************************************************\n"
  code << "*****\n"
  code << "***** DEFAULT TASK TO BUILD, GENERATE, VALIDATE AND INSTALL THIS LAUNCHPAD.\n"
  code << "*****\n"
	code << "*****  Script inserted by #{$THIS_SCRIPT}\n"
	code << "*****\n"
  code << "******************************************************************************************************\n"
	code << "-->\n"
	code << "	<target name=\"build\" depends=\"startBuild"
	deploymentConfig['payload'].each do |payload|
		name = payload['name'].to_s
		code << ",#{name}"
	end
	if $LAUNCHPAD_TYPE != "workbench" and $LAUNCHPAD_TYPE != "designer"
    code << ",launchpad-web.xml,production-web.xml"
    code << ",launchpad-tooltwist.conf,production-tooltwist.conf"
    code << ",launchpad-wbd.conf,production-wbd.conf"
		code << ",generationDirs,dummyBinDir"
	end
	code << ",endBuild\"\n"
	code << "                description=\"Building launchpad #{$LAUNCHPAD_NAME}\"/>\n"
	code << "\n"

	#
	# Add tasks for each bundle, depending upon it's type
	#
cnt = 0
	deploymentConfig['payload'].each do |payload|
		layer = payload['layer'].to_s
		name = payload['name'].to_s
		type = payload['type'].to_s

		template = "#{$TTC_CONFIG}/ant-templates/v8-construct-2-#{type}.xml"

cnt = cnt + 1
		code << "\n\n"
		code << "<!--\n"
    code << "******************************************************************************************************\n"
		code << "*****\n"
		code << "***** TASKS FOR PAYLOAD #{name}\n"
		code << "*****\n"
		code << "*****  project type: #{layer}\n"
		code << "*****  payload type: #{type}\n"
		code << "*****  template: #{template}\n"
		code << "*****\n"
    code << "******************************************************************************************************\n"
		code << "-->\n"
		print "\#\#\#\#  Adding tasks for #{name} (#{type})\n"
	
		tmp = File.read(template)
		tmp = replaceLaunchpadParams(tmp)
		tmp = replacePayloadParams(tmp, payload)
		code << tmp
	end

	#
	#	Add tasks for this launchpad type
	#
#	if not File.exist?(template)
#		bomb "Missing template for this type of launchpad: #{template}"
#	end
	template = "#{$TTC_CONFIG}/ant-templates/v8-construct-3-#{$LAUNCHPAD_TYPE}.xml"
	code << "\n\n"
	code << "<!--\n"
  code << "******************************************************************************************************\n"
	code << "*****\n"
	code << "***** OTHER TASKS FOR THIS TYPE OF LAUNCHPAD\n"
	code << "*****\n"
  code << "*****  launchpad type: #{$LAUNCHPAD_TYPE}\n"
	code << "*****  template: #{template}\n"
	code << "*****\n"
  code << "******************************************************************************************************\n"
	code << "-->\n"
	print "\#\#\#\#  Adding tasks for launchpad type #{$LAUNCHPAD_TYPE}\n"

	tmp = File.read(template)
	tmp = replaceLaunchpadParams(tmp)
	code << tmp
	
  #
  # Add tasks to validate
  #
  template = "#{$TTC_CONFIG}/ant-templates/v8-construct-4.xml"
  code << "\n\n"
  code << "<!--\n"
  code << "******************************************************************************************************\n"
  code << "*****\n"
  code << "***** TASKS TO VALIDATE, GENERATE and INSTALL THE WEBSITE / WEBAPP\n"
  code << "*****\n"
  code << "******************************************************************************************************\n"
  code << "-->\n"
  print "\#\#\#\#  Adding tasks for validation\n"
  tmp = File.read(template)
  tmp = replaceLaunchpadParams(tmp)
  code << tmp

	#print "\n\nCODE IS NOW:\n\n#{code}"
	return code
end

#
#	Replace any launchpad related variables in a string
#
def replaceLaunchpadParams(code)

	code = code.gsub("@LAUNCHPAD_NAME@", $LAUNCHPAD_NAME) 
	code = code.gsub("@LAUNCHPAD_TYPE@", $LAUNCHPAD_TYPE) 
	code = code.gsub("@LAUNCHPAD_DIR@", $LAUNCHPAD_DIR) 
	code = code.gsub("@IMAGE_DIR@", $IMAGE_DIR) 
	code = code.gsub("@LAUNCHPAD_HOME@", $LAUNCHPAD_HOME) 
	code = code.gsub("@BIN_DIR@", $BIN_DIR) 
	code = code.gsub("@TTC_CONFIG@", $TTC_CONFIG) 
	code = code.gsub("@TTC_PORT@", $TTC_PORT)
	code = code.gsub("@TTC_DEPLOY@", $TTC_DEPLOY)
	code = code.gsub("@GENCONF_HOME@", $GENCONF_HOME)
	code = code.gsub("@CUSTCONF_HOME@", $CUSTCONF_HOME)
	code = code.gsub("@HTTP_PORT@", $GENERATOR_HTTP_PORT)
	code = code.gsub("@HTTPS_PORT@", $GENERATOR_HTTPS_PORT)
	code = code.gsub("@SERVER_PORT@", $GENERATOR_SERVER_PORT)
	code = code.gsub("@AJP_PORT@", $GENERATOR_AJP_PORT)
	code = code.gsub("@IMAGEMAGICK_HOME@", $GENERATOR_IMAGEMAGICK_HOME)
	code = code.gsub("@PRODUCTION_HTTP_PORT@", $PRODUCTION_HTTP_PORT)
	code = code.gsub("@PRODUCTION_HTTPS_PORT@", $PRODUCTION_HTTPS_PORT)
	code = code.gsub("@PRODUCTION_SERVER_PORT@", $PRODUCTION_SERVER_PORT)
	code = code.gsub("@PRODUCTION_AJP_PORT@", $PRODUCTION_AJP_PORT)
	code = code.gsub("@PRODUCTION_SERVER_HOME@", $PRODUCTION_SERVER_HOME)
	code = code.gsub("@PRODUCTION_WEBAPPS_DIR@", $PRODUCTION_WEBAPPS_DIR)
	code = code.gsub("@PRODUCTION_TOMCAT_HOME@", $PRODUCTION_TOMCAT_HOME)
	code = code.gsub("@PRODUCTION_WEBAPP_NAME@", $PRODUCTION_WEBAPP_NAME)
	code = code.gsub("@PRODUCTION_SERVER@", $PRODUCTION_SERVER)
	code = code.gsub("@PRODUCTION_IMAGEMAGICK_HOME@", $PRODUCTION_IMAGEMAGICK_HOME)
	code = code.gsub("@EXTENSION_PAYLOADS@", $EXTENSION_PAYLOADS)
=begin
= begin
		.gsub("@PAYLOAD_HOME@", $PAYLOAD_HOME) \
		.gsub("@VERSIONS_HOME@", $VERSIONS_HOME) \

		.gsub("@PRODUCTION_INSTALL_CMD@", $PRODUCTION_INSTALL_CMD) \
		.gsub("@PRODUCTION_DISABLE@", $PRODUCTION_DISABLE) \
		.gsub("@PRODUCTION_INSTALL_TOMCAT@", $PRODUCTION_INSTALL_TOMCAT) \
		.gsub("@PRODUCTION_WEBAPP_NAME@", $PRODUCTION_WEBAPP_NAME) \
=end
	code = code.gsub("@JAVAC_MEMORY@", $JAVAC_MEMORY)
	code = code.gsub("@JAVAC_DEBUG@", $JAVAC_DEBUG)
	code = code.to_s
	return code
end

#
#	Replace any payload related variables in a string
#
def replacePayloadParams(code, payload)
	layer = payload['layer'].to_s
	name = payload['name'].to_s
	type = payload['type'].to_s
	version = payload['version'].to_s
	payload_dir = getPayloadDirectory(payload)
	# Is this used any more? ZZZZ
	payload_tmp = "#{$LAUNCHPAD_DIR}/tmp/#{$PAYLOAD_NAME}-#{$PAYLOAD_VERSION}"

	#print "\n\n\n\n\n\ntemplate code=#{code}\n\n\n\n\n\n\n"
	code = code \
    .gsub("@PAYLOAD_LAYER@", layer) \
    .gsub("@PAYLOAD_NAME@", name) \
		.gsub("@PAYLOAD_TYPE@", type) \
		.gsub("@PAYLOAD_VERSION@", version) \
		.gsub("@PAYLOAD_DIR@", payload_dir) \
		.gsub("@PAYLOAD_TMP@", payload_tmp) \
		.to_s
	return code
end

#
#	Return the directory containing a payload
#
def getPayloadDirectory(payload)
	layer = payload['layer'].to_s
	name = payload['name'].to_s
	if layer == "technology" or layer == "extension"
		return "#{$TTC_DEPLOY}/payloads/extension-projects/#{name}"
	elsif layer == "webdesign"
		return "#{$TTC_DEPLOY}/payloads/webdesign-projects/#{name}"
	elsif layer == "webserver"
		return "#{$TTC_DEPLOY}/payloads/webserver-projects/#{name}"
	else
		bomb "Payload '#{name}' has unknown project type '#{layer}'."
	end
end

###########################################################################
#
#	
#

def run_phase(phase, launchpad_name)
  if phase == ControllerPhase::BUILD
      phase_build(launchpad_name)
  elsif phase == ControllerPhase::GENERATE
      phase_generate(launchpad_name)
  elsif phase == ControllerPhase::VALIDATE
      phase_validate(launchpad_name)
  elsif phase == ControllerPhase::TRANSFER
      phase_transfer(launchpad_name)
  elsif phase == ControllerPhase::COMMIT
      phase_commit(launchpad_name)
  else
    softbomb "Internal error: unknown phase #{phase}"
  end
end

def phase_build(launchpad_name)
  
  print "\n"
  print "+------------------------------------------------------------------------------------------+\n"
  print "|                                                                                          |\n"
  print "|                                       BUILD PHASE                                        |\n"
  print "|                                                                                          |\n"
  print "+------------------------------------------------------------------------------------------+\n"
  print "SKIPPED\n\n\n"
  return
  time1 = Time.new
  puts "Current Time : " + time1.inspect
  
  # Check the launchpad, creating project directories and creating the build.xml script for Ant.
  preBuild launchpad_name
  
  # Make sure the launchpad is not running
  print "**\n"
  print "**  Check the launchpad server is not running.\n"
  print "**\n"
  ControllerLaunchpad::shutdown_if_running($GENERATOR_HTTP_PORT)
  
  
  # If we got here all must be well, so run the build script
  Dir.chdir($LAUNCHPAD_DIR) do
    print "\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n"
    print "\#\#\#\#\n"
    print "\#\#\#\#     Run the build command\n"
    cmd = "ant -f #{$BUILD_FILE}"
    print "$ cd #{$LAUNCHPAD_DIR}\n"
    print "$ #{cmd}\n"
    success = system(cmd)
    softbomb "Build failed" if not success
    print "\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n"
    print "\#\#\#\#\n"
    print "\#\#\#\#     Build appears to have been successful\n"
    print "\#\#\#\#\n"
    print "\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n"
    return true
  end
  
  return false # Can this happen?
end

#
#   Get the launchpad to generate the website
#
#   Prerequisites:
#     ControllerMisc.setupGlobals(launchpad_name)
#     ControllerMisc.loadLaunchpadDetails(launchpad_name)
#     ControllerMisc.loadGeneratorSlotDetails(launchpad_name)
#     ControllerMisc.loadServerSlotDetails(launchpad_name)
#
def phase_generate(launchpad_name)
  print "\n"
#  print "+------------------------------------------------------------------------------------------+\n"
#  print "|                                                                                          |\n"
#  print "|                                     GENERATION PHASE                                     |\n"
#  print "|                                                                                          |\n"
#  print "+------------------------------------------------------------------------------------------+\n"
  print "launchpad is (#{launchpad_name})\n"
  
#  print "GENERATE NEEDS TO HAPPEN\n\n\n"
#  return;
  
  now = Time.now.strftime("%Y%m%d-%H%M")
  
if $OLD_VERSION  then
end # $OLD_VERSION

  ControllerMisc.setupGlobals(launchpad_name)
#  ControllerMisc.loadLaunchpadDetails(launchpad_name)
  $LAUNCHPAD_TYPE = 'test'
  ControllerMisc.loadGeneratorSlotDetails(launchpad_name) or bomb "Launchpad '#{launchpad_name}' does not have a generator slot assigned"
  ControllerMisc.loadServerSlotDetails(launchpad_name) or bomb "Launchpad '#{launchpad_name}' does not have a server slot assigned"

  #8.3hack
  $GENERATOR_HTTP_PORT = 8080;
  
  # Check we have email configured
if $OLD_VERSION  then
  mail_configuration = "#{$TTC_DEPLOY}/config-files/mail-conf.rb"
  require mail_configuration
end # $OLD_VERSION
  
#  print "\n"
#  print "PRODUCTION_DESCRIPTION #{$PRODUCTION_DESCRIPTION}\n"
#  print "PRODUCTION_IS_GROUP #{$PRODUCTION_IS_GROUP}\n"
#  print "PRODUCTION_SERVER_HOME #{$PRODUCTION_SERVER_HOME}\n"
#  print "PRODUCTION_WEBAPP_NAME #{$PRODUCTION_WEBAPP_NAME}\n"
#  print "PRODUCTION_HTTP_PORT #{$PRODUCTION_HTTP_PORT}\n"
  
  # Find the project - it will be the last webdesign project
  project_name = nil 
  deploymentConfig = ControllerMisc.loadPayloadDetails(launchpad_name)
  config_file = ControllerMisc.deploymentConfigFile(launchpad_name)
  deploymentConfig['payload'].each do |payload|
    layer = xmlValue(config_file, payload, 'layer')
    name = xmlValue(config_file, payload, 'name')
    if layer == "webdesign"
      project_name = name
    end
  end
  if project_name == nil
    bomb "No webdesign project mapped to this launchpad"
  end

if $OLD_VERSION  then
end # $OLD_VERSION

 
#  # Check the server is running
#  print "**\n"
#  print "**  Check the server is running\n"
#  print "**\n"
#  ControllerLaunchpad::startup_if_not_running($GENERATOR_HTTP_PORT)
#  print "-\n"
#  started = ControllerLaunchpad::wait_to_start($GENERATOR_HTTP_PORT)
#  if started
#    print "The launchpad is now running.\n"
#  elsif
#    softbomb "Could not start the launchpad."
#  end


  # Get a list of navpoints that need to be generated
  #   a) from the --navpoints parameter
  #   b) from an environment variable
  #   c) ask the launchpad server for a list
  command_line_navpoints = $command_line_options.navpoints
  environment_variable_navpoints = ENV["NAVPOINTS"]
    
  if command_line_navpoints != nil and command_line_navpoints != ""
    
    # Navpoints defined by command line option
    if command_line_navpoints == "-" or command_line_navpoints == "all"
      print "Generating all navpoints.\n"
      print "Command line option: --navpoint #{command_line_navpoints}\n"
      navpoint_list = get_navpoints(project_name, "-")
    else
      print "==============================================================================> NOTE: PARTIAL GENERATE\n"
      print "Generating these navpoints: #{command_line_navpoints}\n"
      print "Command line option: --navpoint #{command_line_navpoints}\n"
      navpoint_list = get_navpoints(project_name, command_line_navpoints)
    end
    
  elsif environment_variable_navpoints != nil and environment_variable_navpoints != ""
    
    # Navpoints defined by environment variable
    if environment_variable_navpoints == "-" or environment_variable_navpoints == "all"
      print "Generating all navpoints.\n"
      print "Environment variable: NAVPOINTS=#{environment_variable_navpoints}\n"
      navpoint_list = get_navpoints(project_name, "-")
    else
      print "==============================================================================> NOTE: PARTIAL GENERATE\n"
      print "Generating these navpoints: #{environment_variable_navpoints}\n"
      print "Environment variable: NAVPOINTS=#{environment_variable_navpoints}\n"
      navpoint_list = get_navpoints(project_name, environment_variable_navpoints)
    end

  else
    
    # Generate all navpoints
    print "Generating all navpoints.\n"
    print "(No --navpoints or environment variable named 'NAVPOINTS')\n"
    navpoint_list = get_navpoints(project_name, "-")
  end    


  num_navpoints = navpoint_list.size
  print "Total of #{num_navpoints} navpoints.\n"
  if num_navpoints <= NOT_MANY_NAVPOINTS
    #
    # There's not enough navpoints to go multi-threaded, so keep it simple with one thread
    #
    print "Will use a single thread.\n"
    arr = []
    arr[0] = ""
    navpoint_list.each do |navpoint|
      #print "   - #{navpoint}\n"
      str = "\t\t<navpoint>#{navpoint}</navpoint>\n"
      arr[0] << str
    end
  else
    #
    # Use multiple threads, with the first thread having only one navpoint
    #
    num_threads = (num_navpoints / NAVPOINTS_PER_THREAD) + 2
    if num_threads > MAX_THREADS
      num_threads = MAX_THREADS
    end
    print "Will use #{num_threads} threads.\n"

    # Prepare an array of XML to define the navpoints for each thread.
    arr = []
    for i in 0..(num_threads-1)
      arr[i] = ""
    end

    # Add the navpoints to the XML for each thread
    # Put a single navpoint in the first thread
    # Put the rest in the remaining threads
    i = 0
    navpoint_list.each do |navpoint|
      #print "   - #{navpoint}\n"
      str = "\t\t<navpoint>#{navpoint}</navpoint>\n"
      if i == 0
        arr[0] = str
      else
        arr[1 + ((i-1) % (num_threads-1))] << str
      end
      i  = i + 1
    end
  end # num_navpoints > NOT_MANY_NAVPOINTS

# print "EARLY RETURN 1\n\n\n"
# return; 
  
  # Remove any existing filter files
  filter_dir = "#{$LAUNCHPAD_DIR}/filters"
  FileUtils.mkdir_p filter_dir
  Dir.glob("#{filter_dir}/filter-*.xml").each do |file|
    File.delete file
  end

  # Load the template for the filter files
  # Try for a launchpad-name-specific template first, and if not found then a lauchpad-type-specific template.
  template = "#{$TTC_DEPLOY}/config-files/TEMPLATES/filters.launchpad-#{$LAUNCHPAD_NAME}.xml"
  if not File.exists?(template)
		template = "#{$TTC_DEPLOY}/config-files/TEMPLATES/filters.#{$LAUNCHPAD_TYPE}.xml"
		if not File.exists?(template)
			bomb "Missing template: #{template}"
		end
  end
  template_xml = File.read(template)
  
  # Check there is a variable in the template to replace
  if template_xml.index(FILTER_TEMPLATE_NAVPOINT_VARIABLE) == nil
    bomb "Template contains no #{FILTER_TEMPLATE_NAVPOINT_VARIABLE}: #{template}"
  end

  # Create the filter file for each thread.
  filterConfigs = []
  for i in 0..(arr.length-1)
    filter_file = "#{filter_dir}/filter-#{i+1}.xml"
#    print "\nThread #{i+1}:\n#{arr[i]}\n"
#    cnt = 0
#    arr[i].each do |np|
#      if cnt > 0
#        print ", "
#      end
#      print np
#      if (cnt++ % 10) == 0
#        print "\n"
#      end
#    end
    filter_xml = template_xml.gsub(FILTER_TEMPLATE_NAVPOINT_VARIABLE, arr[i]) 
    bFile = File.new(filter_file, "w")
    bFile.write(filter_xml)
    bFile.close
    
    filterConfigs << filter_file
  end
  
  
  #
  # Decide where to deploy
  #
  deploy_dir = "#{$TTC_DEPLOY}/image/tomcat/webapps/#{$PRODUCTION_WEBAPP_NAME}"
#  print "AAAAA #{$LAUNCHPAD_DIR}.\n"
#  print "BBBBB #{$PRODUCTION_WEBAPP_NAME}.\n"
  print "CCCCC #{deploy_dir}.\n"
  FileUtils.mkdir_p deploy_dir
  
  print "\n\n"
  print "\t\tNOTE: Generating directly into #{deploy_dir}.\n\n\n"
  print "\n\n"
  
  #
  #   Run the generator once for each filter definition file we have. Each filter file
  #   contains a non-overlapping list of navpoints, or hierarchy to be generated. The first
  #   will be allowed to perform a full generate, while the others will be restricted
  #   to just generating and installing navpoints.
  #  
  print "**\n"
  print "** Run the generator process(es).\n"
  print "**\n"
  threads = []  
  extraParam = ""
  cnt = 1
  for filterConfigFile in filterConfigs
    uri_string = "http://localhost:#{$GENERATOR_HTTP_PORT}/ttsvr/generator"
    uri_string << "?project=#{project_name}"
    uri_string << "&webapp=#{$PRODUCTION_WEBAPP_NAME}"
    uri_string << "&base=#{$LAUNCHPAD_DIR}"
    uri_string << "&config=#{filterConfigFile}"
    uri_string << "&deploy=#{deploy_dir}"
      
    # The second and subsequent thread should generate the required navpoints, but not install any other web assets.
    if cnt > 1
      uri_string << "&onlyInstallNavpoints"
    end
    
    # If we have multiple threads, use a different build ID for
    # each thread and keep the progress messages to a minimum.
    if filterConfigs.length > 1
      uri_string << "&quiet"
      uri_string << "&buildId=#{now}-thread-#{cnt}"
    else
      uri_string << "&buildId=#{now}"
    end

    # Just display the first URL    
    if cnt == 1
      print "uri=#{uri_string}\n"
    end

    # Start a thread for this generate
    threads << Thread.new(filterConfigFile, uri_string) { |config, uri|

        # Call the server, and write the output from the server to our output    
        uri = URI(uri)
        is_problem = false
        begin  
          Net::HTTP.start(uri.host, uri.port) do |http|
            http.open_timeout = 10
            http.read_timeout = 120 * 60 # two hours (hope it's never this slow)
            request = Net::HTTP::Get.new uri.request_uri
            
            http.request request do |response|
        
              # Check the response code        
              case response
                
              when Net::HTTPOK
                # Looks ok, lets output the result
                response.read_body do |chunk|
                  print chunk
                end # read
                
              when Net::HTTPBadRequest
                # Bad request
                print "\n"
                print "**\n"
                print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
                print "**\n"
                print "** The generator has rejected the parameters we passed.\n"
                print "** URI=#{uri}\n"
                print "**\n"
                is_problem = true
              else
                # Some other error
                print "\n"
                print "**\n"
                print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
                print "**\n"
                print "**  Error contacting server: #{response.code} - #{response.message}\n\n\n\n\n"
                print "** URI=#{uri}\n"
                print "**\n"
                is_problem = true
              end #case
              
            end #do |response|
          end# do|http|
          
        rescue Timeout::Error => e
          # Timeout
          errtime = now = Time.now.strftime("%Y%m%d-%H%M")
          print "**\n"
          print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
          print "** Thread #{cnt}: #{errtime}\n"
          print "** Timeout waiting for launchpad server to do generate: #{e.message}\n"
          print "**\n"
          Thread.current[:error] = true
          Thread.exit
  
        rescue Exception => e
          # Some exception
          errtime = now = Time.now.strftime("%Y%m%d-%H%M")
          print "**\n"
          print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
          print "** Thread #{cnt}: #{errtime}\n"
          print "** Error while having the launchpad server do the generate: #{e.message}\n"
          print "**\n"
          Thread.current[:error] = true
          Thread.exit
        end #begin
        
        # If there was a problem, bomb out
        if is_problem
          errtime = now = Time.now.strftime("%Y%m%d-%H%M")
          print "**\n"
          print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
          print "** Thread #{cnt}: #{errtime}\n"
          print "** Cannot proceed, because the generator in the launchpad server could not be invoked.\n"
          print "**\n"
          print "**\n"
          Thread.current[:error] = true
          Thread.exit
        end
        
        # No error
        Thread.current[:error] = false
    }
    
    # Give the first thread a head start to load the ToolTwist metedata, etc
    if cnt == 1 and filterConfigs.length > 1
      sleep 15
    end
    cnt = cnt + 1

  end

  
  # Wait for all the threads to finish    
  have_error = false;
  threads.each { |aThread|
    aThread.join
    # Check for an error in the thread
    if aThread[:error] == true
      have_error = true;
    end
  }
  if have_error
    bomb "Generation Failed: See errors reported above."
  end
  print "\nAll threads complete.\n"
  
  #
  # Look for generation errors
  #
  prefix = "#{$LAUNCHPAD_DIR}/logs/#{now}-"
  generation_error_files = []
  Dir.glob("#{prefix}thread-*/ERROR=*").each do |file|
    if ! file.end_with? "/ERROR=0"
      generation_error_files  << file
    end
  end #glob
  
  if generation_error_files.length == 0
    print "**\n"
    print "**  No Generation Errors detected.\n"
    print "**\n\n"
  else
    print "**\n"
    print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
    print "**\n"
    print "**\t#{generation_error_files.length} errors while generating:\n"
    print "**\n"
    generation_error_files.each do |file|
      $FINAL_ERROR_MESSAGE << "Generator Errors  -->  #{file}\n"
      print "**\t#{file}\n"
    end
    print "**\n\n"
    
    # Send an email
    if EMAIL_GENERATE_ERRORS == ""
      print "**  To set up email notifications, update #{mail_configuration}\n"
    else
      print "**  Sending error report to #{EMAIL_GENERATE_ERRORS}.\n"
      tnow = Time.now.strftime("%Y/%m/%d-%H:%M")
      subject = "Generation Error(s) - #{launchpad_name} - #{tnow}"
      body = "Controller '#{CONTROLLER_IDENTIFIER}' reports that GENERATION errors were encountered while generating pages. Errors at:\n\n\n"
      generation_error_files.each do |file|
        body << "\t#{file}\n"
      end
      send_deploy_result(EMAIL_GENERATE_ERRORS, subject, body, [])
    end
    print "**\n"
  end
  
  
  #
  #   Look for navpoint errors
  #
  navpoint_error_files = []
  prefix = "#{$IMAGE_DIR}/tomcat/webapps/ttsvr/errors/navpoint_"
  suffix = ".html"
  Dir.glob("#{prefix}*#{suffix}").each do |file|
    # Ignore errors for navpoints not in this generate
    name = file[(prefix.length)..-(suffix.length + 1)]
    if navpoint_list.include?(name)
      navpoint_error_files << file
    end
  end
  if navpoint_error_files.length == 0
    print "**\n"
    print "**  No Web Design Errors detected.\n"
    print "**\n"
  else
    
    # We have errors...
    print "**\n"
    print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
    print "**\n"
    print "**  #{navpoint_error_files.length} errors in webdesign:\n"
    print "**\n"
    navpoint_error_files.each do |file|
      $FINAL_ERROR_MESSAGE << "Web Design Errors  -->  #{file}\n"
      print "**\t#{file}\n"
    end
    print "**\n"
    
    # Send an email
    if EMAIL_WEBDESIGN_ERRORS == ""
      print "**  To set up email notifications, update #{mail_configuration}\n"
    else
      print "** Sending report to #{EMAIL_WEBDESIGN_ERRORS}.\n"
      tnow = Time.now.strftime("%Y/%m/%d-%H:%M")
      subject = "Navpoint Error(s) - #{launchpad_name} - #{tnow}"
      body = "Controller '#{CONTROLLER_IDENTIFIER}' reports that NAVPOINT errors were encountered while generating pages, please see attached.\n\n\n\n"
      send_deploy_result(EMAIL_WEBDESIGN_ERRORS, subject, body, navpoint_error_files)
    end
    print "**\n"
  end

  return
  
end

#
#   Get a list of individual navpoints from the launchpad server
#
def get_navpoints(project_name, navpoints_requested)
  # Were they passed as a parameter?
  # Is an environtmnet variable provided?
  # Get the full list from the launchpad
  #http://dev3:37080/ttsvr/listNavpoints?project=ttdemo&shuffle=true
  uri_string = "http://localhost:#{$GENERATOR_HTTP_PORT}/ttsvr/listNavpoints"
  uri_string << "?project=#{project_name}"
  if navpoints_requested != nil and navpoints_requested != "-" and navpoints_requested != "all"
    uri_string << "&navpoints=#{navpoints_requested}"
  end
  uri_string << "&shuffle=true"
  print "URL=#{uri_string}\n"

  # Call the server, and get our list of navpoints    
  uri = URI(uri_string)
  is_problem = false
  replyFromServlet = ""
  begin  
    Net::HTTP.start(uri.host, uri.port) do |http|
      http.open_timeout = 10
      http.read_timeout = 120 * 60 # two hours (hope it's never this slow)
      request = Net::HTTP::Get.new uri.request_uri
      http.request request do |response|
  
        # Check the response code        
        case response
        when Net::HTTPOK
          # all ok
          
#        when Net::HTTPBadRequest
#          print "\n"
#          print "**\n"
#          print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
#          print "**\n"
#          print "** The generator has rejected the parameters we passed.\n"
#          print "** URI=#{uri}\n"
#          print "**\n"
#          is_problem = true
        else
          print "\n"
          print "**\n"
          print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
          print "**\n"
          print "**  Error contacting server: #{response.code} - #{response.message}\n\n\n\n\n"
          print "** URI=#{uri}\n"
          print "**\n"
          is_problem = true
        end #case
          
        # Looks ok, lets output the result
        response.read_body do |chunk|
          replyFromServlet << chunk
        end # read
        
      end #do |response|
    end# do|http|
  rescue Timeout::Error => e
    puts "Timeout waiting for launchpad server to do generate: #{e.message}\n"
  rescue Exception => e
    puts "Error while having the launchpad server do the generate: #{e.message}\n"
  end #begin

  # Get the navpoints from the reply
  list = []
  prefix = "navpoint="
  errorPrefix = "error="
  replyFromServlet.each_line do |line|
#    print "->#{line}"
    if line.start_with?(prefix)
      navpoint = line[prefix.length .. -1].strip
#      print "+++++ #{navpoint}.\n"
      list << navpoint
    elsif line.start_with?(errorPrefix)
      error = line[errorPrefix.length .. -1].strip
      print "\nERROR ERROR ERROR: #{error}\n\n"
    elsif line.start_with?("COMPLETION-MARKER")
#      print "WE'RE DON!!!!!\n"
      break
    end
  end
  
  # If there was a problem, bomb out
  if is_problem
    print "Cannot proceed, because the generator in the launchpad server could not be invoked.\n"
    Thread.exit
  end
  
  return list
end



#
#   Validate the website 
#
def phase_validate(launchpad_name)
  print "\n"
  print "+------------------------------------------------------------------------------------------+\n"
  print "|                                                                                          |\n"
  print "|                                     VALIDATION PHASE                                     |\n"
  print "|                                                                                          |\n"
  print "+------------------------------------------------------------------------------------------+\n"
  print "launchpad is (#{launchpad_name})\n"
  print "VALIDATE DOES NOTHING YET\n\n\n"
  return true
end

def phase_transfer(launchpad_name)
  print "\n"
#  print "+------------------------------------------------------------------------------------------+\n"
#  print "|                                                                                          |\n"
#  print "|                                      TRANSFER PHASE                                      |\n"
#  print "|                                                                                          |\n"
#  print "+------------------------------------------------------------------------------------------+\n"
  print "launchpad is (#{launchpad_name})\n"
#  print "SKIPPED\n\n\n"
#  return;

  # Set the environment variables
  ControllerMisc.setupGlobals(launchpad_name)
#  print "TTC_CONFIG=#{$TTC_CONFIG}\n"
#  ENV["TTC_CONFIG"] = $TTC_CONFIG
   # JAVA_OPTS is set inside installLaunchpad
   #java_opts = "-Xms256m -Xmx2g -XX:MaxPermSize=512m"
   #print "Setting JAVA_OPTS=#{java_opts}\n"
   #ENV["JAVA_OPTS"] = java_opts

  # Check we have email configured
  mail_configuration = "#{$TTC_DEPLOY}/config-files/mail-conf.rb"
  require mail_configuration
  
  # Run the command, capturing the output
  bin = File.dirname(__FILE__)
  tmpfile = "/tmp/,xfer" 
  cmd = "#{bin}/installLaunchpad #{launchpad_name}" 
  print "$ #{cmd}\n"
  output = ""
  IO.popen(cmd) { |f|
    until f.eof?
      str = f.gets
      puts str
      output << str
    end
  }
  rv = $?


  # Handle any errors  
  if rv == 0
    print "**\n"
    print "**  No transfer errors detected.\n"
    print "**\n\n"
  else
    
    # We have errors...
#    print "**\n"
#    print "** ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR\n"
#    print "**\n"
#    print "**  Errors occurred transferring files:\n"
#    print "**\n"

    # Get the summary lines from the output
    summary = ""
    output.each do |line|
      if line.start_with? "SUMMARY: "
        #print "line: #{line}\n"
        summary << line[9..-1]
      end
    end    
    $FINAL_ERROR_MESSAGE << "Errors occurred installing the files.\n"
    $FINAL_ERROR_MESSAGE << "#{summary}\n"
    
    # Send an email
    if EMAIL_XFER_ERRORS == ""
      print "**  To set up email notifications, update #{mail_configuration}\n"
    else
      print "** Sending report to #{EMAIL_XFER_ERRORS}.\n"
      tnow = Time.now.strftime("%Y/%m/%d-%H:%M")
      subject = "Navpoint Error(s) - #{launchpad_name} - #{tnow}"
      body = "Controller '#{CONTROLLER_IDENTIFIER}' reports that TRANSFER errors were encountered.\n\n#{summary}\n\n"
      send_deploy_result(EMAIL_XFER_ERRORS, subject, body, [])
    end
    print "**\n"
  end
  
  return rv
end

def phase_commit(launchpad_name)
  print "\n"
  print "+------------------------------------------------------------------------------------------+\n"
  print "|                                                                                          |\n"
  print "|                                       COMMIT PHASE                                       |\n"
  print "|                                                                                          |\n"
  print "+------------------------------------------------------------------------------------------+\n"
  print "launchpad is (#{launchpad_name})\n"
  print "COMMIT DOES NOTHING YET\n\n\n"
  return true
end


###########################################################################
#
# Main entry point:
# Check the launchpad has a build.xml, then run it with the provided parameters.
#
def main_showPayloadVersions
  print "main_showPayloadVersions\n"
  print "SKIPPED\n\n\n"
  return
  
  launchpad_name = ARGV[0]
  layer = ARGV[1]
  payloadName = ARGV[2]
  ControllerMisc.setupGlobals(launchpad_name)

  # Create a payload object
  payload = {}
  payload["layer"] = layer
  payload['name'] = payloadName
    
  # Check the paylaod exists and get the payload type
  checkPayload(payload)  
  payloadType = payload['type']
  #print "type=#{payloadType}\n"
  
  # Instantiate the payloadType plugin and call it  
  ntype = payloadType.gsub("-", "_")
  class_name = "PayloadType_#{ntype}"
  #print "class_name=#{class_name}\n"
  type_plugin = Object::const_get(class_name).new
  type_plugin.showVersions("test", layer, payloadName)
  
end

#
#   Start the launchpad server
#
def main_startup(launchpad_name)
  print "main_startup\n"
  print "SKIPPED\n\n\n"
  return
  
  launchpad_name = ARGV[0]

  # Set global variables
  ControllerMisc.setupGlobals(launchpad_name)
  ControllerMisc.loadGeneratorSlotDetails(launchpad_name) or bomb "Launchpad '#{launchpad_name}' does not have a generator slot assigned"

  # Start the server
  print "$GENERATOR_HTTP_PORT = #{$GENERATOR_HTTP_PORT}\n"
  ControllerLaunchpad::startup_if_not_running($GENERATOR_HTTP_PORT)
end

#
#   Start the launchpad server, and wait for it to be running
#
def main_startupAndWait(launchpad_name)
  print "main_startupAndWait\n"
  print "SKIPPED\n\n\n"
  return
  
  launchpad_name = ARGV[0]

  # Set global variables
  ControllerMisc.setupGlobals(launchpad_name)
  ControllerMisc.loadGeneratorSlotDetails(launchpad_name) or bomb "Launchpad '#{launchpad_name}' does not have a generator slot assigned"
  
  # Start the server
  ControllerLaunchpad::startup_if_not_running($GENERATOR_HTTP_PORT)
  started = ControllerLaunchpad::wait_to_start($GENERATOR_HTTP_PORT)
  if started
    #print "The launchpad is now running.\n"
  elsif
    softbomb "Could not start the launchpad."
  end
end

#
#   Shut down the launchpad server
#
def main_shutdown(launchpad_name)
  print "main_shutdown\n"
  print "SKIPPED\n\n\n"
  return
  

  # Set global variables
  ControllerMisc.setupGlobals(launchpad_name)
  ControllerMisc.loadGeneratorSlotDetails(launchpad_name) or bomb "Launchpad '#{launchpad_name}' does not have a generator slot assigned"
  
  # Start the server
  ControllerLaunchpad::shutdown_if_running($GENERATOR_HTTP_PORT)
end

#
#   Show the launchpad server's log file
#
def main_log(launchpad_name)
  launchpad_name = ARGV[0]

  # Set global variables
  ControllerMisc.setupGlobals(launchpad_name)
  tomcatLogsDir = "#{$IMAGE_DIR}/tomcat/logs"
  if RUBY_PLATFORM.downcase.include?("mswin")
    # Running on Windows. Change to the directory and use 'type'
    Dir.chdir(tomcatLogsDir) do
      cmd = "type catalina.out"
      print  "$ #{cmd}   (in #{tomcatLogsDir})\n"
      system cmd
    end
  else
      # *nix
    cmd = "tail -f #{tomcatLogsDir}/catalina.out"
    print  "$ #{cmd}\n"
    exec cmd
  end
end


###########################################################################
#
# Main entry point:
# Check the launchpad has a build.xml, then run it with the provided parameters.
#
def main_updatePayloadCache
  print "main_updatePayloadCache\n"
  print "SKIPPED\n\n\n"
  return
  
  
  launchpad_name = ARGV[0]
  layer = ARGV[1]
  payloadName = ARGV[2]
  ControllerMisc.setupGlobals(launchpad_name)

  # Create a payload object
  payload = {}
  payload["layer"] = layer
  payload['name'] = payloadName
    
  # Check the paylaod exists and get the payload type
  checkPayload(payload)  
  payloadType = payload['type']
  #print "type=#{payloadType}\n"
  
  # Instantiate the payloadType plugin and call it  
  ntype = payloadType.gsub("-", "_")
  class_name = "PayloadType_#{ntype}"
  #print "class_name=#{class_name}\n"
  type_plugin = Object::const_get(class_name).new
  type_plugin.updatePayloadCache(payload)
  
end

#
# This is just a test function, called during development
def do_some_test_thing
  print "do_some_test_thing()\n"
  ControllerMisc.setupGlobals("{{&LAUNCHPAD}}")
  #print "$IMAGE_DIR=#{$IMAGE_DIR}\n"
  port = 42001
  
  #ControllerLaunchpad::shutdown_if_running(port)
  ControllerLaunchpad::startup_if_not_running(port)
  print "ok\n"
  started = ControllerLaunchpad::wait_to_start(port)
  if started
    print "IS RUNNING NOW\n"
  elsif
    print "LAUNCHPAD IS NOT RUNNING\n"
  end

=begin
  while true
    
    success = ControllerLaunchpad::wait_to_start(port)
    
    if success
      print "SERVER RUNNING\n"
    else
      print "SERVER DID NOT START\n"
    end
    
    sleep 3
  end # while
=end
  exit
  
end

###########################################################################
# This is equivalent to main()
#

#do_some_test_thing()
print "\n\n***** NOTE: Running launchpad-8.3.rb *****\n\n\n"

# Parse the command line arguments
p = Trollop::Parser.new do
  banner <<-EOS
Usage:
    launchpad-8.3.rb <launchpad> [--from phase] [--to phase] [options]
    launchpad-8.3.rb <launchpad> [phase] [options]        
  
        where [phase] is one of:
            build (b)
            generate (g)
            validate (v)
            transfer (t) or xfer (x)
            commit (c)
      
Specific phases can be run with:
    launchpad-8.3.rb <launchpad> build [options] ZZZZ
    launchpad-8.3.rb <launchpad> generate
    launchpad-8.3.rb <launchpad> validate ZZZZ
    launchpad-8.3.rb <launchpad> transfer
    launchpad-8.3.rb <launchpad> xfer
    launchpad-8.3.rb <launchpad> commit ZZZZ

Launchpad server commands:
    launchpad-8.3.rb <launchpad> start ZZZZ
    launchpad-8.3.rb <launchpad> startAndWait ZZZZ
    launchpad-8.3.rb <launchpad> stop ZZZZ
    launchpad-8.3.rb <launchpad> log

Utility variants:
    launchpad-8.3.rb --show-payload-versions <launchpad> <layer> <payload>
    launchpad-8.3.rb --update-payload-cache <launchpad> <layer> <payload>
  
Options:
EOS
  opt :skip_cache_checks, "Assume the cached git repositories are up to date.", :short => "-s"
  opt :skip_git_checks, "Assume all git repositories are up to date.", :short => "-S"
  opt :skip_remote_checks, "Skip checking that the 'remote' values for git are correct.", :short => "-r"
  opt :from, "Update the cache for  payload.", :short => "-f", :type => String, :default => "-" #ControllerPhase::BUILD
  opt :to, "Update the cache for  payload.", :short => "-t", :type => String, :default => "-" #ControllerPhase::COMMIT
  
  opt :show_payload_versions, "Show the available versions of a payload.", :short => "-v"
  opt :update_payload_cache, "Update the cache for  payload.", :short => "-u"
  opt :clean, "Clean out caches before build/generate.", :short => "-C"
  opt :navpoints, "Only generate specific navpoints/hierarchies.", :short => "-n", :type => String, :default => ""
end
Trollop::with_standard_exception_handling p do
  $command_line_options = p.parse ARGV
 # print "\n#{ARGV.length}\n"

  
  # Decide what to do
  $FINAL_ERROR_MESSAGE = ""
  if $command_line_options.show_payload_versions
    # Show versions of a payload
    raise Trollop::HelpNeeded if not ARGV.length == 3 # show help screen
    main_showPayloadVersions
    
  elsif $command_line_options.update_payload_cache
    # Update any caching for a specific payload
    raise Trollop::HelpNeeded if not ARGV.length == 3 # show help screen
    main_updatePayloadCache

  #
  #   Specific phases
  #
  elsif ARGV.length == 2 and ARGV[1] == "build"
    phase_build(ARGV[0])
  elsif ARGV.length == 2 and ARGV[1] == "generate"
    phase_generate(ARGV[0])
  elsif ARGV.length == 2 and ARGV[1] == "validate"
    phase_validate(ARGV[0])
  elsif ARGV.length == 2 and (ARGV[1] == "transfer" or ARGV[1] == "xfer")
    phase_transfer(ARGV[0])
  elsif ARGV.length == 2 and ARGV[1] == "commit"
    phase_commit(ARGV[0])

  #
  #   Server commands
  #
  elsif ARGV.length == 2 and (ARGV[1] == "startup" or ARGV[1] == "start")
    main_startup(ARGV[0])
  elsif ARGV.length == 2 and (ARGV[1] == "startupAndWait" or ARGV[1] == "startAndWait")
    main_startupAndWait(ARGV[0])
  elsif ARGV.length == 2 and (ARGV[1] == "shutdown" or ARGV[1] == "stop")
    main_shutdown(ARGV[0])
  elsif ARGV.length == 2 and ARGV[1] == "log"
    main_log(ARGV[0])

  #
  #   Run a range of phases
  #    
  elsif ARGV.length == 1 or (ARGV.length == 2 and ARGV[1] == "deploy")
    
    launchpad_name = ARGV[0];
    
#    print "launchpad is #{launchpad_name}\n"
    
    argFrom =  $command_line_options.from
    envFrom = ENV["FROM"]
    from = ControllerPhase.fromName(argFrom, envFrom, ControllerPhase::BUILD)
    raise Trollop::HelpNeeded if from == nil
    
    argTo =  $command_line_options.to
    envTo = ENV["TO"]
    to = ControllerPhase.fromName(argTo, envTo, ControllerPhase::COMMIT)
    raise Trollop::HelpNeeded if to == nil

    # Prepare the global variables
    ControllerMisc.setupGlobals(launchpad_name)
#    ControllerMisc.loadLaunchpadDetails(launchpad_name)
    $LAUNCHPAD_TYPE = 'test'
    ControllerMisc.loadGeneratorSlotDetails(launchpad_name) or bomb "Launchpad '#{launchpad_name}' does not have a generator slot assigned"
    ControllerMisc.loadServerSlotDetails(launchpad_name) or bomb "Launchpad '#{launchpad_name}' does not have a server slot assigned"

    # Run each of the phases in turn
    (from .. to).each do |phase|
      #print "phase is #{phase}\n"
      run_phase(phase, launchpad_name)
    end

  else

    # Invalid command
    raise Trollop::HelpNeeded

  end # args

  #
  # Display the final status
  #
  print "launchpad-8.3.rb complete\n"
  print "\n"
#  print "+------------------------------------------------------------------------------------------+\n"
#  print "|                                                                                          |\n"
#  print "|                                        END OF RUN                                        |\n"
#  print "|                                                                                          |\n"
#  print "+------------------------------------------------------------------------------------------+\n"
#  print "\n"
  if $FINAL_ERROR_MESSAGE == ""
    print "No errors detected.\n"
  else  
    print "Errors have occurred:\n"
    print $FINAL_ERROR_MESSAGE
  end
  print "\n"
  
  
end # Trollop
