import os.path

Import('env', 'subenvs', 'meta')

# default versions for downloaded third-party libraries
thirdparty_versions = {
    # libs
    'alsa':             '1.0.29',
    'json-c':           '0.12-20140410',
    'libatomic_ops':    '7.6.10',
    'libunwind':        '1.2.1',
    'libuv':            '1.35.0',
    'ltdl':             '2.4.6',
    'openfec':          '1.4.2.4',
    'pulseaudio':       '12.2',
    'sndfile':          '1.0.28',
    'sox':              '14.4.2',
    'speexdsp':         '1.2.0',

    # CLI tools
    'gengetopt':        '2.22.6',
    'ragel':            '6.10',

    # tests
    'cpputest':         '4.0',
    'google-benchmark': '1.6.1',
}

# root directory for building third-parties
thirdparty_build_dir = env['ROC_THIRDPARTY_BUILDDIR']

# check if local platform differs from target platform
is_crosscompiling = (meta.build != meta.host)

# source code of these dependencies is included directly in our repo
vendored_dependencies = ['hedley']

# all dependencies that should be either installed system-wide or downloaded by us
external_dependencies = set([t.replace('target_', '') for t in env['ROC_TARGETS']])

# on macos libunwind is provided by the OS
if meta.platform in ['darwin']:
    external_dependencies.discard('libunwind')

external_dependencies.add('ragel')

if not GetOption('disable_tools'):
    external_dependencies.add('gengetopt')

if 'pulseaudio' in external_dependencies and meta.platform in ['linux']:
    external_dependencies.add('alsa')

if GetOption('enable_tests'):
    external_dependencies.add('cpputest')

if GetOption('enable_benchmarks'):
    external_dependencies.add('google-benchmark')

# dependencies that we should download and build manually
autobuild_dependencies = set()

# dependencies that have explicitly provided version
explicit_version = set()

for name, version in env.ParseThirdPartyList(GetOption('build_3rdparty')):
    if name != 'all' and not name in thirdparty_versions:
        env.Die("unknown thirdparty name '%s' in '--build-3rdparty', expected any of: %s",
                    name, ', '.join(['all'] + list(sorted(thirdparty_versions.keys()))))
    autobuild_dependencies.add(name)
    if version:
        thirdparty_versions[name] = version
        explicit_version.add(name)

if 'all' in autobuild_dependencies:
    autobuild_dependencies = external_dependencies

# dependencies that should be pre-installed on system
system_dependencies = external_dependencies - autobuild_dependencies

# dep: hedley
if 'hedley' in vendored_dependencies:
    env.Append(CPPPATH=['#3rdparty/hedley'])

# dep: libuv
if 'libuv' in autobuild_dependencies:
    env.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                   meta.thirdparty_variant, thirdparty_versions, 'libuv')

elif 'libuv' in system_dependencies:
    conf = Configure(env, custom_tests=env.CustomTests)

    if not conf.AddPkgConfigDependency('libuv', '--cflags --libs'):
        conf.env.AddPkgConfigLibs(['uv'])

    if not is_crosscompiling:
        if not conf.CheckLibWithHeaderExt(
            'uv', 'uv.h', 'C', expr='UV_VERSION_MAJOR >= 1 && UV_VERSION_MINOR >= 5'):
            env.Die("libuv >= 1.5 not found (see 'config.log' for details)")
    else:
        if not conf.CheckLibWithHeaderExt('uv', 'uv.h', 'C', run=False):
            env.Die("libuv not found (see 'config.log' for details)")

    env = conf.Finish()

# dep: libunwind
if 'libunwind' in autobuild_dependencies:
    env.BuildThirdParty(thirdparty_build_dir,
                   meta.toolchain, meta.thirdparty_variant,
                   thirdparty_versions, 'libunwind')

elif 'libunwind' in system_dependencies:
    conf = Configure(env, custom_tests=env.CustomTests)

    if not conf.AddPkgConfigDependency('libunwind', '--cflags --libs'):
        conf.env.AddPkgConfigLibs(['unwind'])

    if not conf.CheckLibWithHeaderExt('unwind', 'libunwind.h', 'C', run=not is_crosscompiling):
        env.Die("libunwind not found (see 'config.log' for details)")

    env = conf.Finish()

# dep: libatomic_ops
if 'libatomic_ops' in autobuild_dependencies:
    env.BuildThirdParty(thirdparty_build_dir,
                   meta.toolchain, meta.thirdparty_variant,
                   thirdparty_versions, 'libatomic_ops')

elif 'libatomic_ops' in system_dependencies:
    conf = Configure(env, custom_tests=env.CustomTests)

    if not conf.AddPkgConfigDependency('atomic_ops', '--cflags --libs'):
        conf.env.AddPkgConfigLibs(['atomic_ops'])

    if not conf.CheckLibWithHeaderExt('atomic_ops', 'atomic_ops.h', 'C',
                                      run=not is_crosscompiling):
        env.Die("libatomic_ops not found (see 'config.log' for details)")

    env = conf.Finish()

# dep: openfec
if 'openfec' in autobuild_dependencies:
    env.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                   meta.thirdparty_variant, thirdparty_versions,
                   'openfec', includes=[
                        'lib_common',
                        'lib_stable',
                        ])

elif 'openfec' in system_dependencies:
    conf = Configure(env, custom_tests=env.CustomTests)

    if not conf.AddPkgConfigDependency('openfec', '--cflags --libs'):
        conf.env.AddPkgConfigLibs(['openfec'])

        if GetOption('with_openfec_includes'):
            openfec_includes = GetOption('with_openfec_includes')
            conf.env.Append(CPPPATH=[
                openfec_includes,
                '%s/lib_common' % openfec_includes,
                '%s/lib_stable' % openfec_includes,
            ])
        elif not is_crosscompiling:
           for prefix in ['/usr/local', '/usr']:
               if os.path.exists('%s/include/openfec' % prefix):
                   conf.env.Append(CPPPATH=[
                       '%s/include/openfec' % prefix,
                       '%s/include/openfec/lib_common' % prefix,
                       '%s/include/openfec/lib_stable' % prefix,
                   ])
                   conf.env.Append(LIBPATH=[
                       '%s/lib' % prefix,
                   ])
                   break

    if not conf.CheckLibWithHeaderExt(
            'openfec', 'of_openfec_api.h', 'C', run=not is_crosscompiling):
        env.Die("openfec not found (see 'config.log' for details)")

    if not conf.CheckDeclaration('OF_USE_ENCODER', '#include <of_openfec_api.h>', 'c'):
        env.Die("openfec has no encoder support (OF_USE_ENCODER)")

    if not conf.CheckDeclaration('OF_USE_DECODER', '#include <of_openfec_api.h>', 'c'):
        env.Die("openfec has no decoder support (OF_USE_DECODER)")

    if not conf.CheckDeclaration('OF_USE_LDPC_STAIRCASE_CODEC',
                                 '#include <of_openfec_api.h>', 'c'):
        env.Die(
            "openfec has no LDPC-Staircase codec support (OF_USE_LDPC_STAIRCASE_CODEC)")

    env = conf.Finish()

# dep: speexdsp
if 'speexdsp' in autobuild_dependencies:
    env.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                meta.thirdparty_variant, thirdparty_versions, 'speexdsp')

elif 'speexdsp' in system_dependencies:
    conf = Configure(env, custom_tests=env.CustomTests)

    if not conf.AddPkgConfigDependency('speexdsp', '--cflags --libs'):
        conf.env.AddPkgConfigLibs(['speexdsp'])

    if not conf.CheckLibWithHeaderExt('speexdsp', 'speex/speex_resampler.h', 'C',
                                          run=not is_crosscompiling):
        env.Die("speexdsp not found (see 'config.log' for details)")

    env = conf.Finish()

# dep: alsa
if 'alsa' in autobuild_dependencies:
    env.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                             meta.thirdparty_variant, thirdparty_versions, 'alsa')

# dep: pulseaudio
if 'pulseaudio' in autobuild_dependencies:
    if not 'pulseaudio' in explicit_version and not is_crosscompiling:
        pa_ver = env.ParseToolVersion('pulseaudio --version')
        if pa_ver:
            thirdparty_versions['pulseaudio'] = pa_ver

    pa_deps = [
        'ltdl',
        'json-c',
        'sndfile',
        ]

    if 'alsa' in autobuild_dependencies:
        pa_deps += ['alsa']

    env.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                        meta.thirdparty_variant, thirdparty_versions, 'ltdl')
    env.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                        meta.thirdparty_variant, thirdparty_versions, 'json-c')
    env.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                        meta.thirdparty_variant, thirdparty_versions, 'sndfile')
    env.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                        meta.thirdparty_variant, thirdparty_versions,
                        'pulseaudio', deps=pa_deps, libs=['pulse', 'pulse-simple'])

elif 'pulseaudio' in system_dependencies:
    conf = Configure(env, custom_tests=env.CustomTests)

    if not conf.AddPkgConfigDependency('libpulse', '--cflags --libs'):
        conf.env.AddPkgConfigLibs(['pulse'])

    if not conf.CheckLibWithHeaderExt(
            'pulse', 'pulse/pulseaudio.h', 'C', run=not is_crosscompiling):
        env.Die("libpulse not found (see 'config.log' for details)")

    env = conf.Finish()

    if GetOption('enable_examples'):
        conf = Configure(subenvs.examples, custom_tests=env.CustomTests)

        if not conf.AddPkgConfigDependency('libpulse-simple', '--cflags --libs'):
            conf.env.AddPkgConfigLibs(['pulse-simple'])

        if not conf.CheckLibWithHeaderExt(
                'pulse-simple', 'pulse/simple.h', 'C', run=not is_crosscompiling):
            env.Die("libpulse-simple not found (see 'config.log' for details)")

        subenvs.examples = conf.Finish()

# dep: sox
if 'sox' in autobuild_dependencies:
    sox_deps = []

    if 'alsa' in autobuild_dependencies:
        sox_deps += ['alsa']

    if 'pulseaudio' in autobuild_dependencies:
        sox_deps += ['pulseaudio']

    env.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                        meta.thirdparty_variant, thirdparty_versions, 'sox', sox_deps)

    conf = Configure(env, custom_tests=env.CustomTests)

    if not 'alsa' in autobuild_dependencies:
        for lib in [
                'asound',
                ]:
            conf.CheckLib(lib)

    if meta.platform in ['darwin']:
        env.Append(LINKFLAGS=[
            '-Wl,-framework,CoreAudio'
        ])

    env = conf.Finish()

elif 'sox' in system_dependencies:
    conf = Configure(env, custom_tests=env.CustomTests)

    if not conf.AddPkgConfigDependency('sox', '--cflags --libs'):
        conf.env.AddPkgConfigLibs(['sox'])

    if not is_crosscompiling:
        if not conf.CheckLibWithHeaderExt(
                'sox', 'sox.h', 'C',
                expr='SOX_LIB_VERSION_CODE >= SOX_LIB_VERSION(14, 4, 0)'):
            env.Die("libsox >= 14.4.0 not found (see 'config.log' for details)")
    else:
        if not conf.CheckLibWithHeaderExt('sox', 'sox.h', 'C', run=False):
            env.Die("libsox not found (see 'config.log' for details)")

    env = conf.Finish()

# dep: ragel
if 'ragel' in autobuild_dependencies:
    env.BuildThirdParty(thirdparty_build_dir, "",
                   meta.thirdparty_variant, thirdparty_versions, 'ragel')

    env['RAGEL'] = env.GetThirdPartyExecutable(
        thirdparty_build_dir, thirdparty_versions, 'ragel', exe_name='ragel')

elif 'ragel' in system_dependencies:
    conf = Configure(env, custom_tests=env.CustomTests)

    if 'RAGEL' in env.Dictionary():
        ragel = env['RAGEL']
    else:
        ragel = 'ragel'

    if not conf.CheckProg(ragel):
        env.Die("ragel not found in PATH (looked for '%s')" % ragel)

    env = conf.Finish()

# dep: gengetopt
if 'gengetopt' in autobuild_dependencies:
    env.BuildThirdParty(thirdparty_build_dir, "",
                   meta.thirdparty_variant, thirdparty_versions, 'gengetopt')

    env['GENGETOPT'] = env.GetThirdPartyExecutable(
        thirdparty_build_dir, thirdparty_versions, 'gengetopt', exe_name='gengetopt')

elif 'gengetopt' in system_dependencies:
    conf = Configure(env, custom_tests=env.CustomTests)

    if 'GENGETOPT' in env.Dictionary():
        gengetopt = env['GENGETOPT']
    else:
        gengetopt = 'gengetopt'

    if not conf.CheckProg(gengetopt):
        env.Die("gengetopt not found in PATH (looked for '%s')" % gengetopt)

    env = conf.Finish()

# dep: cpputest
if 'cpputest' in autobuild_dependencies:
    subenvs.tests.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                        meta.thirdparty_variant, thirdparty_versions, 'cpputest')

elif 'cpputest' in system_dependencies:
    conf = Configure(subenvs.tests, custom_tests=env.CustomTests)

    if not conf.AddPkgConfigDependency('cpputest', '--cflags --libs'):
        conf.env.AddPkgConfigLibs(['CppUTest'])

    if not conf.CheckLibWithHeaderExt(
            'CppUTest', 'CppUTest/TestHarness.h', 'CXX', run=not is_crosscompiling):
        subenvs.tests.Die("CppUTest not found (see 'config.log' for details)")

    subenvs.tests = conf.Finish()

# dep: google-benchmark
if 'google-benchmark' in autobuild_dependencies:
    subenvs.tests.BuildThirdParty(thirdparty_build_dir, meta.toolchain,
                        meta.thirdparty_variant, thirdparty_versions, 'google-benchmark')

    if tuple(map(int, thirdparty_versions['google-benchmark'].split('.'))) >= (1, 6, 0):
        subenvs.tests.AppendUnique(CPPDEFINES=['ROC_BENCHMARK_USE_ACCESSORS'])

elif 'google-benchmark' in system_dependencies:
    conf = Configure(subenvs.tests, custom_tests=env.CustomTests)

    if not conf.AddPkgConfigDependency('benchmark', '--cflags --libs'):
        conf.env.AddPkgConfigLibs(['benchmark'])

    if not conf.CheckLibWithHeaderExt(
            'benchmark', 'benchmark/benchmark.h', 'CXX', run=not is_crosscompiling):
        subenvs.tests.Die("Google Benchmark not found (see 'config.log' for details)")

    if conf.CheckLibWithHeaderExt(
        'benchmark', 'benchmark/benchmark.h', 'CXX',
        expr='(bool)(int (benchmark::State::*)()const)&benchmark::State::thread_index',
        run=False):
        subenvs.tests.AppendUnique(CPPDEFINES=['ROC_BENCHMARK_USE_ACCESSORS'])

    subenvs.tests = conf.Finish()

# end of deps

return_value = (env, subenvs)
Return('return_value')
