#! /usr/bin/env python

import os
import pathlib

# Inherit the parent construction environment
Import(['env', 'project_substitution_dictionary'])
project_dir = pathlib.Path(env['project_dir'])

# VVV Sphinx Scanner prototype
# TODO: boostrap from waves.scons_extensions.sphinx_scanner *after* the function is deployed on conda-forge
# DO NOT 'bootstrap' by importing the current/repository version of WAVES. That's not bootstrapping, it's a bug factory
import re
import SCons.Scanner
def sphinx_scanner():
    suffixes = ['.rst', '.txt']
    flags = re.MULTILINE
    expression = re.compile(r'^\s*\.\. (?:include|literalinclude|figure|bibliography)::\s*(.+)$', flags)
    def suffix_only(node_list):
        return [node for node in node_list if node.path.endswith(tuple(suffixes))]
    def regex_scan(node, env, path):
        contents = node.get_text_contents()
        includes = expression.findall(contents)
        includes = [file.strip() for file in includes]
        return includes
    custom_scanner = SCons.Scanner.Scanner(function=regex_scan, skeys=suffixes, recursive=suffix_only)
    return custom_scanner
env.Append(SCANNERS=sphinx_scanner())
# ^^^ Sphinx Scanner prototype

# Set empty alias return list
alias_list = []

# Perform variable substitution on Sphinx configuration file
env.Substfile(
    "conf.py.in",
    SUBST_DICT=project_substitution_dictionary)

# Copy root directory files into documentation build directory
copy_files = (
    ("README.txt", "#/README.rst"),
    ("CITATION.bib", "#/CITATION.bib"),
    ("LICENSE.txt", "#/LICENSE.txt"),
    ("environment.yml", "#/environment.yml")
)
for target, source in copy_files:
    Command(target=target,
            source=source,
            action=Copy("$TARGET", "$SOURCE"))

# Explicit Sphinx documentation dependency list
sphinx_configuration_source_list = [
    'conf.py',  # Generated by an SCons target
    # Not strictly required for all Sphinx build types, so not always found by the scanner.
    # It is required for Sphinx configuration, so it needs to be explicitly included in the deps list.
    'CITATION.bib',  # Copied from CITATION.bib to docs build directory by root SConscript
    'references.bib',
    'environment.yml',  # Copied from environment.yml to docs build directory by root SConscript
    'targets.txt',  # Found in conf.py, which is not currently scanned
    'waves_logo_brandmark_smaller.png',  # Found in conf.py, which is not currently scanned
    'waves_logo_primary_large_crop.png',  # Found in conf.py, which is not currently scanned
    '_static/custom.css'
]

documentation_file_list = [
    'eabm_api.rst',
    'eabm_cli.rst',
    'api.rst',
    'internal_api.rst',
    'changelog.rst',
    'citation.rst',
    'license.rst',
    'cli.rst',
    'computational_practices_introduction.rst',
    'computational_practices_version_control.rst',
    'computational_practices_documentation.rst',
    'computational_practices_build_system.rst',
    'computational_practices_compute_environment.rst',
    'computational_practices_regression_testing.rst',
    'devops.rst',
    'glossary.rst',
    'index.rst',
    'release_philosophy.rst',
    'scons_quickstart.rst',
    'scons_multiactiontask.rst',
    'installation.rst',
    'tutorial_introduction.rst',
    'tutorial_quickstart.rst',
    'tutorial_core.rst',
    'tutorial_supplemental.rst',
    'tutorial_wip.rst',
    'tutorial_00_scons.rst',
    'tutorial_01_geometry_waves.rst',
    'tutorial_02_partition_mesh_waves.rst',
    'tutorial_argparse_types_waves.rst',
    'tutorial_03_solverprep_waves.rst',
    'tutorial_04_simulation_waves.rst',
    'tutorial_05_parameter_substitution_waves.rst',
    'tutorial_06_include_files_waves.rst',
    'tutorial_07_cartesian_product_waves.rst',
    'tutorial_07_latin_hypercube_waves.rst',
    'tutorial_07_sobol_sequence_waves.rst',
    'tutorial_extend_study_waves.rst',
    'tutorial_08_data_extraction_waves.rst',
    'tutorial_09_post_processing_waves.rst',
    'tutorial_10_regression_testing_waves.rst',
    'tutorial_11_archival_waves.rst',
    'tutorial_setuptools_scm_waves.rst',
    'tutorial_cubit_abaqus.rst',
    'tutorial_cubit_sierra.rst',
    'tutorial_mesh_convergence_waves.rst',
    'tutorial_escape_sequences_waves.rst',
    'tutorial_remote_execution_waves.rst',
    'tutorial_sbatch_waves.rst',
    'tutorial_task_reuse_waves.rst',
    'waves_quickstart.rst',
    'zreferences.rst',
]

# Copy tutorial files to the build/docs directory
tutorials_dir = project_dir / pathlib.Path(env['tutorials_dir'])
tutorial_files_list = [
    tutorials_dir / 'scons_quickstart_SConstruct',
    tutorials_dir / 'scons_multiactiontask_SConstruct',
    tutorials_dir / 'tutorial_00_SConstruct',
    tutorials_dir / 'tutorial_01_geometry',
    tutorials_dir / 'tutorial_01_geometry_SConstruct',
    tutorials_dir / 'tutorial_02_partition_mesh',
    tutorials_dir / 'tutorial_02_partition_mesh_SConstruct',
    tutorials_dir / 'tutorial_argparse_types',
    tutorials_dir / 'tutorial_argparse_types_SConstruct',
    tutorials_dir / 'tutorial_03_solverprep',
    tutorials_dir / 'tutorial_03_solverprep_SConstruct',
    tutorials_dir / 'tutorial_04_simulation',
    tutorials_dir / 'tutorial_04_simulation_SConstruct',
    tutorials_dir / 'tutorial_05_parameter_substitution',
    tutorials_dir / 'tutorial_05_parameter_substitution_SConstruct',
    tutorials_dir / 'tutorial_06_include_files',
    tutorials_dir / 'tutorial_06_include_files_SConstruct',
    tutorials_dir / 'tutorial_07_cartesian_product',
    tutorials_dir / 'tutorial_07_cartesian_product_SConstruct',
    tutorials_dir / 'tutorial_07_latin_hypercube',
    tutorials_dir / 'tutorial_07_latin_hypercube_SConstruct',
    tutorials_dir / 'tutorial_07_sobol_sequence',
    tutorials_dir / 'tutorial_07_sobol_sequence_SConstruct',
    tutorials_dir / 'tutorial_08_data_extraction',
    tutorials_dir / 'tutorial_08_data_extraction_SConstruct',
    tutorials_dir / 'tutorial_09_post_processing',
    tutorials_dir / 'tutorial_09_post_processing_SConstruct',
    tutorials_dir / 'tutorial_10_regression_testing',
    tutorials_dir / 'tutorial_10_regression_testing_SConstruct',
    tutorials_dir / 'tutorial_11_archival',
    tutorials_dir / 'tutorial_11_archival_SConstruct',
    tutorials_dir / 'tutorial_setuptools_scm_SConstruct',
    tutorials_dir / 'tutorial_escape_sequences',
    tutorials_dir / 'tutorial_escape_sequences_SConstruct',
    tutorials_dir / 'tutorial_extend_study',
    tutorials_dir / 'tutorial_extend_study_SConstruct',
    tutorials_dir / 'tutorial_mesh_convergence',
    tutorials_dir / 'tutorial_mesh_convergence_SConstruct',
    tutorials_dir / 'tutorial_remote_execution',
    tutorials_dir / 'tutorial_remote_execution_SConstruct',
    tutorials_dir / 'tutorial_sbatch',
    tutorials_dir / 'tutorial_sbatch_SConstruct',
    tutorials_dir / 'tutorial_task_reuse',
    tutorials_dir / 'tutorial_task_reuse_SConstruct',
    tutorials_dir / 'rectangle_geometry_partition.scons',
    tutorials_dir / 'rectangle_mesh_solverprep_solve_extract.scons',
    tutorials_dir / 'gitignore',
    tutorials_dir / 'pyproject.toml',
    tutorials_dir / env['abaqus_dir'] / 'rectangle_geometry.py',
    tutorials_dir / env['abaqus_dir'] / 'rectangle_partition.py',
    tutorials_dir / env['abaqus_dir'] / 'rectangle_mesh.py',
    tutorials_dir / env['abaqus_dir'] / 'abaqus_journal_utilities.py',
    tutorials_dir / env['abaqus_dir'] / 'rectangle_compression.inp',
    tutorials_dir / env['abaqus_dir'] / 'assembly.inp',
    tutorials_dir / env['abaqus_dir'] / 'boundary.inp',
    tutorials_dir / env['abaqus_dir'] / 'field_output.inp',
    tutorials_dir / env['abaqus_dir'] / 'history_output.inp',
    tutorials_dir / env['abaqus_dir'] / 'materials.inp',
    tutorials_dir / env['abaqus_dir'] / 'parts.inp',
    tutorials_dir / env['abaqus_dir'] / 'rectangle_compression.inp.in',
    tutorials_dir / env['argparse_types_dir'] / 'rectangle_geometry.py',
    tutorials_dir / env['argparse_types_dir'] / 'rectangle_partition.py',
    tutorials_dir / env['argparse_types_dir'] / 'rectangle_mesh.py',
    tutorials_dir / env['cubit_dir'] / 'rectangle_geometry.py',
    tutorials_dir / env['cubit_dir'] / 'rectangle_partition.py',
    tutorials_dir / env['cubit_dir'] / 'rectangle_mesh.py',
    tutorials_dir / env['eabm_dir'] / 'argparse_types.py',
    tutorials_dir / env['python_dir'] / 'rectangle_compression_nominal.py',
    tutorials_dir / env['python_dir'] / 'rectangle_compression_cartesian_product.py',
    tutorials_dir / env['python_dir'] / 'rectangle_compression_cartesian_product.csv',
    tutorials_dir / env['python_dir'] / 'rectangle_compression_latin_hypercube.py',
    tutorials_dir / env['python_dir'] / 'rectangle_compression_sobol_sequence.py',
    tutorials_dir / env['python_dir'] / 'rectangle_compression_mesh_convergence.py',
    tutorials_dir / env['python_dir'] / 'mesh_convergence_stress.yaml',
    tutorials_dir / env['python_dir'] / 'post_processing.py',
    tutorials_dir / "tutorial_cubit/cubit",
    tutorials_dir / "tutorial_cubit/abaqus",
    tutorials_dir / "tutorial_cubit/sierra",
    tutorials_dir / "tutorial_cubit/SConstruct",
    tutorials_dir / "tutorial_cubit/eabm_package/sierra/rectangle_compression.i",
    tutorials_dir / "waves_quickstart/SConstruct",
    tutorials_dir / "waves_quickstart/SConscript",
]
copied_file_list = []
for file_path in tutorial_files_list:
    target = f'{file_path.parts[-2]}_{file_path.name}'
    Command(target=target,
            source=str(file_path),
            action=Copy("$TARGET", "$SOURCE"))
    copied_file_list.append(target)
source_files = sphinx_configuration_source_list + documentation_file_list + copied_file_list

sphinx_options = '-W'
html_directory = "html"
html_target = Dir(html_directory)
html = env.Command(
    target=[html_target],
    source=source_files,
    action=f"{env['sphinx_build']} ${{sphinx_options}} -b html ${{TARGET.dir.abspath}} ${{TARGET.dir.abspath}}{os.sep}{html_directory} ${{tags}}",
    sphinx_options=sphinx_options)
env.Clean(html, [html_target] + documentation_file_list)
env.AlwaysBuild(html)
alias_list.extend(env.Alias(html_directory, html))

# TODO: Combine the two html build logic
internal_directory = "html-internal"
internal_target = Dir(internal_directory)
internal = env.Command(
    target=[internal_target],
    source=source_files,
    action=f"{env['sphinx_build']} ${{sphinx_options}} -b html ${{TARGET.dir.abspath}} ${{TARGET.dir.abspath}}{os.sep}{internal_directory} ${{tags}}",
    sphinx_options=sphinx_options,
    tags="-t aea")
env.Clean(internal, [internal_target] + documentation_file_list)
env.AlwaysBuild(internal)
alias_list.extend(env.Alias(internal_directory, internal))

latex_directory = "latex"
latex_target = Dir(latex_directory)
latexpdf = env.Command(
    target=[latex_target],
    source=source_files,
    action=f"{env['sphinx_build']} -M latexpdf ${{TARGET.dir.abspath}} ${{TARGET.dir.abspath}} ${{tags}} ${{sphinx_options}}",
    sphinx_options=sphinx_options
)
env.Clean(latexpdf, [latex_target] + documentation_file_list)
env.AlwaysBuild(latexpdf)
alias_list.extend(env.Alias('latexpdf', latexpdf))

man_directory = "man"
man_target = Dir(man_directory)
man_source = [
    "man_index.rst",
    "ssh_builder_actions_warning.txt"  # Required by ``ssh_builder_actions`` docstring and not found in man_index.rst
]
man = env.Command(
    target=[man_target],
    source=man_source + sphinx_configuration_source_list,
    action=f"{env['sphinx_build']} ${{sphinx_options}} -b man ${{TARGET.dir.abspath}} ${{TARGET.dir.abspath}}{os.sep}{man_directory} ${{tags}}",
    sphinx_options=sphinx_options,
    tags="-t man")
env.Clean(man, [man_target] + documentation_file_list)
env.AlwaysBuild(man)
alias_list.extend(env.Alias(man_directory, man))

epub_directory = "epub"
epub_target = Dir(epub_directory)
epub = env.Command(
    target=[epub_target],
    source=source_files,
    action=f"{env['sphinx_build']} ${{sphinx_options}} -b epub ${{TARGET.dir.abspath}} ${{TARGET.dir.abspath}}{os.sep}{epub_directory} ${{tags}}",
    sphinx_options=sphinx_options)
env.Clean(epub, [epub_target] + documentation_file_list)
env.AlwaysBuild(epub)
alias_list.extend(env.Alias(epub_directory, epub))

# Collector alias to build all documentation
alias_list.extend(env.Alias('documentation', html + latexpdf + man))

if not env['unconditional_build'] and not env['sphinx_build']:
    print(f"Program 'sphinx-build' was not found in construction environment. Ignoring Sphinx target(s)")
    Ignore(['.', html_directory, 'html'], html)
    Ignore(['.', internal_directory, 'html-internal'], internal)
    Ignore(['.', latex_directory, 'latexpdf'], latexpdf)
    Ignore(['.', man_directory, 'man'], man)
    Ignore(['.', epub_directory, 'epub'], epub)

# Return the alias list to SConstruct for help message output
Return('alias_list')
