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

import os
import sys
import SCons
import shutil

#TODO:
#-figure out what files actually need what defines and write separate builder lines for them (so that changing a #define doesn't cause a rebuild of everything)
#-
#-

# BIG FAT WARNING:
# Make sure you use TABS for indentation, NOT spaces! (Python likes spaces well enough, but would much prefer to share tea time with tabs)
#
#   ####       ####  ##########  #####    #####  #####    #####  #####    #####
#   ######   ######     ####      #####  #####    #####  #####    #####  #####
#   #### ## ## ####     ####        ########        ########        ########
#   ####   #   ####     ####      #####  #####    #####  #####    #####  #####
#   ####       ####  ##########  #####    #####  #####    #####  #####    #####
#
#  #############################################################################
#    #########################################################################
#

#
#Useful functions
#

print "WE ARE IN:", os.getcwd()

plugins = []


###### MAIN LINE ######
#######################


Import('build_dir')
Import('platformString')
Import('env')
Import('sources')
Import('flags')
Import('soundsource_plugins') #Grab these from the SConstruct above us
Import('bitwidth')


#Tell SCons to build Mixxx
#=========================
if 'win' in platformString:
	dist_dir = 'dist'+bitwidth
	mixxx_bin = env.Program('mixxx', sources, LINKCOM  = [env['LINKCOM'], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;1'])
else:
	mixxx_bin = env.Program('mixxx', sources)



#Set up the install target
#=========================
"""
flags['prefix'] = ARGUMENTS.get('prefix', '/usr/local')
if not os.path.exists(flags['prefix']):
	print "Error: Prefix path does not exist!"
	Exit(1)
else:
	unix_share_path = flags['prefix'] + "/share"
	unix_bin_path   = flags['prefix'] + "/bin"
"""

#Mixxx binary
binary_files = mixxx_bin;

# Mixxx essential resource files
resource_files = Glob('#res/schema.xml')

#Soundsource plugins
soundsource_plugin_files = soundsource_plugins

#Skins
skin_files = Glob('#res/skins/*')

#MIDI mappings
midimappings_files = Glob('#res/midi/*')

#Keyboard mapping(s)
keyboardmappings_files = Glob('#res/keyboard/*')

#Promo tracks
promotracks_files = Glob('#res/promo/*')

#LADSPA shizzle
ladspapresets_files = Glob('#res/ladspa_presets/*')

#Documentation
docs_files = Glob('#LICENSE')
docs_files += Glob('#README')
docs_files += Glob('#Mixxx-Manual.pdf')

#.desktop file for KDE/GNOME menu
dotdesktop_files = Glob('#src/mixxx.desktop')

#Icon file for menu entry
icon_files = Glob('#res/images/mixxx-icon.png')

#Images for preferences dialog
image_files = Glob('#res/images/preferences/*')  # These are compiled in to the "mixxx" binary through mixxx.qrc

#Windows DLLs
dll_files = Glob('#/../mixxx-win' + bitwidth + 'lib-msvc/[!"msvc"]*.dll') # TODO: Use reference to SharedLibrary for libsndfile and others, glob only gets all files on 2+ builds after a clean.
# dll_files = libsndfile
dll_files += Split("""$QTDIR/bin/Qt3Support4.dll $QTDIR/bin/QtCore4.dll $QTDIR/bin/QtGui4.dll $QTDIR/bin/QtNetwork4.dll $QTDIR/bin/QtOpenGL4.dll $QTDIR/bin/QtSql4.dll $QTDIR/bin/QtXml4.dll $QTDIR/bin/QtXmlPatterns4.dll $QTDIR/bin/QtSql4.dll $QTDIR/bin/QtSVG4.dll $QTDIR/bin/QtWebKit4.dll
$QTDIR/bin/phonon4.dll""")

if int(flags['script']) or int(flags['midiscript']):
	dll_files += Split("""$QTDIR/bin/QtScript4.dll""")



if platformString in ('linux', 'bsd'):
	flags['prefix'] = ARGUMENTS.get('prefix', '/usr/local')
	if not os.path.exists(flags['prefix']):
		print "Error: Prefix path does not exist!"
		Exit(1)
	else:
		#install_root is used in Debian/Ubuntu packaging (check the debian/rules file in the Ubuntu package)
		#Basically, the flags['prefix'] is compiled into strings in Mixxx, whereas the install_root is not. When you're
		#building a Debian package, pbuilder wants to install Mixxx to a temporary directory, but you still need
		#the compiled-in strings using /usr as the prefix. That's why we have install_root and flags['prefix'].
		install_root = ARGUMENTS.get('install_root', flags['prefix'])
		print "Install root: " + install_root
		if install_root != flags['prefix']:
			unix_share_path = install_root + "/share"
			unix_bin_path   = install_root + "/bin"
			unix_lib_path   = install_root + "/lib"
		else:
			unix_share_path = flags['prefix'] + "/share"
			unix_bin_path   = flags['prefix'] + "/bin"
			unix_lib_path   = flags['prefix'] + "/lib"

		binary = env.Install(unix_bin_path, binary_files)
		resource = env.Install(unix_share_path + "/mixxx", resource_files)
		skins = env.Install(unix_share_path + "/mixxx/skins", skin_files)
		soundsource_plugins = env.Install(unix_lib_path + "/mixxx/plugins/soundsource", soundsource_plugin_files)
		midimappings = env.Install(unix_share_path + "/mixxx/midi", midimappings_files)
		keyboardmappings = env.Install(unix_share_path + "/mixxx/keyboard", keyboardmappings_files)
		if int(flags['ladspa']):
			ladspapresets = env.Install(unix_share_path + "/mixxx/ladspa_presets", ladspapresets_files)
		dotdesktop = env.Install(unix_share_path + "/applications", dotdesktop_files)
		docs = env.Install(unix_share_path + "/doc/mixxx", docs_files)
		icon = env.Install(unix_share_path + "/pixmaps", icon_files)
		promotracks = env.Install(unix_share_path + "/mixxx/promo", promotracks_files)

		#Makes each of those Install builders get fired off when you run "scons install" :)
		env.Alias('install', binary)
		env.Alias('install', resource)
		env.Alias('install', skins)
		env.Alias('install', soundsource_plugins)
		env.Alias('install', midimappings)
		env.Alias('install', keyboardmappings)
		if int(flags['ladspa']):
			env.Alias('install', ladspapresets)
		env.Alias('install', docs)
		env.Alias('install', dotdesktop)
		env.Alias('install', icon)
		env.Alias('install', promotracks)

		#Delete the old Mixxx installation (because SCONS won't overwrite it)
		#if 'install' in COMMAND_LINE_TARGETS:
			#os.system('scons -c install')
			#Delete(unix_share_path + "/mixxx/skins")
			#print "Copying skins..."
			#env.Command(unix_share_path + "/mixxx/skins", skin_files, Copy("$TARGET", "$SOURCE"), source_scanner = DirScanner)
			#Copy(unix_share_path + "/.ixxx/skins", skin_files)
			#Delete(unix_bin_path + "mixxx")

			#Delete(unix_share_path + "/mixxx/midi")
			#Delete(unix_share_path + "/mixxx/keyboard")

#Build the Mixxx.app bundle
if platformString == 'osx' and 'bundle' in COMMAND_LINE_TARGETS:
	#Mixxx build variables
	VOLNAME="Mixxx" #tmp tmp tmp, it's unclean to pass this into build_dmg this way. perhaps pass it in the env?
	ARCH="macintel" #XXX should get this from scons or the system somehow?
	DMG_ICON="#res/osx/VolumeIcon.icns"

	#this is a BIG HACK to support Qt's plugins (since Qt *requires* that it's plugins be in specific subdirectories, which OS X doesn't really play nice with)
	qt_plugins = ([("iconengines", e) for e in ["libqsvgicon.dylib"]] +
                      [("imageformats", e) for e in ["libqgif.dylib", "libqjpeg.dylib", "libqsvg.dylib"]] +
                      [("sqldrivers", e) for e in ["libqsqlite.dylib"]])
	#Left out libqmng and libqtiff to save space.

	#Concatenate the SoundSource plugins to our list of plugins
    #(converting from SCons File nodes to strings)
	for x in soundsource_plugins:
		plugins.append(x.get_abspath())

	bundle = env.App("Mixxx", [mixxx_bin, '#res/osx/application.icns',
		Dir('#res/skins/'),
		File('#res/schema.xml'),
		Dir('#res/midi/'), Dir('#res/keyboard/'),
		Dir('#res/ladspa_presets'), Dir('#res/doc/'), Dir('#res/promo/')],
		 PLUGINS=plugins, ##XXX test what happens if we don't pass any plugins
			#Qt plugins ((Qt *NEEDS* its plugins in specific locations or it refuses to find them, however this is clearly awkward to write out like this.. maybe))
		 QT_HACK = [(p_tgt_dir, os.path.join("/Developer/Applications/Qt/plugins/", p_tgt_dir, p)) for p_tgt_dir, p in qt_plugins] #sigh :(
		 ,
		 STRIP=True,
		 #STRIP=(type == 'RELEASE')
		 )
	#env.Default(mixxx_bin) #todo: make the Default() just the program itself *globally* (not just for OS X); bundle should be a separate target
	env.Alias('bundle', bundle)
	dmg = env.Dmg('Mixxx-'+env['MIXXX_VERSION']+'-'+ARCH, [bundle, ] + docs_files, VOLNAME=VOLNAME, ICON = DMG_ICON)
	env.Alias('package', dmg)

if 'win' in platformString:
	skins = env.Install("#"+dist_dir+"/skins", skin_files)
	resource = env.Install("#"+dist_dir+"/", resource_files)
	midimappings = env.Install("#"+dist_dir+"/midi", midimappings_files)
	keyboardmappings = env.Install("#"+dist_dir+"/keyboard", keyboardmappings_files)
	if int(flags['ladspa']):
		ladspapresets = env.Install("#"+dist_dir+"/ladspa_presets", ladspapresets_files)
	docs = env.Install("#"+dist_dir+"/doc/", docs_files)
	promotracks = env.Install("#"+dist_dir+"/promo/", promotracks_files)
	#icon = env.Install("#"+dist_dir+"", icon_files)
	dlls = env.Install("#"+dist_dir+"/", dll_files)
	soundsource_plugins = env.Install("#"+dist_dir+"/plugins/soundsource/", soundsource_plugin_files)
	binary = env.Install("#"+dist_dir+"/", binary_files)

	#Always trigger these install builders when compiling on Windows
	env.Alias('mixxx', skins)
	env.Alias('mixxx', resource)
	env.Alias('mixxx', midimappings)
	env.Alias('mixxx', keyboardmappings)
	if int(flags['ladspa']):
		env.Alias('mixxx', ladspapresets)
	env.Alias('mixxx', promotracks)
	env.Alias('mixxx', docs)
	env.Alias('mixxx', dlls)
	env.Alias('mixxx', soundsource_plugins)
	#env.Alias('mixxx', icon)
	env.Alias('mixxx', binary)

	def cleanSVNDirsFromDist():
		if os.path.exists(dist_dir):
			print "Cleaning .svn directories from "+dist_dir+"... ",
			os.system('cmd.exe /c @FOR /F "tokens=*" %D IN (\'dir /b /ad /s '+dist_dir+'\*.svn*\') do @(rd /s /q "%D") 2> NUL')
			print "Done."
	import atexit
	atexit.register(cleanSVNDirsFromDist)

def BuildRelease(target, source, env):
	print
	print "==== Mixxx Post-Build Checks ===="
	print
	print "You have built version ", env['MIXXX_VERSION']
	print
	if int(flags['msvcdebug']):
		print "YOU ARE ABOUT TO PACKAGE A DEBUG BUILD!!"
		print
	print "Binary has size ",
	os.system('ls -lh '+dist_dir+'/mixxx.exe | cut -d \' \' -f 5')
	print
	print "Installer file ",
	os.system('grep OutFile Mixxx.nsi | cut -d \' \' -f 2')
	print
	print "Top line of README, check version:"
	os.system('head -n 1 README')
	print
	print "Top 2 lines of LICENSE, check version and copyright dates:"
	os.system('head -n 2 LICENSE')
	print
	print "More checks here soon... :)"
	print

	if (raw_input("Go ahead and build installer (yes/[no])? ") == "yes"):
		print "\nNow building installer..."

		buildwin64 = ""
		type(buildwin64) == str

		# Windows registry access to find where NSIS is installed
		import _winreg
		hklm = _winreg.ConnectRegistry( None, _winreg.HKEY_LOCAL_MACHINE )
		nsis_location_handle = ""
		try:
			nsis_location_handle = _winreg.OpenKey(hklm, "SOFTWARE\\NSIS", 0, _winreg.KEY_READ)
		except WindowsError:
			nsis_location_handle = None

		if not nsis_location_handle:
			try:
				nsis_location_handle = _winreg.OpenKey(hklm, "SOFTWARE\\Wow6432Node\\NSIS", 0, _winreg.KEY_READ)
			except WindowsError:
				nsis_location_handle = None

		if not nsis_location_handle:
			print "Cannot find NSIS. Do you have it installed?"
		else:
			if bitwidth == '64':
				buildwin64 = "/Dx64=1"
			nsis_location = _winreg.QueryValue(nsis_location_handle, None)
			_winreg.CloseKey(hklm)

			# Call the NSIS build
			command = '\"%(path)s\\makensis.exe\" /DPRODUCT_VERSION=%(version)s %(64bit)s build\\nsis\\Mixxx.nsi' % {'path':nsis_location, \
'version':env['MIXXX_VERSION'], '64bit':buildwin64}
			type(command) == str
			print "Using command:" + command
			os.system(command)
	else:
		print "Aborted building installer"

# Do release things
versionbld = Builder(action = BuildRelease, suffix = '.foo', src_suffix = '.bar')
env.Append(BUILDERS = {'BuildRelease' : versionbld})

if 'makerelease' in COMMAND_LINE_TARGETS:
	makerelease = env.BuildRelease('', binary_files)
	env.Alias('makerelease', makerelease)

#Build the Ubuntu package
def BuildUbuntuPackage(target, source, env):
	print
	print "==== Mixxx Post-Build Checks ===="
	print
	print "You have built version ", env['MIXXX_VERSION']
	print
	print
	print "Top line of README, check version:"
	os.system('head -n 1 README')
	print
	print "Top 2 lines of LICENSE, check version and copyright dates:"
	os.system('head -n 2 LICENSE')
	print
	print "Top line of debian/ubuntu changelog, check version:"
	os.system('head -n 1 build/debian/changelog')
	print

	if ("yes" == "yes"):
		print "Now building DEB package..."
                print
		mixxx_dir = 'mixxx-' + env['MIXXX_VERSION']
		mixxx_tarball = 'mixxx_' + env['MIXXX_VERSION'] + '.orig.tar.gz' #The underscore is super important here to make the deb package work

		if not os.path.exists('ubuntu'):
			os.mkdir('ubuntu')

		if os.path.exists('ubuntu/'+mixxx_dir):
			print "* Cleaning up ubuntu/"+mixxx_dir + " (cwd: " + os.getcwd() + ")"
			print
			os.system('rm -rf ubuntu/'+mixxx_dir)

		# TODO: make a get flags arg to accept a revision which can override this and checkout of a specific SVN rev for the package

		# Export the source folder
		print "* Exporting source folder from current workspace (bzr rev: " + getBZRRevision() + ")"
		print
		os.system('bzr export ubuntu/'+mixxx_dir + ' .')

		# Copy a patch to be included in the exported build sources (this can also be something like src/SConscript, /build/debian/rules)
		if os.path.exists('post-export-patch'):
			print "* Applying post export patch"
			print
			os.system('cp --dereference -r post-export-patch/* ubuntu/'+mixxx_dir)

		os.chdir('ubuntu')
		# Tar the source code
		print "* Tarring source directory to "+ os.getcwd() +"/mixxx_" + env['MIXXX_VERSION'] + ".orig.tar.gz ... (this can take a couple minutes)"
		print
		os.system("rm -f mixxx_" + env['MIXXX_VERSION'] + ".orig.tar.gz") #Remove old tarball
		os.system('tar --exclude=debian --exclude=debian/* -czf  mixxx_' + env['MIXXX_VERSION'] + '.orig.tar.gz ' + mixxx_dir)

		os.chdir(mixxx_dir)
		# Copy the debian folder from /build/debian to exported source folder root
		print "* Copying Debian build directory from build/debian to debian (cwd: " + os.getcwd() + ")"
		print
		os.system('cp -r build/debian .')

		#Run pbuilder
		print "* Starting pbuilder ...  (cwd: " + os.getcwd() + ")"
		print
		os.system('pdebuild')

		# Return back to the starting directory, otherwise you'll get a .sconsign.dblite error!
		os.chdir('../..')
		print "* Returning to starting working directory ...  (cwd: " + os.getcwd() + ")"
		print

		#/var/cache/pbuilder/result
		print
		# print "Signing the .deb changes file..."
		# os.system('sudo debsign /var/cache/pbuilder/result/*.changes')

		print "Done! Package and tarballs are in /var/cache/pbuilder/result"

	else:
		print "Aborted building installer"

#Build the Ubuntu package if "makeubuntu" was passed as an argument
versiondebbld = Builder(action = BuildUbuntuPackage) #, suffix = '.foo', src_suffix = '.bar')
env.Append(BUILDERS = {'BuildUbuntuPackage' : versiondebbld})

if 'makeubuntu' in COMMAND_LINE_TARGETS:
	makeubuntu = env.BuildUbuntuPackage("blah", "defs_version.h" ) #(binary_files)
	env.Alias('makeubuntu', makeubuntu)
