#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Code for compiling latex from Python.

Based on: https://github.com/GjjvdBurg/labella.py

Author: Gertjan van den Burg
Copyright (c) 2018 - The Alan Turing Institute
License: See the LICENSE file.

"""

import os
import shutil
import subprocess
import tabulate
import tempfile


def compile_latex(fname, tmpdirname, silent=True):
    compiler = "latexmk"
    compiler_args = [
        "--pdf",
        "--outdir=" + tmpdirname,
        "--interaction=nonstopmode",
        fname,
    ]
    command = [compiler] + compiler_args
    try:
        output = subprocess.check_output(command, stderr=subprocess.STDOUT)
    except (OSError, IOError) as e:
        raise (e)
    except subprocess.CalledProcessError as e:
        print(e.output.decode())
        raise (e)
    else:
        if not silent:
            print(output.decode())


def build_latex_doc(tex, output_name=None, silent=True):
    with tempfile.TemporaryDirectory() as tmpdirname:
        basename = "labella_text"
        fname = os.path.join(tmpdirname, basename + ".tex")
        with open(fname, "w") as fid:
            fid.write(tex)

        compile_latex(fname, tmpdirname, silent=silent)

        pdfname = os.path.join(tmpdirname, basename + ".pdf")
        if output_name:
            shutil.copy2(pdfname, output_name)


def build_latex_table(
    table, headers, floatfmt="g", missingval="", bests="default", 
    table_spec=None
):
    """Construct the LaTeX code for a table

    This function creates the LaTeX code for a data table while taking number 
    formatting, headers, missing values, and "best value formatting" into 
    account.

    The numbers in the table are formatted following the provided float format 
    and the missing value indicator using the ``_format`` function from the 
    ``tabulate`` package. To indicate a missing value the data row should mark 
    this value as ``None``.

    The ``bests`` parameter is used to decide how to highlight the best values 
    in each row. It can be either ``'default'``, ``None``, a list of length 1 
    where the element is either ``min`` or ``max``, or a list of length ``K`` 
    with similar elements where ``K`` is the length of the data table. If it is 
    ``'default'`` then ``max`` will be considered best for each row. If a list 
    of length 1 is supplied then the provided function will be used for each 
    row. If ``None``, no highlighting will be done.

    The ``table_spec`` parameter allows the user to specify the table 
    specification. This value is not checked. If it is None, the first column 
    will get 'l' spec and the remaining columns will get the 'r' spec.

    """
    if bests == "default":
        bests = [max]
    elif bests is None:
        bests = []

    if len(bests) > 1:
        assert len(bests) == len(table)
    assert all((x in [min, max] for x in bests))

    if len(bests) == 0:
        best_funcs = [None for x in range(len(table))]
    elif len(bests) == 1:
        best_funcs = [bests[0] for x in range(len(table))]
    else:
        best_funcs = bests[:]

    list_of_lists, headers = table, headers
    cols = list(zip(*list_of_lists))
    coltypes = list(map(tabulate._column_type, cols))
    cols = [
        [tabulate._format(v, ct, floatfmt, missingval) for v in c]
        for c, ct in zip(cols, coltypes)
    ]
    n_cols = len(cols)

    data_rows = table
    text_rows = list(zip(*cols))

    text = []
    if table_spec is None:
        text.append("\\begin{tabular}{l%s}" % ("r" * n_cols))
    else:
        text.append("\\begin{tabular}{%s}" % table_spec)
    text.append(" & ".join(headers) + "\\\\")
    text.append("\\hline")
    for data_row, text_row, best_func in zip(data_rows, text_rows, best_funcs):
        text_row = list(text_row)
        if not best_func is None:
            best_val = best_func([x for x in data_row if isinstance(x, float)])
            best_idx = [i for i, v in enumerate(data_row) if v == best_val]
            for idx in best_idx:
                text_row[idx] = "\\textbf{" + text_row[idx] + "}"
        text.append(" & ".join(text_row) + "\\\\")
    text.append("\\hline")
    text.append("\\end{tabular}")

    return "\n".join(text)
