#! /bin/python3

#
# Combine tous les fichiers markdown de la documentation en un seul fichier
#
# Requis :
# - pandoc
# - texlive (lister les paquets)
# - texlive-bibtex-extra
# - texlive-font-utils
# - latexmk
#
# Usage :
# - python3 ./mkdocs2pdf.py
#

import os
import sys
import yaml
import re
import shutil

# verbose output
verbose = False

baseDir = os.getcwd()
buildDir = os.path.join(baseDir, 'build')
latexSourceDir = os.path.join(baseDir, 'docs/latex')
pdfBuildDir = os.path.join(buildDir, 'pdf_build')
outputDir = os.path.join(baseDir, 'dist/assets/docs/pdf')

latexModelDir = 'latex_models'
modelDir = os.path.join(pdfBuildDir, latexModelDir)

mergedDocFilenamePrefix = 'cassiopee_doc_contents_'
filenamePrefix = 'cassiopee_doc_'

latexModelRepository = 'https://gitlab.irstea.fr/david.dorchies/latex_models.git'

def runCommand(cmd):
    if os.waitstatus_to_exitcode(os.system(cmd)) != 0:
        raise RuntimeError("error executing:",cmd)

# Create a symbolic link
def createLink(src):
    # check if destination already exists
    dest = os.path.basename(src)
    if os.path.exists(dest):
        if not os.path.islink(dest):
            raise Exception('{} exists but is not a symbolic link'.format(dest))
    else:
        runCommand('ln -s {}'.format(src))

def createEmptyDir(path):
    if os.path.exists(path):
        shutil.rmtree(path)
    os.makedirs(path)

def createDir(path):
    if not os.path.exists(path):
        os.makedirs(path)

# Reads an MkDocs configuration file
def readConfig(sYAML):
    f = open(sYAML, 'r')
    with f:
        try:
            dMkdocsYaml = yaml.load(f, Loader=yaml.SafeLoader)
        except yaml.YAMLError as e:
            sys.exit("ERROR on YAML loading {}: {}".format(sYAML, str(e)))
    return dMkdocsYaml

def getMdHeader(title, level):
    return "#" * level + " " + title + "\n"

def shiftMdHeaders(mdContent, level):
    if level == 0:
        return mdContent

    lMd = mdContent.splitlines()
    for i, item in enumerate(lMd):
        if len(item) > 0 :
            if item[0] == "#":
                lMd[i] = ("#" * level) + item
    return "\n".join(lMd)

# Browses MkDocs configuration file and merges .md files
def exploreAndMerge(docs_dir, nav, output = '', level = 0):
    """
    @param docs_dir path to the mkdocs content files
    @param nav dictionnary with the structure to explore
    @param output markdown files content already merged
    """
    if type(nav) is str:
        nav = [nav]
    for d in nav:
        if type(d) is str:
            filepath = os.path.join(docs_dir, d)
            f = open(filepath, 'r')
            # Triple "../" because file will be compiled from pdf_build/latex_models
            path = os.path.join(baseDir, os.path.dirname(filepath))
            s = f.read() + "\n"
            # Modification of image and links paths
            s = re.sub(r'(\!\[.+\]\()(.+)(\))', r'\1'+path+r'/\2\3', s)
            s = re.sub(r'(\\\()(.+?)(\\\))', r'$\2$', s)
            s = shiftMdHeaders(s, level)
            output += "\n" + s

        elif type(d) is dict:
            level += 1
            for key, value in d.items():
                if type(value) is list:
                    output += "\n" + getMdHeader(key, level) + "\n"
                    output = exploreAndMerge(docs_dir, value, output, level)
                else:
                    output = exploreAndMerge(docs_dir, value, output, level-1) # do not dig level when sub-chapter has only one entry (alias)
            level -= 1
    return output

# Creates a filePath.tex LaTeX contents file based on filePath.md
def convertMdToTex(filePath):
    # Convert .md to .tex
    runCommand(
        'pandoc {0}.md -f markdown -t latex -s -o {0}.tex'.format(filePath)
    )
    # Remove header of tex file
    bContent = False
    ls = []
    with open('{}.tex'.format(filePath), 'r') as f:
        for line in f:
            if line.strip() == '\\end{document}':
                bContent = False
                exit
            if bContent:
                ls.append(line)
            if line.strip() == '\\begin{document}':
                bContent = True
    # Adjust generated content
    for i, line in enumerate(ls):
        l = line
        # adjust images max width/height
        l = l.replace('\\includegraphics', '\\includegraphics[max size={\\textwidth}{0.9\\textheight}]')
        # force figures placement
        l = l.replace('\\begin{figure}', '\\begin{figure}[h!]')
        # make som subsubsections invisible (for CHANGELOG)
        l = re.sub(r'(\\subsubsection)({[0-9]+.[0-9]+.[0-9]+)', r'\1*\2', l)
        ls[i] = l

    with open('{}.tex'.format(filePath), 'w') as f:
        f.writelines(ls)

def getLatexModel():
    # Clone Git repository
    os.chdir(pdfBuildDir)
    if os.path.isdir(latexModelDir):
        # git directory exists, update it
        os.chdir(latexModelDir)
        runCommand('git pull')
        # platform independent "cd .."
        os.chdir(os.path.dirname(os.getcwd()))
    else:
        runCommand('git clone {} {}'.format(latexModelRepository, latexModelDir))
    # back to original working drectory
    os.chdir(baseDir)

# Inject generated merged documentation and necessary resources (template, logos…)
# into LaTeX models directory by symlinking them
def injectContentIntoModel(mergedDocFilenameTex, lang):
    """
    @param mergedDocFilenameTex filename of the generated LaTeX merged doc, with its .tex extension, without directory
    @param lang language; used to inject content in the right template
    """
    # Symlink necessary resources
    os.chdir(modelDir)
    relPathToMergedTexDoc = os.path.join('..', mergedDocFilenameTex)
    createLink(relPathToMergedTexDoc)
    latexTemplate = filenamePrefix + lang + '.tex'
    relPathToLatexTemplate = os.path.join(latexSourceDir, latexTemplate)
    createLink(relPathToLatexTemplate)
    createLink(os.path.join(latexSourceDir, 'logo_pole.png'))
    createLink('{}/schema_rugosite_fond.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')))
    createLink('{}/bloc_cylindre.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')))
    createLink('{}/bloc_face_arrondie.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')))
    createLink('{}/bloc_base_carree.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')))
    runCommand('rm rapport_inrae/logos.tex')
    createLink('{} rapport_inrae/'.format(os.path.join(latexSourceDir, 'logos.tex')))
    # back to original working drectory
    os.chdir(baseDir)

# Build a PDF file from the LaTeX source
def buildPDF(lang):
    # Compile LaTeX source
    os.chdir(modelDir)
    sourceTexFile = filenamePrefix + lang + '.tex'
    outputPdfFile = filenamePrefix + lang + '.pdf'

    # copy Cassiopée version LateX file
    cvt = os.path.join(buildDir, 'cassiopee_version.tex')
    shutil.copy(cvt, modelDir)

    if verbose:
        os.system('latexmk -f -xelatex -pdf -interaction=nonstopmode {} > /dev/null'.format(sourceTexFile))
    else:
        os.system('latexmk -f -xelatex -pdf -interaction=nonstopmode {} > /dev/null 2>&1'.format(sourceTexFile))

    # copy generated PDF to release directory
    shutil.copy(outputPdfFile, outputDir)
    # back to original working drectory
    os.chdir(baseDir)

# Creates the PDF documentation file for the given language
def buildDocForLang(lang):

    # Prepare temporary build directory
    createEmptyDir(pdfBuildDir)
    # Prepare output directory
    createDir(outputDir)

    # Read config
    yamlPath = 'mkdocs/mkdocs-' + lang + '.yml'
    dMkdocsYaml = readConfig(yamlPath)

    # Create string with merged MarkDown
    s = exploreAndMerge(dMkdocsYaml['docs_dir'], dMkdocsYaml['nav'])
    # Save the merged .md file
    mergedDocFilename = mergedDocFilenamePrefix + lang
    mergedDocOutputPath = os.path.join(pdfBuildDir, mergedDocFilename)
    # remove internal links @TODO convert them to hyperref ?
    s = re.sub(r'\[([^/]+)\]\([^ ]+\.md\)', r'\1', s)
    with open('{}.md'.format(mergedDocOutputPath), 'w') as f:
        f.writelines(s)

    # Convert to tex format
    convertMdToTex(mergedDocOutputPath)

    # Get INRAE report LaTeX model and inject merged content inside
    getLatexModel()
    injectContentIntoModel(mergedDocFilename + '.tex', lang)

    # Build PDF from LaTeX source
    buildPDF(lang)

    # Clean build dir
    shutil.rmtree(pdfBuildDir)
    #raise RuntimeError()


if __name__ == '__main__':

    for l in ['fr', 'en']:
        print('building PDF doc for language "{}"'.format(l))
        buildDocForLang(l)