#! /usr/bin/env python

import os
import sys
import pathlib
import inspect

import waves

# Accept command line options with fall back default values
AddOption(
    "--build-dir",
    dest="variant_dir_base",
    default="build",
    nargs=1,
    type="string",
    action="store",
    metavar="DIR",
    help="SCons build (variant) root directory. Relative or absolute path. (default: '%default')",
)
AddOption(
    "--unconditional-build",
    dest="unconditional_build",
    default=False,
    action="store_true",
    help="Boolean to force building of conditionally ignored targets. (default: '%default')",
)
AddOption(
    "--solve-cpus",
    dest="solve_cpus",
    default=1,
    nargs=1,
    type="int",
    action="store",
    metavar="N",
    help="Run the Abaqus Solve task using N CPUs. (default: '%default')",
)
AddOption(
    "--print-build-failures",
    dest="print_build_failures",
    default=False,
    action="store_true",
    help="Print task *.stdout target file(s) on build failures. (default: '%default')",
)
# Python optparse appends to the default list instead of overriding. Must implement default/override ourselves.
default_abaqus_commands = [
    "/apps/abaqus/Commands/abq2024",
    "/usr/projects/ea/abaqus/Commands/abq2024",
    "abq2024",
    "abaqus",
]
AddOption(
    "--abaqus-command",
    dest="abaqus_command",
    nargs=1,
    type="string",
    action="append",
    metavar="COMMAND",
    help=f"Override for the Abaqus command. Repeat to specify more than one (default: {default_abaqus_commands})",
)

# Inherit user's full environment and set project options
env = waves.scons_extensions.WAVESEnvironment(
    ENV=os.environ.copy(),
    variant_dir_base=pathlib.Path(GetOption("variant_dir_base")),
    unconditional_build=GetOption("unconditional_build"),
    solve_cpus=GetOption("solve_cpus"),
    print_build_failures=GetOption("print_build_failures"),
    abaqus_commands=GetOption("abaqus_command"),
    TARFLAGS="-c -j",
    TARSUFFIX=".tar.bz2",
)

# Conditionally print failed task *.stdout files
env.PrintBuildFailures(print_stdout=env["print_build_failures"])

# Empty defaults list to avoid building all simulation targets by default
env.Default()

# Set project internal variables and variable substitution dictionaries
project_configuration = pathlib.Path(inspect.getfile(lambda: None))
project_dir = project_configuration.parent
project_name = project_dir.name
version = "0.1.0"
author_list = [
    "Kyle Brindley",
    "Prabhu S. Khalsa",
    "Thomas Roberts",
    "Sergio Cordova",
    "Matthew Fister",
    "Scott Ouellette",
]
author_latex = r" \and ".join(author_list)
latex_project_name = project_name.replace("_", "-")
documentation_source_dir = pathlib.Path("docs")
modsim_package_dir = pathlib.Path("modsim_package")
test_source_dir = "modsim_package/tests"
project_variables = {
    "project_configuration": project_configuration,
    "project_dir": project_dir,
    "project_name": project_name,
    "version": version,
    "author_list": author_list,
    "author_html": ", ".join(author for author in author_list),
    "author_latex": author_latex,
    "documentation_pdf": f"{latex_project_name}-{version}.pdf",
    "report_pdf": f"{latex_project_name}-{version}-report.pdf",
    "documentation_abspath": project_dir / documentation_source_dir,
    "modsim_package_abspath": project_dir / modsim_package_dir,
    "test_source_abspath": project_dir / test_source_dir,
    "datacheck_alias": "datacheck",
    "regression_alias": "regression",
}
for key, value in project_variables.items():
    env[key] = value

# Make the modsim package importable for: (1) SConscript files and (2) Python and Abaqus Python environments
sys.path.insert(0, str(project_dir))
env.PrependENVPath("PYTHONPATH", project_dir)

# Find required programs for conditional target ignoring and absolute path for use in target actions
env["SPHINX_BUILD"] = env.AddProgram(["sphinx-build"])
env["ABAQUS_PROGRAM"] = env.AddProgram(
    env["abaqus_commands"] if env["abaqus_commands"] is not None else default_abaqus_commands
)

# Add WAVES builders and scanners
env.Append(
    BUILDERS={
        "AbaqusExtract": waves.scons_extensions.abaqus_extract(program=env["ABAQUS_PROGRAM"]),
        "CondaEnvironment": waves.scons_extensions.conda_environment(),
    }
)
env.Append(SCANNERS=waves.scons_extensions.sphinx_scanner())

# Dump the Conda environment as documentation of as-built target environment
environment_target = env.CondaEnvironment(
    target=[env["variant_dir_base"] / "environment.yaml"],
    source=[],
)
env.AlwaysBuild(environment_target)
env.Alias("environment", environment_target)

# Add documentation target(s)
# Project documentation
build_dir = env["variant_dir_base"] / documentation_source_dir
SConscript(
    documentation_source_dir / "SConscript",
    variant_dir=build_dir,
    exports={"env": env, "project_variables": project_variables},
)

# Analysis report
report_dir = pathlib.Path("report")
build_dir = env["variant_dir_base"] / report_dir
SConscript(
    report_dir / "SConscript",
    variant_dir=build_dir,
    exports={"env": env, "project_variables": project_variables},
    duplicate=True,
)

# Add simulation targets
workflow_configurations = [
    "rectangle_compression-nominal",
    "rectangle_compression-mesh_convergence",
]
for workflow in workflow_configurations:
    build_dir = env["variant_dir_base"] / workflow
    SConscript(workflow, variant_dir=build_dir, exports={"env": env}, duplicate=False)

# Add unit test target
test_workflow = "unit_testing"
test_build_dir = env["variant_dir_base"] / test_workflow
SConscript(test_workflow, variant_dir=test_build_dir, exports={"env": env}, duplicate=False)

# Add default target list to help message
# 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.
env.ProjectHelp()
