#! /usr/bin/env python
"""Configure the SPADE c++ compilation."""

import os
import pathlib
import platform
import shutil
import string
import sys

import _settings
import _utilities

# Project command line options
AddOption(
    "--build-dir",
    default="build",
    nargs=1,
    type="string",
    action="store",
    metavar="DIR",
    help="SCons build (variant) root directory. Relative or absolute path. (default: '%default')",
)
AddOption(
    "--abaqus-command",
    default="abaqus",
    nargs=1,
    type="string",
    action="store",
    help="Abaqus executable relative or absolute path (default: '%default')",
)
AddOption(
    "--recompile",
    default=False,
    action="store_true",
    help="Recompile the spade executable. Implemented as an AlwaysBuild when True (default: '%default')",
)

# Project environment
windows_system = platform.system().lower() == "windows"
user_env = os.environ.copy()
env = Environment(
    ENV=user_env,
    recompile=GetOption("recompile"),
)
env.Tool("compilation_db")

# Abaqus and system settings
abaqus_command = GetOption("abaqus_command")
env["ABAQUS_PROGRAM"] = shutil.which(abaqus_command, path=env["ENV"]["PATH"])
if env["ABAQUS_PROGRAM"] is None:
    sys.exit(f"Could not find the Abaqus executable at '{abaqus_command}'")
abaqus_version = _utilities.abaqus_official_version(env["ABAQUS_PROGRAM"])
abaqus_installation, abaqus_code_bin, abaqus_code_include = _utilities.return_abaqus_code_paths(env["ABAQUS_PROGRAM"])

# Add CXX if missing; Use compiler name directly instead of inheriting from $CC
if "CXX" in user_env and "CXX" not in env:
    env["CXX"] = user_env["CXX"]
if env["CXX"] == "$CC":
    env["CXX"] = env["CC"]

# Search for OS-specific include and library paths
conda_prefix = pathlib.Path(user_env["CONDA_PREFIX"])
if windows_system:
    conda_include_paths = list(conda_prefix.glob("Library/include"))
    conda_lib_path = next((path for path in conda_prefix.glob("Library/lib")), None)
else:
    conda_include_paths = list(conda_prefix.glob("include"))
    conda_lib_path = next((path for path in conda_prefix.glob("lib")), None)
if not conda_include_paths or conda_lib_path is None:
    sys.exit("Could not find the expected OS-specific CONDA_PREFIX 'include' or 'lib' path")

# Construct OS-specific flags
if windows_system:
    env["CXXFLAGS"] = " /TP /MD /external:W0 /std:c++17 /EHsc /DH5_BUILT_AS_DYNAMIC_LIB " + " ".join(
        f"/external:I{path.as_posix()}" for path in conda_include_paths
    )
    env["ABAQUSCXXFLAGS"] = (
        env["CXXFLAGS"]
        + _settings._compiler_flags_msvc
        + f" /external:I{abaqus_code_include.as_posix()} /external:I{abaqus_installation.as_posix()}"
    )
    compile_cpp = string.Template("${CXX} ${ABAQUSCXXFLAGS}")
    link_exe = string.Template(
        "link"
        " /nologo /INCREMENTAL:NO /subsystem:console /machine:AMD64 /STACK:20000000 /NODEFAULTLIB:LIBC.LIB"
        " /NODEFAULTLIB:LIBCMT.LIB /DEFAULTLIB:OLDNAMES.LIB /DEFAULTLIB:kernel32.lib /DEFAULTLIB:user32.lib"
        " /DEFAULTLIB:advapi32.lib /FIXED:NO /LARGEADDRESSAWARE"
        " /Fe:%J %L"
        f" /LIBPATH:{conda_lib_path.as_posix()}"
        " %F %M ${objects} %B %O"
        " oldnames.lib user32.lib ws2_32.lib netapi32.lib advapi32.lib"
        " msvcrt.lib vcruntime.lib ucrt.lib"
        " getopt.lib hdf5.lib hdf5_cpp.lib hdf5_hl.lib"
    )
else:
    env["CXXFLAGS"] = (
        env["CXXFLAGS"] + " -std=c++17 " + " ".join(f"-isystem {path.as_posix()}" for path in conda_include_paths)
    )
    env["ABAQUSCXXFLAGS"] = (
        env["CXXFLAGS"]
        + _settings._compiler_flags_gcc
        + f" -isystem {abaqus_code_include.as_posix()} -isystem {abaqus_installation.as_posix()}"
    )
    compile_cpp = string.Template("${CXX} ${ABAQUSCXXFLAGS}")
    link_exe = string.Template(
        "${CXX}"
        " -fPIC -Wl,-Bdynamic -Wl,--add-needed"
        " -o %J %F %M ${objects} %L %B %O"
        f" -L{conda_lib_path.as_posix()} -Wl,-rpath,{abaqus_code_bin.as_posix()},-rpath,{conda_lib_path.as_posix()}"
        " -lhdf5 -lhdf5_cpp -lstdc++ -lhdf5_hl "
    )

# Configure tasks
build_directory = pathlib.Path(GetOption("build_dir"))
sconsign_file = build_directory / ".sconsign.dblite"
env.SConsignFile(sconsign_file)
env.SConscript(
    "SConscript",
    variant_dir=build_directory,
    exports={"env": env, "compile_cpp": compile_cpp, "link_exe": link_exe},
    duplicate=True,
)

# Add aliases to help message so users know what build target options are available
# This must come *after* all expected Alias definitions and SConscript files.
from SCons.Node.Alias import default_ans  # noqa: E402

alias_help = "\nTarget Aliases:\n"
for alias in default_ans:
    alias_help += f"    {alias}\n"
try:
    Help(alias_help, append=True, keep_local=True)
except TypeError:
    Help(alias_help, append=True)
