"""LaTeX table formatter for dlist"""

from .table import TableFormatter
from ..helpers import LATEXcleanStr


class LaTeXFormatter(TableFormatter):
    r"""LaTeX tabular formatter

    Produces ``\begin{tabular}`` environments wrapped in ``\scalebox``
    and ``\begin{center}``.  Column headers are rendered as
    ``\textbf{...}``.  Special characters (``_ & % $ # { } ~``) are
    escaped automatically.

    Requires ``\usepackage[table]{xcolor}`` and ``\usepackage{graphicx}``
    in the document preamble (a commented-out preamble is included by
    default).

    Access via ``get_formatter('latex')`` or ``Dlist.write(format='latex')``.

    Parameters (passed as \*\*kwargs to ``format`` / ``format_categorized``)::

        keys      – list of column keys to display
        titles    – {key: display_name} for column headers;
                    for categories: {cat_key: format_string} e.g. 'Type: {}'
        width     – int: fixed column width (values truncated with …)
                    'auto': each column sized to its widest value
        maxwidth  – int: cap for auto-width (0 = no cap)
        nCols     – number of column groups side by side (default: 1)
        page      – rows per page; 0 = no pagination (default: 0)
        blank     – placeholder for missing values (default: '---')
        scale     – scalebox factor (default: 0.85)
        preamble  – include commented LaTeX preamble (default: True)

    **Basic table** (with bold column headers)::

        >>> fmt = get_formatter('latex')
        >>> print(fmt.format(d, keys=['id', 'name', 'type'], width=10))
        \begin{center}
        \scalebox{0.85}{
        \begin{tabular}{ | lll | }
        \hline
        \textbf{id} & \textbf{name} & \textbf{type} \\
        \hline
        E0001      & Sintel     & E          \\
        ...
        \hline
        \end{tabular}
        }
        \end{center}

    **Multi-column** (vertical rules between groups)::

        >>> print(fmt.format(d, keys=['id', 'name'], nCols=3, width=10))
        \begin{tabular}{ | ll | ll | ll | }
        ...

    **Categorized** (shaded ``\multicolumn`` headings)::

        Top-level categories use ``\cellcolor{gray!50}`` with a thick
        double ``\hline`` separator.  Subcategories use
        ``\cellcolor{gray!35}`` with a single ``\hline``.

        >>> print(fmt.format_categorized(d, ['type', 'dir'], ['id', 'name'],
        ...       titles={'type': 'Type: {}', 'dir': 'Dir: {}'}))
        \hline\hline
        \multicolumn{6}{|l|}{\cellcolor{gray!50} Type: A} \\
        \hline
        \multicolumn{6}{|l|}{\cellcolor{gray!35} Dir: /flaw/} \\
        \hline
        \textbf{ID} & \textbf{Name} & ... \\
        E0004      & test\_4   & ...       \\
        \hline
        \multicolumn{6}{|l|}{\cellcolor{gray!35} Dir: /test/} \\
        ...

    **Suppress preamble** (for embedding in existing documents)::

        >>> print(fmt.format(d, keys=['id'], width=8, preamble=False))

    **Via Dlist methods**::

        >>> d.write(format='latex', keys=['id', 'name'], width='auto')
        >>> d.write_categorized(['type'], ['id', 'name'], format='latex')
    """

    def _skin(self, **kwargs):
        return {
            'sep': ' & ',
            'sepCol': ' & ',
            'start': '',
            'lstart': None,
            'end': r' \\',
            'blank': '---',
            'width': 'auto',
            'nCols': 1,
            'page': 0,
            'maxwidth': 0,
            'scale': 0.85,
            'preamble': True,
        }

    def _clean(self, value):
        return LATEXcleanStr(value)

    def _tabular_spec(self, keys, fmt):
        """Build the tabular column spec string, e.g. '| ll | ll |'."""
        nCols = fmt['nCols']
        group = 'l' * len(keys)
        parts = [group] * nCols
        return '| ' + ' | '.join(parts) + ' |'

    def _n_total_cols(self, keys, fmt):
        """Total number of LaTeX columns (for multicolumn spans)."""
        return len(keys) * fmt['nCols']

    def _hline(self, keys, fmt, pos='mid'):
        if not keys:
            return ''
        # In categorized mode, _cat_prefix handles hlines
        if getattr(self, '_in_categorized', False):
            return ''
        return r'\hline'

    def _header(self, keys, titles, fmt):
        if not keys:
            return ''
        nCols = fmt['nCols']
        width = fmt['width']

        cw = fmt.get('_col_widths')
        if cw:
            cells = [f'{{:{cw[k]}s}}'.format(r'\textbf{' + titles.get(k, k) + '}')
                     for k in keys]
        else:
            w = width if isinstance(width, int) else 9
            fmtStr = f'{{:{w}s}}'
            cells = [fmtStr.format(r'\textbf{' + titles.get(k, k) + '}')
                     for k in keys]

        group = ' & '.join(cells)
        parts = [group] * nCols
        return ' & '.join(parts) + r' \\'

    def _wrap(self, body, keys, fmt):
        """Wrap body in tabular + center + scalebox environment."""
        if not keys:
            return body
        specs = self._tabular_spec(keys, fmt)
        scale = fmt.get('scale', 0.85)
        preamble = fmt.get('preamble', True)

        parts = []
        if preamble:
            parts.append(r'%\documentclass[12pt,draft]{article}')
            parts.append(r'%\usepackage[table]{xcolor}')
            parts.append(r'%\usepackage{graphicx}')
            parts.append('%')
            parts.append(r'%\begin{document}')
            parts.append('')

        parts.append(r'\begin{center}')
        parts.append(f'\\scalebox{{{scale}}}{{')
        parts.append(f'\\begin{{tabular}}{{ {specs} }}')
        parts.append(body)
        parts.append(r'\end{tabular}')
        parts.append('}')
        parts.append(r'\end{center}')

        if preamble:
            parts.append('')
            parts.append(r'%\end{document}')

        return '\n'.join(parts)

    # ── Categorized ──────────────────────────────────────────────────

    def _cat_skin(self, **kwargs):
        """Defaults for categorized output."""
        return {
            'nCols': 1,
            'sep': ' & ',
            'sepCol': ' & ',
            'start': '',
            'lstart': None,
            'end': r' \\',
            'blank': '---',
            'width': 'auto',
            'page': 0,
            'maxwidth': 0,
            'scale': 0.85,
            'preamble': True,
        }

    def _cat_prefix(self, label, level, depth, is_last, fmt, is_first=False):
        """Category heading as a multicolumn shaded row."""
        n_cols = self._n_total_cols(self._current_keys, fmt)
        shade = 50 - level * 15
        if level == 0:
            prefix = '\\hline\\hline\n'
        elif is_first:
            # First subcategory right after parent — parent already has \hline
            prefix = ''
        else:
            prefix = '\\hline\n'
        return (
            f'{prefix}'
            f'\\multicolumn{{{n_cols}}}{{|l|}}'
            f'{{\\cellcolor{{gray!{shade}}} {label}}} \\\\\n'
            f'\\hline'
        )

    def _cat_indent(self, level, total_depth, is_last, fmt):
        # No indent in LaTeX — rows are flat inside tabular
        return ''

    def format_categorized(self, dlist, ctree, keys, titles={}, **kwargs):
        # Store keys for _cat_prefix to compute n_total_cols
        self._current_keys = keys
        self._in_categorized = True

        merged = self._cat_skin(**kwargs)
        merged.update({k: v for k, v in kwargs.items() if v is not None})
        result = super().format_categorized(dlist, ctree, keys, titles=titles,
                                            **merged)
        self._in_categorized = False
        return result

    def _wrap_categorized(self, body, keys, fmt):
        """Wrap categorized body in tabular environment."""
        specs = self._tabular_spec(keys, fmt)
        scale = fmt.get('scale', 0.85)
        preamble = fmt.get('preamble', True)

        parts = []
        if preamble:
            parts.append(r'%\documentclass[12pt,draft]{article}')
            parts.append(r'%\usepackage[table]{xcolor}')
            parts.append(r'%\usepackage{graphicx}')
            parts.append('%')
            parts.append(r'%\begin{document}')
            parts.append('')

        parts.append(r'\begin{center}')
        parts.append(f'\\scalebox{{{scale}}}{{')
        parts.append(f'\\begin{{tabular}}{{ {specs} }}')
        parts.append(body)
        parts.append(r'\hline')
        parts.append(r'\end{tabular}')
        parts.append('}')
        parts.append(r'\end{center}')

        if preamble:
            parts.append('')
            parts.append(r'%\end{document}')

        return '\n'.join(parts)
