#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import os
import ast
import shutil
import zipfile
import argparse
import html2text
import urllib2
import tarfile
import re
import subprocess


def wget_action(conf, args):
	print "Downloading archives to '%s'" % args.downloads_dir
	for name, package in conf.items():
		dst = os.path.join(args.downloads_dir, package['filename'])
		print "  %s ..." % dst,
		if os.path.isfile(dst):
			print "already there, skipping"
		else:
			print "downloading from '%s' ..." % package['url'],
			request = urllib2.urlopen(package['url'])
			content = request.read()
			with file(dst, 'wb') as f:
				f.write(content)
			print "done!"
		


def extract_action(conf, args):
	print "Extracting archives to '%s'" % args.int_dir

	if os.path.isdir(args.int_dir):
		print "Intermediate directory '%s' already exists, wiping out ..." % args.int_dir
		shutil.rmtree(args.int_dir)
	os.mkdir(args.int_dir)

	for name, package in conf.items():
		
		extract_action = 'extract'
		skip_root = False
		if 'extract' in package:
			if package['extract'] == 'skip-root':
				skip_root = True
			elif package['extract'] == 'copy':
				extract_action = 'copy'
			else:
				raise ValueError("Unknown 'extract' action in package '%s': %s" % (name, package['extract']))
	
		arc_filepath = os.path.join(args.downloads_dir, package['filename'])
		
		if extract_action == 'extract':
		
			dst_parent_dir = os.path.join(args.int_dir, name)
			print "Uncompressing '%s' ..." % package['filename']
			basename, extension = os.path.splitext(package['filename'])
			if extension == '.zip':
				with zipfile.ZipFile(arc_filepath) as zfile:
					for item in zfile.namelist():
						dst_dir, dst_name = os.path.split(item)
						if skip_root:
							dst_path = os.path.normpath(os.path.join(dst_parent_dir, '..', dst_dir))
						else:
							dst_path = os.path.join(dst_parent_dir, dst_dir)
						#print "Decompressing '%s' to '%s' (%s)" % (dst_name, dst_dir, dst_path)
						if not os.path.exists(dst_path):
							os.makedirs(dst_path)
						if dst_name:
							dst_filepath = os.path.join(dst_path, dst_name)
							#print "  ->",dst_filepath
							with open(dst_filepath,"wb") as dst:
								dst.write(zfile.read(item))
			
			elif extension == '.gz' and os.path.splitext(basename)[1] == '.tar':
				itar = tarfile.open(arc_filepath, "r:gz")
				for itarinfo in itar:
					itar.extract(itarinfo, dst_parent_dir)
			
			else:
				raise Exception("Don't know what to do uncompress file '%s'" % arc_filepath)
				
		elif extract_action == 'copy':
			print "Copying '%s' ..." % package['filename']
			dst_dir = os.path.join(args.int_dir, 'runtimes')
			if not os.path.isdir(dst_dir):
				os.makedirs(dst_dir)
			dst = os.path.join(dst_dir, package['filename'])
			shutil.copy(arc_filepath, dst)
			
			
def conf_action(conf, args):
	print "Copying configuration files to '%s'" % args.int_dir
	config = {}
	for name, package in conf.items():
		
		if 'conf' not in package:
			continue

		base, ext = os.path.splitext(package['conf'])
		conf_src = os.path.join(args.confs_dir, base+'-'+package['version']+ext)

		if 'conf-target-dir' in package:
			# Put the configuration file at the right place now
			conf_dst = os.path.join(args.int_dir, name, package['conf-target-dir'], package['conf'])
		else:
			# Copy the configuration file to confs/ for later use
			dst_path = os.path.join(args.int_dir, 'confs')
			if not os.path.isdir(dst_path):
				os.makedirs(dst_path)
			conf_dst = os.path.join(dst_path, package['conf'])
		
		print "  %s -> %s" % (conf_src, conf_dst)
		shutil.copy(conf_src, conf_dst)

		# Gather TCP port numbers from the config files, so as to write
		# our own config file.
		if name == 'apache':
			rsrc = r'^Listen (\d+)'
		elif name == 'pgsql':
			rsrc = r'^port = (\d+)'
		else:
			rsrc = None
		if rsrc:
			r = re.compile(rsrc)
			with file(conf_dst,'rb') as f:
				for line in f:
					res = r.search(line)
					if res:
						port = int(res.group(1))
						config[name] = {'port':port}
						break
		
	config_filename = os.path.join(args.int_dir, 'pws.conf')
	print "  Writing PWS configuration file '%s'" % config_filename
	with file(config_filename,'wb') as f:
		f.write(repr(config))
		
		
def license_action(conf, args):
	license_file = 'LICENSE.txt'
	license_path = os.path.join(args.int_dir, license_file)
	header = '''
===============================================================================
                     PiAnoS Windows Server License
-------------------------------------------------------------------------------
This application contains multiple components, each of which is licensed under
its own terms. All of them are open-source software, according to the Open 
Source Initiative (OSI). You can find the specific terms of each license below.

Component        License
--------------   -----------------------------
PiAnoS           GNU Affero GPL, v3 (AGPL v3)
PostgreSQL       The PostgreSQL License
Apache           The Apache License, v2.0
PHP              The PHP License, v3.01

'''
	license_header = '''
===============================================================================
%s
-------------------------------------------------------------------------------
'''
	print "Creating license file '%s'" % license_file
	with file(license_path,'w') as f:
		f.write(header)
		for name in ['pianos','pgsql','apache','php']:
			package = conf[name]
			src = os.path.join(args.int_dir, name, package['license'])
			print "Appending '%s'" % src
			with file(src,'r') as f2:
				f.write(license_header % '{: ^80}'.format('License for '+package['display-name']))
				_,ext = os.path.splitext(package['license'])
				if ext == '.html':
					content = html2text.html2text(f2.read())
				else:
					content = f2.read()
				f.write(content)
				

def exe_action(conf, args):
	print "Compiling GUI application using py2exe ..."
	subprocess.check_call(['py_setup_pws.py', 'py2exe'], shell=True)
	dst = os.path.join(args.int_dir, 'gui')
	print "  Copying result to output folder ..."
	if os.path.isdir(dst):
		shutil.rmtree(dst)
	shutil.copytree('py2exe_dist', dst)
	print "  Creating main batch file"
	batch_filename = os.path.join(args.int_dir, 'start_pianos_server.bat')
	with file(batch_filename, 'w') as f:
		f.write('''@echo off
SETLOCAL
echo Starting PiAnoS Windows Server GUI, please wait ...
cd gui
pianos_winsrv.exe "..\"
ENDLOCAL''')
	shutil.rmtree('py2exe_build')
	shutil.rmtree('py2exe_dist')
		
def a2help_action(conf, args):
	print "Compiling Apache 2 helper scripts ..."
	vcvars_batch = os.path.normpath(os.path.join(os.getenv('VS90COMNTOOLS'),'..','..','VC','vcvarsall.bat'))
	solution_name = 'pianos_apache_helper.sln'
	solution_dir = os.path.join('apache_helper','vstudio')
	msbuild_cmd = 'msbuild %s /target:Rebuild /p:VCBuildAdditionalOptions=" /useenv",Configuration="%s",Platform="%s"' % (solution_name, 'Release', 'Win32')
	batch_code = '''@echo off
SETLOCAL
call "%s" x86
cd "%s"
%s
ENDLOCAL
''' % (vcvars_batch, solution_dir, msbuild_cmd)
	batch_name = 'comp_a2help.bat'
	with file(batch_name,'w') as f:
		f.write(batch_code)
	subprocess.check_call([batch_name])
	os.unlink(batch_name)
		
def post_action(conf, args):
	print "Post-processing ..."
	name = 'www-default'
	www_src = os.path.join(args.confs_dir, name)
	www_dst = os.path.join(args.int_dir, name)
	if os.path.isdir(www_dst):
		print "  Removing existing directory '%s'" % www_dst
		shutil.rmtree(www_dst)
	print "  Copying '%s' to '%s'" % (www_src, www_dst)
	shutil.copytree(www_src, www_dst)
	
	print "  Creating 'var' directory"
	var_dir = os.path.join(args.int_dir, 'var')
	if not os.path.isdir(var_dir):
		os.mkdir(var_dir)
	
	print "  Copying Apache helper scripts"
	cpp_rel_dir = os.path.join('apache_helper', 'vstudio', 'Release')
	dst_dir = os.path.join(args.int_dir, 'apache', 'Apache24', 'bin')
	for script in ['pianos_apache_helper.exe', 'trigger_apache_stop_event.exe']:
		print "    %s" % script
		src = os.path.join(cpp_rel_dir, script)
		dst = os.path.join(dst_dir, script)
		shutil.copy(src, dst)
	
	
def all_action(conf, args):
	wget_action(conf, args)
	extract_action(conf, args)
	conf_action(conf, args)
	license_action(conf, args)
	exe_action(conf, args)
	a2help_action(conf, args)
	post_action(conf, args)
	
def read_config_file(filename):
	print "Loading configuration file '%s'" % filename
	with file(filename,'r') as f:
		conf = ast.literal_eval(f.read())
		return conf
		
def main():

	default_config_file = 'downloads.conf'
	default_output_dir = 'dist'
	default_int_dir = 'build'
	default_downloads_dir = 'downloads'
	default_confs_dir = 'confs'

	parser = argparse.ArgumentParser(description="A tool to create the PiAnoS Windows Server Installer.")
	
	parser.add_argument('-c', '--config-file', metavar='FILENAME', help="Configuration file. Defaults to '%s'" % default_config_file, default=default_config_file)
	parser.add_argument('-o', '--output-dir', metavar='PATH', help="Output directory. Defaults to '%s'" % default_output_dir, default=default_output_dir)
	parser.add_argument('-i', '--int-dir', metavar='PATH', help="Intermediate directory. Defaults to '%s'" % default_int_dir, default=default_int_dir)
	parser.add_argument('--downloads-dir', metavar='PATH', help="Directory where downloads are to be found. Defaults to '%s'" % default_downloads_dir, default=default_downloads_dir)
	parser.add_argument('--confs-dir', metavar='PATH', help="Directory where configuration files are to be found. Defaults to '%s'" % default_confs_dir, default=default_confs_dir)

	subparsers = parser.add_subparsers(dest='action')

	wget = subparsers.add_parser('wget', help="download the packages from upstream vendor")
	wget.set_defaults(func=wget_action)

	extract = subparsers.add_parser('extract', help="extract the .zip archives")
	#extract.add_argument('-f', '--force', action='store_true', help="Overwrite output directory if it exists", default=False)
	extract.set_defaults(func=extract_action)
	
	conf = subparsers.add_parser('conf', help="copy the configuration files")
	#extract.add_argument('-f', '--force', action='store_true', help="Overwrite output directory if it exists", default=False)
	conf.set_defaults(func=conf_action)
	
	license = subparsers.add_parser('license', help="create the license file")
	license.set_defaults(func=license_action)

	exe = subparsers.add_parser('exe', help="compile the GUI application using py2exe and copy output")
	exe.set_defaults(func=exe_action)

	a2help = subparsers.add_parser('a2help', help="compile the Apache start/stop helper scripts")
	a2help.set_defaults(func=a2help_action)

	post = subparsers.add_parser('post', help="perform post-processing steps")
	post.set_defaults(func=post_action)

	all = subparsers.add_parser('all', help="perform all actions in sequence (wget, extract, conf, license)")
	all.set_defaults(func=all_action)

	args = parser.parse_args()
	
	conf = read_config_file(args.config_file)
	
	return args.func(conf, args)
	
	
if __name__=='__main__':
	sys.exit(main())