#!/usr/bin/python
EnsureSConsVersion(0, 96, 92)
import sys
import os
import glob

sys.path.append( os.path.abspath("scons/libs/") )
from clam_build_helpers import *
from clam_dependent_libs_checks import *
# TODO: Must solve the dependency loop of options and platforms
crosscompiling = False
isWindowsPlatform = sys.platform=='win32' or crosscompiling
isLinuxPlatform = sys.platform=='linux2' and not crosscompiling
isDarwinPlatform = sys.platform=='darwin'

def load_config_file_to_env( env, dir ):
	opts = Options(dir+'/flags.conf')
	opts.Add( 'CPPPATH', 'CPP PATH')
	opts.Add( 'CCFLAGS', 'CC FLAGS')
	opts.Add( 'CPPFLAGS', 'CPP FLAGS')
	opts.Add( 'LIBPATH', 'LIB PATH')
	opts.Add( 'LIBS', 'libs to link')
	opts.Add( 'LINKFLAGS', 'LINKFLAGS') # This one added because on Mac OS X we need additional '-framework OpenGL && AGL'
	opts.Add( 'pkg_config_available', 'PKG Config Available', 'False')
	opts.Update(env)

def save_config_file_from_env( env, dir ):
	# workaround due to bug in scons (if we add the flag after loading it will add "" to the new flagand will fail
	opts = Options()
	opts.Add( 'CPPPATH', 'CPP PATH', '')
	opts.Add( 'CCFLAGS', 'CC FLAGS', '')
	opts.Add( 'CPPFLAGS', 'CPP FLAGS', '')
	opts.Add( 'LIBPATH', 'LIB PATH', '')
	opts.Add( 'LIBS', 'libs to link', env['LIBS'])
	opts.Add( 'LINKFLAGS', 'LINKFLAGS', '') # This one added because on Mac OS X we need additional '-framework OpenGL && AGL'
	opts.Add( 'pkg_config_available', 'PKG Config Available', 'False')
	opts.Save(dir+'/flags.conf', env)

def configure_clam(clam_env) :
	print """\
############################################
### CLAM GLOBAL DEPENDENCIES CHECKING    ###
############################################"""
	# Sandbox setup
	if isWindowsPlatform :
		libbasenames = [ 'xercesc', 'fftw', 'fftw3', 'dxsdk', 'id3lib', 'libmad', 'libsndfile','oggvorbis','portmidi','portaudio','pthreads']
		for basename in libbasenames :
			basePath = os.path.join(clam_env['sandbox_path'], basename)
			clam_env.Append( CPPPATH = [os.path.join(basePath,'include')] )
			clam_env.Append( LIBPATH = [os.path.join(basePath,'lib')] )
		environmentIncludes = ""
		if os.environ.has_key('INCLUDE') : environmentIncludes = os.environ['INCLUDE']
		environmentIncludesList = environmentIncludes.split(';')
		for include in environmentIncludesList :
			if not include : continue
			print( 'adding include dir from windows config: ' + include )
			clam_env.Append( CPPPATH = [include] )
	if isLinuxPlatform :
		clam_env.Append( CPPPATH= ['/usr/local/include'] )
	if isDarwinPlatform :
		clam_env.Append( CPPPATH= ['/usr/local/include', '/opt/local/include'] )


	conf = Configure( clam_env, custom_tests = custom_check_routines )
	if not setup_global_environment( clam_env, conf ) :
		Exit(1)
	clam_env = conf.Finish()

def configure_core(clam_env) :
	print """\
#########################################
### CLAM CORE DEPENDENCIES CHECKING   ###
#########################################"""
	core_env = clam_env.Copy()
	conf = Configure( core_env, custom_tests = custom_check_routines )
	if not setup_core_environment( core_env, conf ) :
		Exit(1)
	core_env = conf.Finish()
	clam_env['xmlbackend'] = core_env['xmlbackend']
	return core_env

def configure_processing(core_env) :
	print """\
###############################################
### CLAM PROCESSING DEPENDENCIES CHECKING   ###
###############################################"""
	processing_env = core_env.Copy()
	conf = Configure( processing_env, custom_check_routines )
	if not setup_processing_environment( processing_env, conf ) :
		Exit(1)
	processing_env = conf.Finish()
	return processing_env

def configure_audioio(processing_env) :
	print """\
############################################
### CLAM AUDIOIO DEPENDENCIES CHECKING   ###
############################################"""
	audioio_env = processing_env.Copy()

	conf = Configure( audioio_env, custom_check_routines )
	if not setup_audioio_environment( audioio_env, conf ) :
		Exit(1)
	audioio_env = conf.Finish()
	return audioio_env

def configureModules( clam_env ) :
	configure_clam(clam_env)
	save_config_file_from_env(clam_env, 'scons/libs')
	core_env = configure_core(clam_env)
	save_config_file_from_env(core_env, 'scons/libs/core')
	processing_env = configure_processing(core_env)
	save_config_file_from_env(processing_env, 'scons/libs/processing')
	audioio_env = configure_audioio(core_env)
	save_config_file_from_env(audioio_env, 'scons/libs/audioio')

# helper functions
def setup_build_options( env ) :
	# configuration options:
	opts = Options('options.cache')
	
	# global options
	opts.Add( PathOption( 'prefix', 'Install location for CLAM', '/usr/local'))
	opts.Add( PathOption( 'prefix_for_packaging', 'Install location when packaging (just for .deb creation)', '.'))
	if isWindowsPlatform :
		opts.Add( PathOption( 'sandbox_path', 'Path to sandbox', 'G:\\projects' ) )
	opts.Add( BoolOption( 'release', 'Build CLAM with optimizations and stripping debug symbols', 'no'))
	opts.Add( BoolOption( 'double', 'CLAM TData type will be double','no'))
	opts.Add( BoolOption( 'sandbox', 'Presence of libraries in the CLAM sandbox will have preference', 'yes'))
	opts.Add( BoolOption( 'checks', 'Postcondition checks enabled', 'yes' ))
	if sys.platform == "linux2" :
		opts.Add( BoolOption( 'crossmingw', '', 'no' ))

	opts.Add( BoolOption( 'verbose', 'Display commands', False) )
	opts.Add( BoolOption( 'release_asserts', 'CLAM asserts will be triggered on release builds', 'no'))
	opts.Add( BoolOption( 'optimize_and_lose_precision', 'Use tabulated trigonometric functions and the like', 'no' )) 
	opts.Add( BoolOption( 'force_avoid_configure', 'Avoid configure phase. Useful for Eclipse scons plugin. Enable it only if you know what you are doing', 'no' ) )
	# clam_core options
	opts.Add( EnumOption( 'xmlbackend', 'XML passivation backend', 'xercesc', ('xercesc','xmlpp','both','none')) )
	if not isWindowsPlatform :
		opts.Add( BoolOption( 'with_jack', 'Enables/Disable JACK support', 'yes') )
	else :
		opts.Add( BoolOption( 'with_jack', 'Enables/Disable JACK support', 'no') )

	ladspa_option = (sys.platform == 'linux2' and not crosscompiling)
	opts.Add( BoolOption( 'with_ladspa', 'Ladspa plugin support', ladspa_option) )
	
	# clam_processing options
	opts.Add( BoolOption( 'with_fftw3', 'Selects whether to use fftw3 or not', 'yes'))
	opts.Add( BoolOption( 'with_fftw', 'Selects whether to use fftw or not', 'no'))
	opts.Add( BoolOption( 'with_nr_fft', 'Selects whether to use Numerical Recipes fft algorithm implementation or not', 'yes') )

	# clam_audioio options
	opts.Add( BoolOption( 'with_sndfile', 'Enables PCM files reading and writing', 'yes' ) )	
	opts.Add( BoolOption( 'with_oggvorbis', 'Enables ogg/vorbis reading and writing support', 'yes' ) )
	opts.Add( BoolOption( 'with_mad', 'Enables mpeg 1 layer 3 files reading and writing support', 'yes' ) )
	opts.Add( BoolOption( 'with_id3', 'Enables support for accesing ID3 tags on mpeg audio streams', 'yes') )
	opts.Add( BoolOption( 'with_portaudio', 'Enables audio device I/O using PortAudio', 'yes') )
	if isLinuxPlatform :
		opts.Add( BoolOption( 'with_alsa', 'Enables PCM and MIDI device I/O through ALSA', 'yes' ) )
	elif isDarwinPlatform :
		opts.Add( EnumOption( 'audio_backend', 'Selects audio PCM i/o library used by CLAM backend', 'rtaudio', ('rtaudio','portaudio') ) )
	elif isWindowsPlatform :
		opts.Add( EnumOption( 'audio_backend', 'Selects audio PCM i/o library used by CLAM backend', 'rtaudio', ('rtaudio','directx','portaudio') ) )
	opts.Add( BoolOption( 'with_portmidi', 'Enables MIDI device I/O through portmidi', 'no' ) )

	opts.Add( 'distcc_hosts', 'Defines compiling hosts, if defined enables the distcc compiler', '')
	
	opts.Update(env)
	opts.Save('options.cache', env) # Save, so user doesn't have to 
	  				# specify prefix and other options every time

	Help("""
configure : Configures clam libraries. This is a mandatory step
""" + opts.GenerateHelpText(env) )

def compose_install_dirnames( env ) :
	install_dirs = InstallDirs()
	install_dirs.compose( env )
	print """\
#############################################
### INSTALL DIRECTORY INFORMATION         ###
#############################################"""
	print "Directory to install under:", install_dirs.prefix
	print "\tLibrary files will be installed at:", install_dirs.lib
	print "\tExecutable files will be installed at:", install_dirs.bin
	print "\tInclude files will be installed at:", install_dirs.inc
	print "\tDocumentation, data and examples will be installed at:", install_dirs.data
	return install_dirs

def gather_custom_checks() :
	custom_check_routines = dict()

	for checks_collection in package_checks, tool_checks, generic_checks :
		for check_name, check_routine in checks_collection.items() :
			custom_check_routines[check_name] = check_routine
	return custom_check_routines

def create_custom_builders( env ) :
	bld = Builder( action=Action(generate_so_name,generate_so_name_message) )
	env.Append( BUILDERS={'SonameLink' : bld} )	
	bld = Builder( action=Action(generate_linker_name, generate_linker_name_message) )
	env.Append( BUILDERS={'LinkerNameLink' : bld} )

def config_file_missing():
	dirs = ". core audioio processing"
	files = [ "scons/libs/"+dir+"/flags.conf" for dir in dirs.split()]
	func_and = lambda x,y : x and y
	result = not reduce( func_and, map(os.path.exists, files) ) 

#	if result == False:
#		print "\n WARNING: at least one module configuration file is missing. Running automatically as a 'scons configure'\n"

	return result



# SConstruct file for CLAM
# Main section


clam_env = Environment( ENV=os.environ, tools=['default'])
clam_env.SConsignFile()

setup_build_options( clam_env )

if not clam_env['verbose']:
	clam_env['CXXCOMSTR'] = '== Compiling $SOURCE'
	clam_env['SHCXXCOMSTR'] = '== Compiling shared $SOURCE'
	clam_env['LINKCOMSTR'] = '== Linking $TARGET'
	clam_env['SHLINKCOMSTR'] = '== Linking library $TARGET'

crosscompiling = clam_env.has_key('crossmingw') and clam_env['crossmingw']
if crosscompiling :
	clam_env.Tool('crossmingw',toolpath=['scons/sconstools'])

if clam_env['distcc_hosts'] :
	clam_env['CXX'] = 'distcc g++'
	clam_env['ENV']['DISTCC_HOSTS'] = clam_env['distcc_hosts']
	SetOption('num_jobs', len( clam_env['distcc_hosts'].split() ))

sys.path.append('scons/sconstools')
import versionInfo
version, fullVersion = versionInfo.versionFromLocalInfo("CLAM", "CHANGES")
Export('version')
print "Version: ", version
print "Package version: ", fullVersion


versionInfo.generateVersionSources(os.path.join('src','Defines','CLAMVersion'), "CLAM", fullVersion)

Export('clam_env')

#registering custom checks
custom_check_routines = gather_custom_checks()

#registering custom_builders
create_custom_builders(clam_env)

clam_env['CXXFILESUFFIX'] = '.cxx'

#needs configure?
avoid_configure_option=ARGUMENTS.get('force_avoid_configure',0)
needs_configure = ('configure' in COMMAND_LINE_TARGETS) or config_file_missing() or len(ARGUMENTS)>0
avoid_configure = '--help' in COMMAND_LINE_TARGETS or avoid_configure_option=='1' or avoid_configure_option=='yes' or '-c' in ARGUMENTS

if needs_configure and not avoid_configure :
	print 'Configure phase...'
	configureModules(clam_env)
	print "Finished. Invoke 'scons' now."
	Exit(0)

builderCopy = Builder( action=Action(generate_copy_files,generate_copy_files_message) )
clam_env.Append( BUILDERS={'CopyFileAndUpdateIncludes' : builderCopy} )	

core_env = clam_env.Copy()
processing_env = clam_env.Copy()
audioio_env = clam_env.Copy()

modules = [ 'core', 'processing', 'audioio']

Export("crosscompiling")

# install dirs composition
install_dirs = compose_install_dirnames(clam_env)
Export('install_dirs')

#building
if not clam_env.GetOption('clean') :
	for module in modules :
		load_config_file_to_env(eval(module+'_env'), 'scons/libs/'+module)
		Export(module+'_env')

for module in modules :
	SConscript('scons/libs/'+module+'/SConscript')

doxygen = clam_env.Command("DoxyLog", "doxygen.cfg",
	'(cat $SOURCE; echo "PROJECT_NUMBER = %s" ) | doxygen - 2>&1 | tee $TARGET'%fullVersion)
clam_env.Alias("doxygen", doxygen)

sconstoolsTargetDir = os.path.join(install_dirs.data, 'clam', 'sconstools')
sconstoolsInstall = clam_env.Install(sconstoolsTargetDir, glob.glob('scons/sconstools/*.py'))
clam_env.Alias('install_sconstools', sconstoolsInstall)
Depends('install_core', sconstoolsInstall)

# Module dependenciesa
all_alias = Alias( 'all', modules)
install_alias = Alias( 'install', ['install_%s'%module for module in modules])

Default( all_alias )

print """\
##############################################
### BUILDING CLAM LIBRARIES                ###
##############################################"""


