#   This file is part of the program djaTeX.
#
#   Copyright (C) 2017 by Marc Culler and others. 
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#   Project homepage: https://bitbucket.org/marc_culler/djatex
#   Author homepage: https://marc-culler.info

import os, tempfile
from subprocess import run, PIPE

class LaTeXFile:
    """
    An object which holds LaTeX and, optionally, BibTeX source strings.  Its
    compile method creates a byte sequence containing a pdf rendering of the
    LaTeX source, as well as strings containing the xelatex and bibtex logs.
    These are generated by running xelatex and bibtex in a temporary directory
    which is removed once the results are copied into the LaTeXFile object.  If
    the home_dir argument is not None it should be an absolute path to a
    directory containing auxiliary files needed for compilation, such as inputs,
    images or font .fd files.  These will be symlinked into the temporary
    directory before LaTeX is run.
    """
    rerun = b'Rerun to get cross-references right.'
    latex_args = ['xelatex', '-file-line-error', '-halt-on-error', 'source']
    
    def __init__(self, latex_source, bibtex_source=None, home_dir=None,
                 build_dir=None, env=None ):
        self.latex_result = self.bib_result = None
        self.latex_source = latex_source
        if home_dir:
            assert os.path.isdir(home_dir)
        self.home_dir = home_dir
        if build_dir:
            assert os.path.isdir(build_dir)
        self.build_dir = build_dir
        self.env = env
        if isinstance(latex_source, str):
            self.latex_source = self.latex_source.encode('utf8')
        elif not isinstance(latex_source, bytes):
            raise ValueError('the LaTeX source must be of type str or bytes.')
        self.bibtex_source = bibtex_source
        if bibtex_source is None:
            return
        elif isinstance(bibtex_source, str):
            self.bibtex_source = bibtex_source.encode('utf8')
        elif not isinstance(bibtex_source, bytes):
            raise ValueError('The bibtex source must be of type str or bytes.')

    def compile(self):
        if self.build_dir:
            self._compile(self.build_dir)
        else:
            with tempfile.TemporaryDirectory(prefix='renderLaTeX') as build_dir:
                self._compile(build_dir)

    def _compile(self, build_dir):
        tex_path = os.path.join(build_dir, 'source.tex')
        bibtex_path = os.path.join(build_dir, 'source.bib')
        log_path = os.path.join(build_dir, 'source.log')
        pdf_path = os.path.join(build_dir, 'source.pdf')
        if self.home_dir:
            for extra_file in os.listdir(self.home_dir):
                os.symlink(os.path.join(self.home_dir, extra_file),
                           os.path.join(build_dir, extra_file))
        with open(tex_path, 'wb') as output:
            output.write(self.latex_source)
        latex_result = run(self.latex_args, cwd=build_dir, timeout=30,
                           stdout=PIPE, stderr=PIPE, env=self.env)
        if latex_result.returncode == 0 and self.bibtex_source:
            with open(bibtex_path, 'wb') as output:
                output.write(self.bibtex_source)
            self.bib_result = run(['bibtex', 'source'], cwd=build_dir, timeout=30,
                                  stdout=PIPE, stderr=PIPE, env=self.env)
            latex_result = run(self.latex_args, cwd=build_dir, timeout=30,
                               stdout=PIPE, stderr=PIPE, env=self.env)
        if latex_result.returncode == 0 and latex_result.stdout.find(self.rerun) >= 0:
            latex_result = run(self.latex_args, cwd=build_dir, timeout=30,
                               stdout=PIPE, stderr=PIPE, env=self.env)
        self.latex_result = latex_result
        if os.path.exists(log_path):
            with open(log_path, 'rb') as log:
                self.log = log.read()
        if self.latex_result.returncode == 0 and os.path.exists(pdf_path):
            with open(pdf_path, 'rb') as pdf:
                self.pdf = pdf.read()
        else:
            self.pdf = None

    def errors(self):
        if self.latex_result.returncode:
            error_dict = {
                'stage': 'latex',
                'source': [line.decode('utf-8') for line in self.latex_source.split(b'\n')],
                'output': [line.decode('utf-8') for line in self.latex_result.stdout.split(b'\n')],
                'log': [line.decode('utf8') for line in self.log.split(b'\n')]
            }
        elif self.bib_result and self.bib_result.returncode:
            error_dict = {
                'stage': 'bibtex',
                'source': [line.decode('utf-8') for line in self.bibtex_source.split(b'\n')],
                'output': [line.decode('utf-8') for line in self.bib_result.stdout.split(b'\n')]
            }
        else:
            error_dict = None
        return error_dict

test_latex = r"""
\documentclass[11pt]{article}
\usepackage{cite}

\begin{document}

\title{My Article}
\author{Nobody Jr.}
\date{Today}
\maketitle

Blablabla said Nobody ~\cite{Nobody06}.

$$X = X$$

\bibliography{source}{}
\bibliographystyle{plain}
\end{document}
"""
test_bibtex = r"""
@misc{ Nobody06,
       author = "Nobody Jr",
       title = "My Article",
       year = "2006" }
"""
