from collections import namedtuple
from pathlib import Path
import subprocess
from SCons import Errors

from buildutils import multi_glob

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

localenv = env.Clone()

Page = namedtuple('Page', ['name', 'title', 'objects'])


if localenv['doxygen_docs'] or localenv['sphinx_docs']:
    vars = {
        "@INCLUDE@": "interfaces/clib",
        "@ENABLED_SECTIONS@": "CLIB_GENERATED",
        "@GENERATE_HTML@": "YES",
        "@WARNINGS@": "YES",
        "@WARN_IF_DOC_ERROR@": "YES",
        "@WARN_IF_INCOMPLETE_DOC@": "YES",
    }
    if env["clib_legacy"]:
        vars["@INCLUDE@"] = ""
        vars["@ENABLED_SECTIONS@"] = ""
    env.Substfile(
        "#build/doc/Doxyfile_html", "#doc/doxygen/Doxyfile.in", SUBST_DICT=vars)

    docs = build(localenv.Command(
        "#build/doc/html/cxx/index.html", "#build/doc/Doxyfile_html",
        [
            Delete("build/doc/html/cxx"),
            'doxygen $SOURCE',
            Copy("build/doc/html/cxx", "build/doc/doxygen/html")
        ]
    ))
    env.Depends(docs, env.Glob('#doc/doxygen/*') +
                      multi_glob(env, '#include/cantera', 'h') +
                      multi_glob(env, '#include/cantera/*', 'h') +
                      multi_glob(env, '#src/cantera/*', 'h', 'cpp'))

    env.Alias('doxygen', docs)

if localenv['sphinx_docs']:
    localenv.PrependENVPath('PYTHONPATH', Dir('#build/python').abspath)
    def build_sphinx(target, source, env):
        cmd = [env['sphinx_cmd']] + env['sphinx_options'].split()
        if not env['run_examples']:
            cmd.extend(('-D', 'plot_gallery=0'))
        if env['logging'] == 'debug':
            cmd.append('-v')
        cmd += ('-b', 'html', '-d', 'build/doc/sphinx/doctrees', 'build/doc/sphinx',
                'build/doc/html')
        code = subprocess.call(cmd, env=env['ENV'])
        if code:
            raise Errors.BuildError(target, action=env['sphinx_cmd'])

    # RecursiveInstall knows to copy modified files, unlike Copy
    copy_sphinx = localenv.RecursiveInstall("#build/doc/sphinx", "sphinx")
    copy_python_samples = localenv.RecursiveInstall("#build/doc/samples/python",
                                                    "#samples/python")
    copy_matlab_samples = localenv.RecursiveInstall(
        "#build/doc/samples/matlab", "#samples/matlab")
    copy_contrib = localenv.Command("#build/doc/sphinx/develop/CONTRIBUTING.md",
                                    "#CONTRIBUTING.md",
                                    Copy("$TARGET", "$SOURCE"))

    copy_switcher = localenv.Command("#build/doc/html/dev/_static/doc-versions.json",
                                     "sphinx/_static/doc-versions.json",
                                     Copy("$TARGET", "$SOURCE"))

    sphinxdocs = build(localenv.Command('#build/doc/html/index.html',
        'sphinx/conf.py', build_sphinx))
    env.Alias('sphinx', sphinxdocs)
    env.Depends(sphinxdocs, [copy_sphinx, copy_contrib, copy_switcher])
    env.Depends(sphinxdocs, [copy_python_samples, copy_matlab_samples])
    env.Depends(sphinxdocs, env['python_module'])

    # Gather all C++ samples into a single directory so they can be presented as a
    # single sub-gallery by sphinx-gallery
    cxxfiles = (Glob("#samples/cxx/*/*.cpp") + Glob("#samples/cxx/*/*.h")
                + Glob("#samples/cxx/*.rst"))
    for cxxfile in cxxfiles:
        env.Depends(sphinxdocs,
                    localenv.Command(f"#build/doc/samples/cxx/{cxxfile.name}", cxxfile,
                                     Copy("$TARGET", "$SOURCE")))

    # Gather all Fortran (F77 and F90) samples into a single directory
    fort_files = (Glob("#samples/f77/*.f") + Glob("#samples/f77/*.cpp")
                  + Glob("#samples/f90/*.f90") + Glob("#samples/f90/README.rst"))
    for fort_file in fort_files:
        env.Depends(sphinxdocs,
                    localenv.Command(f"#build/doc/samples/fortran/{fort_file.name}",
                                     fort_file, Copy("$TARGET", "$SOURCE")))

    # Gather clib sample files
    clib_legacy_files = Glob("#samples/clib_legacy/*.c")
    clib_legacy_files.append(File("#samples/clib_legacy/README.rst"))
    for clib_file in clib_legacy_files:
        env.Depends(sphinxdocs,
                    localenv.Command(f"#build/doc/samples/clib_legacy/{clib_file.name}",
                                     clib_file, Copy("$TARGET", "$SOURCE")))

    # Gather generated CLib sample files
    clib_files = Glob("#samples/clib/*.c")
    clib_files.append(File("#samples/clib/README.rst"))
    for clib_file in clib_files:
        env.Depends(sphinxdocs,
                    localenv.Command(
                        f"#build/doc/samples/clib/{clib_file.name}",
                        clib_file, Copy("$TARGET", "$SOURCE")))

    # Gather C# sample files
    csharp_files = Glob("#interfaces/dotnet/examples/*/*.cs")
    csharp_files.append(File("#interfaces/dotnet/examples/README.rst"))
    for csharp_file in csharp_files:
        env.Depends(sphinxdocs,
                    localenv.Command(
                        f"#build/doc/samples/dotnet/{csharp_file.name}",
                        csharp_file, Copy("$TARGET", "$SOURCE")))

    # Generate documentation for SCons configuration options
    def save_config(target, source, env):
        Path(str(target[0])).parent.mkdir(parents=True, exist_ok=True)
        with open(str(target[0]), "w") as outfile:
            outfile.write(env["config"].to_rest())
    scons_opts = localenv.Command(
        "#build/doc/sphinx/develop/compiling/scons-config-options.rst.inc",
        "#SConstruct", save_config)
    env.Depends(sphinxdocs, scons_opts)

    localenv.AlwaysBuild(sphinxdocs)
    if localenv['doxygen_docs']:
        localenv.Depends(sphinxdocs, docs)

doc_files = install(localenv.RecursiveInstall, '$inst_docdir',
                    '#build/doc/html', exclude=['\\.map', '\\.md5'])

env['installed_docs'] = bool(doc_files)
