import os.path

Import('env', 'subenvs')

if [s for s in COMMAND_LINE_TARGETS if s == 'test' or s.startswith('test/')]:
    if not GetOption('enable_tests'):
        env.Die("can't use 'test*' target(s) without `--enable-tests' option")

if [s for s in COMMAND_LINE_TARGETS if s == 'bench' or s.startswith('bench/')]:
    if not GetOption('enable_benchmarks'):
        env.Die("can't use 'bench*' target(s) without `--enable-benchmarks' option")

for senv in subenvs.all:
    senv.Append(CPPPATH=['#src/internal_modules'])

    for target_dir in senv.GlobRecursive('internal_modules', 'target_*'):
        if target_dir.name in senv['ROC_TARGETS']:
            senv.Append(CPPPATH=['#src/{}'.format(target_dir)])

    for t in env['ROC_TARGETS']:
        senv.Append(CPPDEFINES=['ROC_{}'.format(t.upper())])

all_modules_libs = []
all_modules_objects = []

for module_name in env['ROC_MODULES']:
    module_dir = 'internal_modules/' + module_name

    module_env = subenvs.internal_modules.DeepClone()
    module_env.Append(CPPDEFINES=('ROC_MODULE', module_name))

    module_gen_env = subenvs.generated_code.DeepClone()
    module_gen_env.Append(CPPDEFINES=('ROC_MODULE', module_name))

    src_dirs = [module_dir]
    for target_dir in env.GlobDirs(module_dir + '/target_*'):
        if target_dir.name in env['ROC_TARGETS']:
            src_dirs.append('{}/{}/{}'.format(module_dir, target_dir.name, module_name))

    objects = []
    for src_dir in src_dirs:
        for cpp in env.GlobFiles(src_dir + '/*.cpp'):
            objects += module_env.Object(cpp)
        for rs in env.GlobFiles(src_dir + '/*.rl'):
            objects += module_gen_env.Ragel(rs)

    if not objects:
        continue

    lib = module_env.StaticLibrary('internal_modules/' + module_name, objects)
    env.Alias(module_name, [lib], env.Action(''))
    env.AlwaysBuild(module_name)

    all_modules_libs = [lib] + all_modules_libs
    all_modules_objects = objects + all_modules_objects

env.Alias('internal_modules', all_modules_libs, env.Action(''))
env.AlwaysBuild('internal_modules')

if not GetOption('disable_shared') or GetOption('enable_static') or GetOption('enable_examples'):
    libs_env = subenvs.public_libs.DeepClone()
    libs_env.Append(CPPDEFINES=('ROC_MODULE', 'roc_api'))
    libs_env.Append(CPPPATH=['public_api/include'])

    public_api_objects = [libs_env.Object(s) for s in env.GlobFiles('public_api/src/*.cpp')]
    public_api_targets = []

    if not GetOption('disable_shared'):
        should_strip = not GetOption('enable_debug') and libs_env.SupportsStripSharedLibrary()

        libroc_shared = libs_env.SharedLibrary(
            'roc_unstripped' if should_strip else 'roc',
            public_api_objects,
            LIBS=all_modules_libs + libs_env['LIBS'],
            SHLIBSUFFIX=libs_env['SHLIBSUFFIX'])

        env.Depends(libroc_shared, all_modules_libs)
        env.Depends(libroc_shared, '#src/public_api/roc.version')

        if should_strip:
            libroc_shared = libs_env.StripSharedLibrary(
                libroc_shared[0].name.replace('_unstripped', ''),
                libroc_shared)

        install_target = env.Install(env['ROC_BINDIR'], libroc_shared)
        symlinks = env.SymlinkLibrary(install_target[0])

        public_api_targets += [install_target]
        public_api_targets += symlinks

        env.AddDistFile(env['ROC_SYSTEM_LIBDIR'], install_target)

        if env.NeedsFixupSharedLibrary():
            env.AddDistAction(env.FixupSharedLibrary(
                env.GetDistPath(env['ROC_SYSTEM_LIBDIR'], install_target[0].name)))

        for lnk in symlinks:
            env.AddDistFile(env['ROC_SYSTEM_LIBDIR'], lnk)

    if GetOption('enable_static'):
        thirdparty_libs = libs_env.GetThirdPartyStaticLibs()

        if libs_env.SupportsRelocatableObject() and libs_env.SupportsLocalizedObject():
            libroc_modules = libs_env.RelocatableObject(
                'libroc_modules_unlocalized',
                public_api_objects + all_modules_objects)

            env.Depends(libroc_modules, all_modules_libs)

            libroc_modules = libs_env.LocalizedObject('libroc_modules', libroc_modules)
            libroc_static = libs_env.StaticLibrary(
                'roc_modules' if thirdparty_libs else 'roc',
                libroc_modules)
        else:
            libroc_static = libs_env.StaticLibrary(
                'roc_modules' if thirdparty_libs else 'roc',
                public_api_objects + all_modules_objects)

        if thirdparty_libs:
            libroc_static = libs_env.ComposeStaticLibraries(
                'roc', libroc_static + thirdparty_libs)

        install_target = env.Install(env['ROC_BINDIR'], libroc_static)
        public_api_targets += [install_target]

        env.AddDistFile(env['ROC_SYSTEM_LIBDIR'], install_target)

    if not GetOption('disable_shared') or GetOption('enable_static'):
        env.Alias('public_api', public_api_targets, env.Action(''))
        env.AlwaysBuild('public_api')

        env.AddDistFile(env['ROC_SYSTEM_INCDIR'], '#src/public_api/include/roc')

        if 'PKG_CONFIG_PATH' in env.Dictionary():
            pc_file = env.GeneratePkgConfig(
                build_dir='.',
                filename='roc.pc',
                prefix=GetOption('prefix'),
                libdir=env['ROC_SYSTEM_LIBDIR'],
                name='roc',
                desc='Real-time audio streaming over the network.',
                url='https://roc-streaming.org',
                version=env['ROC_VERSION'])
            env.AddDistFile(env['ROC_SYSTEM_PCDIR'], pc_file)

if GetOption('enable_examples'):
    examples_env = subenvs.examples.DeepClone()
    examples_env.Append(CPPPATH=['public_api/include'])
    examples_env.Prepend(LIBS=all_modules_libs)

    example_targets = []

    for example_source in env.GlobFiles('public_api/examples/*.c'):
        example_name = os.path.splitext(example_source.name)[0]

        if 'pulseaudio' in example_name and not 'target_pulseaudio' in env['ROC_TARGETS']:
            continue

        exe_name = 'roc-example-{}'.format(example_name.replace('_', '-'))
        exe = examples_env.Program(
            exe_name, [example_source] + public_api_objects)

        example_targets += [env.Install(env['ROC_BINDIR'], exe)]

    env.Alias('examples', example_targets, env.Action(''))
    env.AlwaysBuild('examples')

if not GetOption('disable_tools') \
  or GetOption('enable_tests') or GetOption('enable_benchmarks'):
    sanitizer_objects = []
    if GetOption('sanitizers'):
        sanitizer_objects += subenvs.internal_modules.Object('sanitizer_options.cpp')

if not GetOption('disable_tools'):
    if env['ROC_COMMIT']:
        verion_str = '{} ({})'.format(env['ROC_VERSION'], env['ROC_COMMIT'])
    else:
        verion_str = env['ROC_VERSION']

    for tool_dir in env.GlobDirs('tools/*'):
        tools_env = subenvs.tools.DeepClone()
        tools_env.Prepend(LIBS=all_modules_libs)
        tools_env.Append(CPPDEFINES=('ROC_MODULE', tool_dir.name))
        tools_env.Append(CPPPATH=['tools', '#src/tools/{}'.format(tool_dir.name)])

        sources = env.GlobFiles('{}/*.cpp'.format(tool_dir))

        objects = []
        for ggo in env.GlobFiles('{}/*.ggo'.format(tool_dir)):
            objects += subenvs.generated_code.GenGetOpt(ggo, verion_str)

        exe_name = tool_dir.name.replace('_', '-')

        target = env.Install(env['ROC_BINDIR'],
                    tools_env.Program(exe_name, sources+objects+sanitizer_objects))

        env.Alias(exe_name, [target], env.Action(''))
        env.AlwaysBuild(exe_name)

        env.AddDistFile(env['ROC_SYSTEM_BINDIR'], target)

if GetOption('enable_tests') or GetOption('enable_benchmarks'):
    common_test_env = subenvs.tests.DeepClone()
    common_test_env.Append(CPPDEFINES=('ROC_MODULE', 'roc_test'))
    common_test_env.Prepend(LIBS=[all_modules_libs])

    if GetOption('enable_tests'):
        test_main_object = common_test_env.Object('tests/test_main.cpp')

    if GetOption('enable_benchmarks'):
        bench_main_object = common_test_env.Object('tests/bench_main.cpp')

    for test_name in env['ROC_MODULES'] + ['public_api']:
        test_dir = 'tests/' + test_name

        test_env = common_test_env.DeepClone()
        test_env.Append(CPPPATH=['#src/' + test_dir])

        if test_name == 'public_api':
            if GetOption('disable_shared') and not GetOption('enable_static'):
                continue
            test_env.Append(CPPPATH=['public_api/include'])

        for kind in ['test', 'bench']:
            if kind == 'test':
                if GetOption('enable_tests'):
                    main_object = test_main_object
                else:
                    continue
            else:
                if GetOption('enable_benchmarks'):
                    main_object = bench_main_object
                else:
                    continue

            sources = []

            sources += env.GlobFiles('{}/{}_*.cpp'.format(test_dir, kind))
            sources += env.GlobFiles('{}/test_helpers/*.cpp'.format(test_dir))

            for target_dir in env.GlobRecursive(test_dir, 'target_*'):
                if target_dir.name in env['ROC_TARGETS']:
                    test_env.Append(CPPPATH=['#src/{}'.format(target_dir)])
                    sources += env.GlobRecursive(target_dir, kind + '_*.cpp')

            if not sources:
                continue

            sources_and_objects = sources + main_object + sanitizer_objects
            if test_name == 'public_api':
                sources_and_objects += public_api_objects

            exe_name = 'roc-{}-{}'.format(kind, test_name.replace('roc_', '').replace('_', '-'))

            target = env.Install(env['ROC_BINDIR'],
                test_env.Program(exe_name, sources_and_objects))

            exe_file = '{}/{}'.format(env['ROC_BINDIR'], exe_name)
            if kind == 'test':
                env.AddTest(test_name, exe_file)
            else:
                env.AddBench(test_name, exe_file)
