#!/usr/bin/python
import sys
import os
import shelve
import copy
# tools for build configuration
from buildtools.custom_builders import * 
from buildtools.tool_checks import *
from buildtools.package_checks import *
from buildtools.generic_checks import *
from buildtools.installdirs import * 
# module check sequences
from modconf import *
from modconf.core import * 
from modconf.processing import *
from modconf.audioio import *
from modconf.vmqt import *
from modconf.vmfl import *



def load_config_file_to_env( env, dir ):
	opts = Options(dir+'/flags.conf')
	opts.Add( 'CPPPATH', 'CPP PATH', env['CPPPATH'])
	opts.Add( 'CCFLAGS', 'CC FLAGS', env['CCFLAGS'])
	opts.Add( 'CPPFLAGS', 'CPP FLAGS', env['CPPFLAGS'])
	opts.Add( 'LIBPATH', 'LIB PATH', env['LIBPATH'])
	opts.Add( 'LIBS', 'libs to link', env['LIBS'])
	opts.Add( 'LINKFLAGS', 'LINKFLAGS', env['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.Add( 'QT_LIB', 'QT lib name', '')
	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.Add( 'QT_LIB', 'QT lib name', '')
	opts.Save(dir+'/flags.conf', env)

def configure_clam(clam_env) :
	print """\
############################################
### CLAM GLOBAL DEPENDENCIES CHECKING    ###
############################################"""
	# Sandbox setup
	clam_env.Replace( QT_LIB = '' )
	if sys.platform == 'win32' : #!= 'linux2' :
		libbasenames = [ 'fltk', 'xercesc', 'fftw', 'dxsdk', 'id3lib', 'libmad', 'libsndfile','oggvorbis','portmidi','portaudio','pthreads']
		for basename in libbasenames :
			if sys.platform == 'win32' : path_sep = '\\'
			if sys.platform == 'darwin' : path_sep = '/'

			include_path = os.path.join(clam_env['sandbox_path'], basename, 'include')
			print( 'include path: ' + include_path )
			lib_path =  os.path.join(clam_env['sandbox_path'], basename, 'lib')
			print( 'lib path: ' + lib_path)
			clam_env.Append( CPPPATH= include_path )
			clam_env.Append( LIBPATH = lib_path )
			# if flags is not defined it will crash while loading flags.conf
		environmentIncludes = os.environ['INCLUDE']
		environmentIncludesList = environmentIncludes.split(';')
		for include in environmentIncludesList :
			print( 'adding include dir from windows config: ' + include )
			clam_env.Append( CPPPATH = include)
	if sys.platform == 'linux2':
		clam_env.Append( CPPPATH= '/usr/local/include' )
	if sys.platform == 'darwin' :
		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()
	core_env.Replace( QT_LIB = '')
	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']
	if clam_env['xmlbackend'] != 'none' :
		clam_env.Append( CPPFLAGS=['-DCLAM_USE_XML'] )
	return core_env

def configure_processing(core_env) :
	print """\
###############################################
### CLAM PROCESSING DEPENDENCIES CHECKING   ###
###############################################"""
	processing_env = core_env.Copy()
	processing_env.Replace( QT_LIB = '')
	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()
	audioio_env.Replace( QT_LIB = '')

	conf = Configure( audioio_env, custom_check_routines )
	if not setup_audioio_environment( audioio_env, conf ) :
		Exit(1)
	if audioio_env['with_mad'] :
		audioio_env.Append( CPPFLAGS=['-DWITH_MAD=1'] )
	if audioio_env['with_oggvorbis'] :
		audioio_env.Append( CPPFLAGS=['-DWITH_VORBIS=1'] )
	audioio_env = conf.Finish()
	return audioio_env

def configure_vmfl(audioio_env) :	
	print """\
#########################################
### CLAM VMFL DEPENDENCIES CHECKING   ###
#########################################"""
	vmfl_env = audioio_env.Copy()
	vmfl_env.Replace( QT_LIB = '')
	if sys.platform != 'darwin':  # FLTK configuration bypass under Mac OSX
	#	if sys.platform == 'win32' or sys.platform == 'darwin' :
	#		vmfl_env.AppendUnique( LIBS=copy.copy(audioio_env['LIBS']) )
		conf = Configure( vmfl_env, custom_check_routines )
		if not setup_vmfl_environment( vmfl_env, conf ) :
			Exit(1)
		vmfl_env = conf.Finish()
	return vmfl_env

def configure_vmqt(audioio_env) :	
	print """\
#########################################
### CLAM VMQT DEPENDENCIES CHECKING   ###
#########################################"""
	vmqt_env = audioio_env.Copy()
	if sys.platform == 'win32' :
		vmqt_env.Replace( QT_LIB = 'qt-mt322')
	else :
		vmqt_env.Replace( QT_LIB = 'qt-mt' )
	vmqt_env.Replace( QT_DEBUG='0' )
	conf = Configure( vmqt_env, custom_check_routines )
	if not setup_vmqt_environment( vmqt_env, conf ) :
		Exit(1)
	vmqt_env = conf.Finish()
	return vmqt_env

def configureModules( clam_env ) :
	configure_clam(clam_env)
	core_env = configure_core(clam_env)
	processing_env = configure_processing(core_env)
	audioio_env = configure_audioio(processing_env)
	vmfl_env = configure_vmfl(audioio_env)
	vmqt_env = configure_vmqt(audioio_env)
	save_config_file_from_env(clam_env, '.')
	save_config_file_from_env(core_env, 'core')
	save_config_file_from_env(processing_env, 'processing')
	save_config_file_from_env(audioio_env, 'audioio')
	save_config_file_from_env(vmfl_env, 'vmfl')
	save_config_file_from_env(vmqt_env, 'vmqt')
	#return core_env, processing_env, audioio_env, vmfl_env, vmqt_env

# helper functions
def setup_build_options( env ) :
	# configuration options:
	opts = Options('clam.conf')
	
	# global options
	opts.Add( PathOption( 'prefix', 'Install location for CLAM', '/usr/local'))
	opts.Add( PathOption( 'install_prefix', 'Install location when packaging (just for .deb creation)', '.'))
	if sys.platform == 'win32' : #!= 'linux2' :
		opts.Add( PathOption( 'sandbox_path', 'Path to sandbox', 'G:\\projects' ) )
	if sys.platform == 'win32' :
		opts.Add( BoolOption( 'release', 'Build CLAM with optimizations and stripping debug symbols', 'no' ))
	else :
		opts.Add( BoolOption( 'release', 'Build CLAM with optimizations and stripping debug symbols', 'yes'))
	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' ))

	opts.Add( BoolOption( 'release_asserts', 'CLAM asserts will be triggered on release builds', 'no'))
	
	# clam_core options
	opts.Add( EnumOption( 'xmlbackend', 'XML passivation backend', 'xercesc', ('xercesc','xmlpp','both','none')) )
	if sys.platform != 'win32' :
		opts.Add( BoolOption( 'with_ladspa_support', 'Ladspa plugin support', 'yes') )
		opts.Add( BoolOption( 'with_osc_support', 'Enables/Disables OSC support', 'no') )
		opts.Add( BoolOption( 'with_jack_support', 'Enables/Disable JACK support', 'yes') )
	
	# clam_processing options
	opts.Add( BoolOption( 'with_fftw3', 'Selects whether to use fftw3 or not', 'no'))
	opts.Add( BoolOption( 'with_fftw', 'Selects whether to use fftw or not', 'yes'))
	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 sys.platform == 'linux2' :
		opts.Add( BoolOption( 'with_alsa', 'Enables PCM and MIDI device I/O through ALSA', 'yes' ) )
	elif sys.platform == 'darwin' :
		opts.Add( EnumOption( 'audio_backend', 'Selects audio PCM i/o library used by CLAM backend', 'rtaudio', ('rtaudio','portaudio') ) )
	elif sys.platform == 'win32' :
		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' ) )

	# clam_vmqt options 
	# TODO check if this hardcoded path can be removed also in Windows and Mac
	'''
	if sys.platform == 'linux2' :
		pass
		if os.environ.has_key("QTDIR") and os.environ["QTDIR"] : 
			print "Using existing QTDIR"
		else :
			qt_includes = '/usr/include/qt3'
			qt_libs = '/usr/lib'
			print "QTDIR not found, so assuming these are the paths for qt: qt_includes=%s qt_libs=%s" \
				% (qt_includes, qt_libs)
			opts.Add( PathOption( 'qt_includes', 'Path to the directory where qt includes are located', qt_includes ) )
			opts.Add( PathOption( 'qt_libs', 'Path to the directory where qt binaries are located', qt_libs ) )
	'''	
	if sys.platform == 'win32' : 
		opts.Add( PathOption( 'qt_includes', 'Path to the directory where qt includes are located', 'G:\projects\qt\include' ) )
		opts.Add( PathOption( 'qt_libs', 'Path to the directory where qt binaries are located', 'G:\projects\qt\lib' ) )

	opts.Update(env)
	opts.Save('clam.conf', env) # Save, so user doesn't have to 
				  				# specify PREFIX 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 )
	Export('install_dirs')
	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

def gather_custom_checks() :
	custom_check_routines = dict()

	for check_name, check_routine in package_checks.items() :
		custom_check_routines[check_name] = check_routine

	for check_name, check_routine in tool_checks.items() :
		custom_check_routines[check_name] = check_routine

	for check_name, check_routine in generic_checks.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 vmqt vmfl"
	files = [ 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

top = '../..'
Export('top')

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

setup_build_options( clam_env )

clam_env.Append( CPPFLAGS = '' )

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


versionInfo.generateVersionSources(os.path.join(top,'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)

if sys.platform == 'win32' :
	clam_env.Replace( QT_LIB = 'qt-mt322')
else :
	clam_env.Replace( QT_LIB = 'qt-mt' )
clam_env['CXXFILESUFFIX'] = '.cxx'

if sys.platform=='win32' :
	clam_env.Append(CPPFLAGS=['-D_USE_MATH_DEFINES']) # to have M_PI defined

if 'configure' in COMMAND_LINE_TARGETS or config_file_missing() :
    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()
vmfl_env = clam_env.Copy()
vmqt_env = clam_env.Copy()

#building
if not clam_env.GetOption('clean') :
	load_config_file_to_env(core_env, 'core')
	load_config_file_to_env(processing_env, 'processing')
	load_config_file_to_env(audioio_env, 'audioio')
	load_config_file_to_env(vmfl_env, 'vmfl')
	load_config_file_to_env(vmqt_env, 'vmqt')


Export('core_env')
Export('processing_env')
Export('audioio_env')
Export('vmfl_env')
Export('vmqt_env')

# install dirs composition

compose_install_dirnames(clam_env)


if clam_env.GetOption('clean') :
	if os.path.exists( 'header.db' ) :
		os.remove('header.db')
for module in ['core', 'processing', 'audioio', 'vmqt', 'vmfl'] :
	SConscript(module+'/SConscript')

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

# Module dependenciesa
modules = [ 'core', 'processing', 'audioio', 'vmqt']

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

Default( all_alias )
print """\
##############################################
### BUILDING CLAM LIBRARIES                ###
##############################################"""
