Import([
	'env',
	'sourceDir',
])

import os
import ctypes
from glob import glob

env=env.Clone()
env.Append(CPPDEFINES='/DLIBRARY')
#Whole-program optimization causes eSpeak to distort and worble with its Klatt4 voice
#Therefore specifically force it off
env.Append(CCFLAGS='/GL-')

def _compilePhonemeAndDictData_buildEmitter(target,source,env):
	dictSources=env.Flatten([env.Glob(source[0].abspath+'\\*_%s'%ext) for ext in ('rules','list','listx','extra')])
	phSourceIgnores=['error_log','error_intonation','compile_prog_log','compile_report','envelopes.png']
	phSources=env.Flatten([[Dir(topDir).File(f) for f in files if f not in phSourceIgnores] for topDir,subdirs,files in os.walk(source[1].abspath)])
	sources=env.Flatten([phSources])
	targets=[target[0].File(f) for f in ['intonations','phondata','phondata-manifest','phonindex','phontab']]
	dictSideEffects=[target[0].File('%s_dict'%dictName) for dictName in set(f.name.split('_')[0] for f in dictSources)]
	dictSideEffects.extend([source[0].File(x) for x in ('dict_log','dict_phonemes')])
	env.SideEffect(dictSideEffects,targets)
	phSideEffects=[source[1].File(x) for x in phSourceIgnores]
	env.SideEffect(phSideEffects,targets)
	return targets,sources

env['BUILDERS']['compileEspeakPhonemeAndDictData']=Builder(
	action=[
		[File("#miscDeps/tools/espeakedit.exe"),"--compile"]
	],
	emitter=_compilePhonemeAndDictData_buildEmitter,
)

class espeak_VOICE(ctypes.Structure):
	_fields_=[
		('name',ctypes.c_char_p),
		('languages',ctypes.c_char_p),
		('identifier',ctypes.c_char_p),
		('gender',ctypes.c_byte),
		('age',ctypes.c_byte),
		('variant',ctypes.c_byte),
		('xx1',ctypes.c_byte),
		('score',ctypes.c_int),
		('spare',ctypes.c_void_p),
	]

espeakRepo=Dir("#include/espeak")

env=env.Clone()
env.Append(CCFLAGS='/W0')
env.Append(CPPDEFINES=[('BUILDING_DLL',1)])
env.Prepend(CPPPATH=espeakRepo.Dir('platforms/windows/windows_dll/src'))

def espeak_compileDict_action(target,source,env):
	# We want the eSpeak dll to be freed after each dictionary.
	# This is because it writes to stderr but doesn't flush it.
	# Unfortunately, there's no way we can flush it or use a different stream
	# because our eSpeak statically links the CRT.
	espeak=ctypes.cdll.LoadLibrary(espeakLib[0].abspath)
	espeak.espeak_Initialize(0,0,synthDriversDir.abspath,0x8000)
	try:
		lang=source[0].name.split('_')[0]
		v=espeak_VOICE(languages=lang+'\x00')
		if espeak.espeak_SetVoiceByProperties(ctypes.byref(v))!=0:
			print "espeak_compileDict_action: failed to switch to language %s, skipping compilation"%lang
			return
		dictPath=os.path.split(source[0].abspath)[0]+'/'
		if espeak.espeak_CompileDictionary(dictPath,None,0)!=0:
			return 1
	finally:
		espeak.espeak_Terminate()
		# ctypes doesn't ever unload dlls.
		ctypes.windll.kernel32.FreeLibrary(espeak._handle)

synthDriversDir=sourceDir.Dir('synthDrivers')
espeakDataDir=synthDriversDir.Dir('espeak-data')
tempSrcDir=Dir('src')

#make a temp copy of src, but using the windows dll version of speech.h
winFiles=['speech.h','stdafx.h']
for f in env.Glob(espeakRepo.abspath+'/src/*'):
	if f.name.lower() in winFiles: continue
	env.Command(tempSrcDir.File(f.name),f,Copy('$TARGET','$SOURCE'))
for f in winFiles:
	env.Command(tempSrcDir.File(f),espeakRepo.File('platforms/windows/windows_dll/src/%s'%f),Copy('$TARGET','$SOURCE'))

espeakLib=env.SharedLibrary(
	target='espeak',
	srcdir=tempSrcDir.name,
	source=[
		"speak_lib.cpp",
		"readclause.cpp",
		"compiledict.cpp",
		"dictionary.cpp",
		"intonation.cpp",
		"setlengths.cpp",
		"numbers.cpp",
		"synth_mbrola.cpp",
		"synthdata.cpp",
		"synthesize.cpp",
		"translate.cpp",
		"tr_languages.cpp",
		"voices.cpp",
		"wavegen.cpp",
		"espeak_command.cpp",
		"event.cpp",
		"fifo.cpp",
		"wave.cpp",	"debug.cpp",
		"phonemelist.cpp",
		"klatt.cpp",
		"sonic.cpp",
	],
	LIBS=['advapi32'],
)

#Compile phoneme data
tempPhonemeAndDictDataFiles=env.compileEspeakPhonemeAndDictData(espeakRepo.Dir('espeak-data'),[espeakRepo.Dir('dictsource'),espeakRepo.Dir('phsource')],ENV={'ESPEAK_DATA_PATH':espeakRepo.abspath})
phonemeAndDictDataFiles=[env.Command(espeakDataDir.File(f.name),f,Copy('$TARGET','$SOURCE')) for f in tempPhonemeAndDictDataFiles]
#Ensure these don't try to get built in paralell
for f in phonemeAndDictDataFiles:
	env.SideEffect('_espeak_compileDict',f)

# copy voices from espeak repository to synthDrivers 
env.recursiveCopy(espeakDataDir.Dir('voices'),espeakRepo.Dir('espeak-data\\voices'))

#Copy in the extra variants
for f in env.Glob(Dir('variants').abspath+'/*'):
	env.Command(espeakDataDir.File('voices\\!v\\%s'%f.name),f,Copy("$TARGET","$SOURCE"))

#Compile all dictionaries
for f in env.Glob(espeakRepo.abspath+'/dictsource/*_rules'):
	extraSources=[f.File(f.name.split('_')[0]+x) for x in '_list','_listx','_extra']
	dictFile=env.Command(espeakDataDir.File(f.name.split('_')[0]+'_dict'),[f,[x for x in extraSources if os.path.isfile(x.abspath)]],espeak_compileDict_action)
	env.Depends(dictFile,[espeakLib,espeakDataDir.Dir('voices'),phonemeAndDictDataFiles])
	#Dictionaries can not be compiled in parallel, force SCons not to do this
	env.SideEffect('_espeak_compileDict',dictFile)

env.Requires(synthDriversDir,espeakDataDir)
env.Install(synthDriversDir,espeakLib)
env.Clean(espeakDataDir,espeakDataDir)
