from os.path import join as pjoin
from collections import namedtuple

from buildutils import logger, multi_glob, quoted, compiler_flag_list

Import('env', 'build', 'install', 'buildSample')

Sample = namedtuple(
    'Sample',
    ('name', 'srcdir', 'dest_dir', 'file_patterns', 'openmp', 'install'),
    defaults=(None, ['cpp'], False, True)
)

samples = [
    Sample('combustor', 'combustor'),
    Sample('custom', 'custom'),
    Sample('demo', 'demo'),
    Sample('flamespeed', 'flamespeed'),
    Sample('kinetics1', 'kinetics1'),
    Sample('derivative_speed', 'jacobian'),
    Sample('gas_transport', 'gas_transport'),
    Sample('rankine', 'rankine'),
    Sample('LiC6_electrode', 'LiC6_electrode'),
    Sample('openmp_ignition', 'openmp_ignition', openmp=True),
    Sample('blasius', 'bvp'),
    Sample('demo1a', '#doc/sphinx/userguide',
           dest_dir='#build/samples/userguide',
           file_patterns=['^demo1a.cpp'], install=False),
    Sample('thermodemo', '#doc/sphinx/userguide',
           dest_dir='#build/samples/userguide',
           file_patterns=['^thermodemo.cpp'], install=False),
    Sample('demoequil', '#doc/sphinx/userguide',
           dest_dir='#build/samples/userguide',
           file_patterns=['^demoequil.cpp'], install=False),
    Sample('kinetics_transport', '#doc/sphinx/userguide',
           dest_dir='#build/samples/userguide',
           file_patterns=['^kinetics_transport.cpp'], install=False),
]

for sample in samples:
    localenv = env.Clone()
    cmake_extra = []
    if sample.openmp:
        localenv.Append(CXXFLAGS=env['openmp_flag'], LINKFLAGS=env['openmp_flag'])
        if env['using_apple_clang']:
            localenv.Append(LIBS=['omp'])

        cmake_extra.extend(
            [
                "find_package(OpenMP REQUIRED)",
                'set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")',
                'set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")',
            ]
        )

    # TODO: Accelerate is only used if other BLAS/LAPACK are not used
    if env["OS"] == "Darwin":
        cmake_extra.append("find_library(ACCELERATE_FRAMEWORK Accelerate)")
        localenv.Append(
            LINKFLAGS=env.subst("${RPATHPREFIX}${ct_libdir}${RPATHSUFFIX}"))

    localenv.Append(LIBS=env['cantera_shared_libs'])
    localenv.Prepend(CPPPATH=['#include'])

    dest_dir = sample.dest_dir or sample.srcdir
    if sample.openmp and not env['HAS_OPENMP']:
        logger.info(f"Skipping sample {sample.name!r} because 'omp.h' was not found.")
    else:
        buildSample(localenv.Program, pjoin(dest_dir, sample.name),
                    multi_glob(localenv, sample.srcdir, *sample.file_patterns))

    if not sample.install:
        continue

    # Note: These CMakeLists.txt and SConstruct files are automatically installed
    # by the "RecursiveInstall" that grabs everything in the cxx directory.

    flag_excludes = [r"\$\(", "/TP", r"\$\)", "/nologo"]
    incdirs = [localenv["ct_incroot"]]
    libdirs = [localenv["ct_libdir"]]
    if localenv["package_build"]:
        # Remove sysroot flags in templated output files. This only applies to the
        # conda package for now.
        # Users should compile against their local SDKs, which should be backwards
        # compatible with the SDK used for building.
        flag_excludes.extend(["-isysroot", "-mmacosx", "-march", "-mtune",
                              "-fdebug-prefix-map", ".*/_build_env/"])
    else:
        incdirs.extend([localenv["sundials_include"], localenv["boost_inc_dir"]])
        incdirs.append(localenv["hdf_include"])
        incdirs.extend(localenv["extra_inc_dirs"])
        incdirs = sorted(set(incdirs))

        libdirs.extend(localenv["extra_lib_dirs"])
        libdirs = sorted(set(libdirs))

    if env["OS"] == "Darwin" and env["use_rpath_linkage"] and not env.subst("$__RPATH"):
        # SCons fails to specify RPATH on macOS, so circumvent that behavior by
        # specifying this directly as part of LINKFLAGS
        localenv.Append(LINKFLAGS=[env.subst(f'$RPATHPREFIX{d}$RPATHSUFFIX')
                                   for d in libdirs])

    cmake_libs = localenv['cantera_shared_libs'].copy()
    if "fmt" in cmake_libs:
        cmake_extra.append("find_package(fmt REQUIRED)")
        cmake_libs[cmake_libs.index("fmt")] = "fmt::fmt"
    cc_flags = compiler_flag_list(localenv["CCFLAGS"] + localenv["CXXFLAGS"],
                                  env["CC"], flag_excludes)
    link_flags = compiler_flag_list(localenv["LINKFLAGS"], env["CC"], flag_excludes)
    localenv["tmpl_compiler_flags"] = repr(cc_flags)
    localenv['tmpl_cantera_frameworks'] = repr(localenv['FRAMEWORKS'])
    localenv['tmpl_cantera_incdirs'] = repr([x for x in incdirs if x])
    localenv['cmake_cantera_incdirs'] = ' '.join(quoted(x) for x in incdirs if x)
    localenv['tmpl_cantera_libs'] = repr(localenv['cantera_shared_libs'])
    localenv['cmake_cantera_libs'] = ' '.join(cmake_libs)
    if env['OS'] == 'Darwin':
        localenv['cmake_cantera_libs'] += ' ${ACCELERATE_FRAMEWORK}'
        localenv['cmake_cantera_incdirs'] += ' "/usr/local/include"'
    localenv['tmpl_cantera_libdirs'] = repr([x for x in libdirs if x])
    localenv['cmake_cantera_libdirs'] = ' '.join(quoted(x) for x in libdirs if x)
    localenv['tmpl_cantera_linkflags'] = repr(link_flags)
    localenv['tmpl_progname'] = sample.name
    localenv['tmpl_sourcename'] = sample.name + '.cpp'
    localenv['cmake_extra'] = "\n".join(cmake_extra)
    env_args = []

    ## Generate SConstruct files to be installed
    if localenv['TARGET_ARCH'] is not None:
        env_args.append('TARGET_ARCH={0!r}'.format(localenv['TARGET_ARCH']))
    if 'MSVC_VERSION' in localenv:
        env_args.append('MSVC_VERSION={0!r}'.format(localenv['MSVC_VERSION']))
    localenv['tmpl_env_args'] = ', '.join(env_args)

    if localenv['package_build']:
        # For package builds, use environment variables or rely on SCons to find the
        # default compiler
        localenv['tmpl_cxx'] = "env['CXX'] = os.environ.get('CXX', env['CXX'])"
    else:
        # Otherwise, use the same compiler that was used to build Cantera, with the
        # environment variables optionally overriding
        localenv['tmpl_cxx'] = env.subst("env['CXX'] = os.environ.get('CXX', '$CXX')")

    sconstruct = localenv.SubstFile(pjoin(dest_dir, 'SConstruct'), 'SConstruct.in')
    install(pjoin('$inst_sampledir', 'cxx', dest_dir), sconstruct)

    ## Generate CMakeList.txt files to be installed
    cmakelists = localenv.SubstFile(pjoin(dest_dir, 'CMakeLists.txt'), 'CMakeLists.txt.in')
    install(pjoin('$inst_sampledir', 'cxx', dest_dir), cmakelists)
