# -*- coding: utf-8 -*-
"""
chempy.util.table
-------------------

Convenience functions for presenting reaction systems in tables.
"""
from __future__ import (absolute_import, division, print_function)


import os
import shutil
import subprocess
import tempfile

from ..units import to_unitless, get_derived_unit

tex_templates = {
    'document': {
        'default': r"""
\documentclass[a4paper,9pt]{article}
\pagestyle{empty}
\usepackage[paper=a4paper,margin=1cm]{geometry}
%(usepkg)s
\hypersetup{
  bookmarksnumbered=true,
  breaklinks=false,
  raiselinks=true,
  pdfborder={0 0 0},
  colorlinks=true,
  plainpages=false,
  pdfstartview={FitH},
  pdfcreator={LaTeX with hyperref package},
  citecolor=teal,
  linkcolor=red,
  urlcolor=blue,
}
\begin{document}
%(begins)s
%(table)s
%(ends)s
\end{document}
"""
    },
    'table': {
        'default': r"""
\begin{%(table_env)s}
\centering
\label{tab:%(label)s}
\caption[%(short_cap)s]{%(long_cap)s}
\begin{tabular}{%(alignment)s}
\toprule
%(header)s
\midrule
%(body)s
\bottomrule
\end{tabular}
\end{%(table_env)s}""",
        'longtable': r"""
\begin{%(table_env)s}{%(alignment)s}
\caption[%(short_cap)s]{%(long_cap)s
\label{tab:%(label)s}}\\
\toprule
%(header)s
\midrule
%(body)s
\bottomrule
\end{%(table_env)s}"""
    }
}


def render_tex_to_pdf(contents, texfname, pdffname, output_dir, save):
    created_tempdir = False
    try:
        if output_dir is None:
            output_dir = tempfile.mkdtemp()
            created_tempdir = True
        texpath = os.path.join(output_dir, texfname)
        pdfpath = os.path.join(output_dir, pdffname)
        cmds = ['pdflatex', '-halt-on-error', '-interaction',
                'batchmode', texfname]
        with open(texpath, 'wt') as ofh:
            ofh.write(contents)
            ofh.flush()
        with open(pdfpath + '.out', 'wb') as logfile:
            p = subprocess.Popen(cmds, cwd=output_dir,
                                 stdout=logfile, stderr=logfile)
            retcode = p.wait()
            p = subprocess.Popen(cmds, cwd=output_dir,
                                 stdout=logfile, stderr=logfile)
            retcode += p.wait()
        if retcode:
            fmtstr = "{}\n returned with exit status {}"
            raise RuntimeError(fmtstr.format(' '.join(cmds), retcode))
        else:
            return pdfpath
    finally:
        if save is True or save == 'True':
            pass
        else:
            if save is False or save == 'False':
                if created_tempdir:
                    shutil.rmtree(output_dir)
            else:
                # interpret path to copy pdf to.
                shutil.copy(pdfpath, save)


def rsys2tablines(rsys, rref0=1, coldelim=' & ',
                  tex=True, ref_fmt=None,
                  unit_registry=None, unit_fmt='{}', k_fmt='{0:.4g}'):
    """
    Generates a table representation of a ReactionSystem.

    Parameters
    ----------
    rsys: ReactionSystem
    rref0: integer
        default start of index counter (default: 1)
    coldelim: string
        column delimiter (default: ' & ')
    tex: bool
        use latex formated output (default: True)
    ref_fmt: string or callable
        format string of ``ref`` attribute of reactions
    unit_registry: unit registry
        optional (default: None)
    """
    if ref_fmt is None:
        def ref_fmt(s):
            if s is None:
                return 'None'
            if tex and s.startswith('doi:'):
                return r'\texttt{\href{http://dx.doi.org/'+s[4:]+'}{'+s+'}}'
            else:
                return s

    def _wrap(s):
        if tex:
            return '\\ensuremath{' + s + '}'
        else:
            return s

    lines = []
    for ri, rxn in enumerate(rsys.rxns):
        rxn_ref = rxn.ref
        if unit_registry is not None:
            kunit = (get_derived_unit(unit_registry,
                                      'concentration')**(1-rxn.order()) /
                     get_derived_unit(unit_registry, 'time'))
            try:
                k = k_fmt.format(to_unitless(rxn.param, kunit))
                k_unit_str = (kunit.dimensionality.latex if tex
                              else kunit.dimensionality)
            except:
                k = rxn.param.equation_as_string(k_fmt, tex)
                k_unit_str = '-'
        else:
            k_unit_str = '-'
            k = k_fmt.format(rxn.param)
        r_str, ir_str, arrow_str, p_str, ip_str = rxn._get_str_parts(
            'latex_name' if tex else 'name',
            'latex_arrow' if tex else 'str_arrow',
            rsys.substances)
        lines.append(coldelim.join([
            str(rref0+ri),
            _wrap(r_str + ir_str),
            _wrap(arrow_str),
            _wrap(p_str + ip_str),
            _wrap(k),
            unit_fmt.format(k_unit_str),
            ref_fmt(rxn_ref) if callable(ref_fmt) else ref_fmt.format(rxn_ref)
        ]))

    return lines


def rsys2table(rsys, table_template=None,
               table_template_dict=None, **kwargs):
    r"""
    Renders user provided table_template with table_template_dict which
    also has 'body' entry generated from `rsys2tablines`.

    Defaults is LaTeX table requiring booktabs package to be used
    (add \usepackage{booktabs} to preamble).

    Parameters
    ==========
    rsys: ReactionSystem
    table_template: string
    table_tempalte_dict: dict used to render table_template (excl. "body")
    longtable: bool
        use longtable in defaults. (default: False)
    **kwargs:
        passed onto rsys2tablines
    """
    siunitx = kwargs.pop('siunitx', False)
    line_term = r' \\'
    defaults = {
        'table_env': 'longtable' if kwargs.pop(
            'longtable', False) else 'table',
        'alignment': 'llllSll' if siunitx else 'lllllll',
        'header': kwargs.get('coldelim', ' & ').join([
            'Id.', 'Reactants', '', 'Products', '{Rate constant}',
            'Unit', 'Ref'
        ]) + line_term,
        'short_cap': rsys.name,
        'long_cap': rsys.name,
        'label': (rsys.name or 'None').lower()
    }

    if table_template_dict is None:
        table_template_dict = defaults
    else:
        for k, v in defaults:
            if k not in table_template_dict:
                table_template_dict[k] = v

    if 'body' in table_template_dict:
        raise KeyError("There is already a 'body' key in table_template_dict")
    table_template_dict['body'] = (line_term + '\n').join(rsys2tablines(
        rsys, k_fmt=r'\num{{{0:.4g}}}' if siunitx else '{0:.4g}', **kwargs)) + line_term

    if table_template is None:
        if table_template_dict['table_env'] == 'longtable':
            table_template = tex_templates['table']['longtable']
        else:
            table_template = tex_templates['table']['default']

    return table_template % table_template_dict


def rsys2pdf_table(rsys, output_dir=None, doc_template=None,
                   doc_template_dict=None, save=True, landscape=False,
                   **kwargs):
    """
    Convenience function to render a ReactionSystem as
    e.g. a pdf using e.g. pdflatex.

    Parameters
    ==========
    rsys: ReactionSystem
    output_dir: path to output directory
        (default: system's temporary folder)
    doc_template: string
        LaTeX boiler plate temlpate including preamble,
        document environment etc.
    doc_template_dict: dict (string -> string)
        dict used to render temlpate (excl. 'table')
    longtable: bool
        use longtable in defaults. (default: False)
    **kwargs:
        passed on to `rsys2table`
    """
    if doc_template is None:
        doc_template = tex_templates['document']['default']
    _pkgs = [
        'booktabs', 'amsmath', ('pdftex,colorlinks,unicode=True',
                                'hyperref')] + (
                                    ['lscape'] if landscape else [])
    if kwargs.get('longtable', False):
        _pkgs += ['longtable']
    if kwargs.get('siunitx', False):
        _pkgs += ['siunitx']
    _envs = ['tiny'] + (['landscape'] if landscape else [])
    defaults = {
        'usepkg': '\n'.join([(r'\usepackage' + ('[%s]' if isinstance(pkg, tuple)
                                                else '') + '{%s}') % pkg for
                             pkg in _pkgs]),
        'begins': '\n'.join([r'\begin{%s}' % env for env in _envs]),
        'ends': '\n'.join([r'\end{%s}' % env for env in _envs[::-1]])
    }

    if doc_template_dict is None:
        doc_template_dict = defaults
    else:
        for k, v in defaults:
            if k not in doc_template_dict:
                doc_template_dict[k] = v

    if 'table' in doc_template_dict:
        raise KeyError("There is already a 'table' key in doc_template_dict")
    doc_template_dict['table'] = rsys2table(rsys, **kwargs)

    contents = doc_template % doc_template_dict

    texfname = 'output.tex'
    pdffname = 'output.pdf'
    return render_tex_to_pdf(contents, texfname, pdffname, output_dir, save)
