"""
Data, figures/tables IO handy utilities.
"""

import os as _os
import pandas as _pd
import tempfile as _tempfile
import subprocess as _subprocess
import zipfile as _zipfile
import logging as _logging
import shutil as _shutil

from io import StringIO as _StringIO
from io import BytesIO as _BytesIO

# from collections import OrderedDict as _OrderedDict
from pathlib import Path as _Path
from pylbmisc.dm import _default_dtype_backend
from pylbmisc.dm import fix_varnames as _fix_varnames
from pylbmisc.dm import is_bool as _is_bool
from pylbmisc.dm import is_categorical as _is_categorical
from pylbmisc.dm import is_date as _is_date
from pylbmisc.dm import is_datetime as _is_datetime
from pylbmisc.dm import is_integer as _is_integer
from pylbmisc.dm import is_numeric as _is_numeric
from pylbmisc.dm import is_string as _is_string
from pylbmisc.dm import is_all_missing as _is_all_missing
from pylbmisc.dm import to_categorical as _to_categorical
from pylbmisc.dm import to_date as _to_date
from typing import Sequence as _Sequence
from pylbmisc.r import match_arg as _match_arg

# ------------------------------------
# Figure and images stuff
# ------------------------------------
def export_figure(fig,
                  fdir: str = "outputs",
                  fname: str = "",
                  fext: str | list | set = ("eps", "png", "pdf"),
                  latex_include=True,
                  label: str = "",
                  caption: str = "",
                  scale: float = 1
                  ) -> None:
    """Dump a figure (save to file, include in LaTeX)

    Save to png, eps and pdf and include in Latex an image;
    intended to be used inside pythontex pycode.

    Parameters
    ----------
    fig: matplotlib.figure.Figure
        the fig
    fdir: str
        directory dove salvare, se mancante si usa /tmp
    fname: str
        basename del file da salvare, se mancante si prova a riusare
        label (se specificata) oppure a creare un file temporaneo
    fext: str, list or set
        list or set with format/extensions to be used for exporting
    latex_include: bool
        print command for latex includeing
    label: str
        posta dopo "fig:" in LaTeX
    caption: str
        caption LaTeX, se mancante viene riadattata label
    scale: float
        scale di includegraphics di LaTeX
    """
    # fdir not existing, using /tmp
    if not _os.path.isdir(fdir):
        fdir = "/tmp"

    if isinstance(fext, str):
        fext = [fext]

    # default filename to be set as label, if available or a temporary one
    if fname == "":
        if label != "":
            fname = label
        else:
            tmp = _tempfile.mkstemp(dir=fdir)
            fname = _os.path.basename(tmp[1])

    # produce the file paths
    base_path = _os.path.join(fdir, fname)
    eps_path = base_path + ".eps"
    png_path = base_path + ".png"
    pdf_path = base_path + ".pdf"

    # save figures to hard drive
    if "eps" in fext:
        fig.savefig(eps_path, transparent=True)
    if "png" in fext:
        fig.savefig(png_path, transparent=True)  # , dpi = 600)
    if "pdf" in fext:
        fig.savefig(pdf_path, transparent=True)

    # latex stuff
    if latex_include:
        latex_label = "fig:" + label
        caption = (
            label.capitalize().replace("_", " ") if caption == "" else caption
        )
        latex = (
            "\\begin{figure} \\centering "
            "\\includegraphics[scale=%(scale).2f]{%(base_path)s}"
            " \\caption{%(caption)s} \\label{%(label)s} \\end{figure}"
        )
        subs = {
            "scale": scale,
            "base_path": base_path,
            "caption": caption,
            "label": latex_label,
        }
        print(latex % subs)


# ------------------------------------
# Dataset Import/export routines
# ------------------------------------

def _make_rec_dict(f, t):
    rec_df = (
        _pd.DataFrame({"from": f, "to": t})
        .drop_duplicates()
        .dropna()
        .sort_values(by="from")
    )
    ft = dict()
    for row in rec_df.itertuples():
        ft[row[1]] = row[2]
    return ft


def _import_csv(path, csv_kwargs):
    """
    Import csv handling encryption for .csv.gpg files
    """
    fext = "".join(path.suffixes)
    if fext == ".csv.gpg":
        decrypt_cmd = f"gpg --decrypt {path}"
        decrypted_content = _subprocess.run(
            decrypt_cmd.split(" "),
            capture_output=True,
            text=True).stdout
        data_input = _StringIO(decrypted_content)
    else:
        data_input = path
    data = _pd.read_csv(data_input, **csv_kwargs)
    return data


def _import_excel(path, excel_kwargs):
    """
    Import csv handling encryption for .xls.gpg or .xlsx.gpg files
    """
    fext = "".join(path.suffixes)
    fname = path.name.replace(fext, "")
    if fext in {".xls.gpg", ".xlsx.gpg"}:
        decrypt_cmd = f"gpg --decrypt {path}"
        decrypted_content = _subprocess.run(
            decrypt_cmd.split(" "),
            capture_output=True).stdout
        data_input = _BytesIO(decrypted_content)
    else:
        data_input = path
    # import all the sheets as a dict of DataFrame
    sheets = _pd.read_excel(data_input, None, **excel_kwargs)
    # add xlsx name to sheets ones
    sheets = {f"{fname}_{k}": v for k, v in sheets.items()}
    return sheets


def import_redcap(
        data_fpath: str | _Path = "data/DATA.csv.gpg",
        labels_fpath: str | _Path = "data/LABELS.csv.gpg",
        csv_kwargs: dict = {"dtype_backend": _default_dtype_backend},
        verbose=False
) -> _pd.DataFrame:
    """
    Import dataset exported from redcap adding labels

    The idea is adding names as data['var'].name = labels['var'].column

    Parameters
    ----------
    data_path: str | Path
        csv (or csv.gpg) of raw data export
    labels_path: str | Path
        csv (or csv.gpg) of labelled data export

    Examples
    --------
    >>> df = import_redcap("data/DATA.csv.gpg", "data/LABELS.csv.gpg")
    """
    data = _import_csv(_Path(data_fpath), csv_kwargs)
    labels = _import_csv(_Path(labels_fpath), csv_kwargs)

    varnames = data.columns.to_list()
    comments = labels.columns.to_list()

    if (data.shape[1] != labels.shape[1]):
        msg = "I dataframe passati debbono avere lo stesso numero di colonne"
        raise Exception(msg)

    final_df = data.copy()
    description_dict = dict()

    # process column by column
    for ncol in range(data.shape[1]):
        varname = varnames[ncol]
        comment = comments[ncol]
        description_dict[varname] = comment
        if verbose:
            print(f"Doing {varname}")

        # do recoding if needed
        raw = data.iloc[:, ncol]    # raw variable (before recoding)
        rec = labels.iloc[:, ncol]  # recoded variable (after recoding)

        # factor: raw stringa/intero/numerico e ricodificato a stringa
        probable_factor = ((_is_integer(raw) or _is_numeric(raw))
                           and
                           _is_string(rec))
        probable_date = (_is_string(raw) and
                         raw.str.fullmatch(r"\d{4}-\d{2}-\d{2}").all())

        if probable_date:
            final_df[varname] = _to_date(raw)
        elif probable_factor:
            rec_dict = _make_rec_dict(raw, rec)
            final_df[varname] = _to_categorical(
                raw,
                levels=list(rec_dict.keys()),
                labels=list(rec_dict.values()))
        else:
            pass  # do nothing but in the previous cases

    return final_df, description_dict


def import_data(fpaths: str | _Path | _Sequence[str | _Path],
                csv_kwargs: dict = {"dtype_backend": _default_dtype_backend},
                excel_kwargs: dict = {"dtype_backend": _default_dtype_backend},
                rm_common_prefix: bool = True
                ) -> _pd.DataFrame | dict[str, _pd.DataFrame]:
    '''Import data

    Can be used to import data from one or several filepaths

    Parameters
    ----------
    fpaths: string, Path, or sequence of
        file paths (supported formats: .csv .xls .xlsx .zip .csv.gpg .xlsx.gpg)
    csv_kwargs: dict
        parameter passed to read_csv
    excel_kwargs: dict
        parameter passed to read_excel
    rm_common_prefix: bool
        if dataset share the same common prefix, remove it

    Returns
    -------
    A dict of DataFrame

    '''
    # uniform 1 to many and clean input
    if isinstance(fpaths, str) or isinstance(fpaths, _Path):
        fpaths = [fpaths]

    rval: dict[str, _pd.DataFrame] = {}

    for fpath in fpaths:
        # breakpoint()
        # normalize to Path
        fpath = _Path(fpath)
        if not fpath.exists():
            msg = f"{fpath} does not exists. Ignored"
            _logging.warning(msg)
            continue
        fext = "".join(fpath.suffixes)
        fname = fpath.name.replace(fext, "")
        if fext in {".csv", ".csv.gpg"}:
            data = _import_csv(fpath, csv_kwargs)
            dfname = fname
            if dfname not in rval.keys():  # check for duplicates
                rval[dfname] = data
            else:
                msg = f"{dfname} is duplicated, skipping to avoid overwriting"
                _logging.warning(msg)
        elif fext in {".xls", ".xlsx", ".xls.gpg", ".xlsx.gpg"}:
            excel_sheets = _import_excel(fpath, excel_kwargs)
            rval.update(excel_sheets)
        elif fext == ".zip":  # unzip in temdir and go by recursion
            with _tempfile.TemporaryDirectory() as tempdir:
                with _zipfile.ZipFile(fpath) as myzip:
                    myzip.extractall(tempdir)
                    zipped_fpaths = [
                        _os.path.join(tempdir, f) for f in _os.listdir(tempdir)
                    ]
                    zipped_data = import_data(zipped_fpaths)
            # prepend zip name to fname (as keys) and update results
            zipped_data = {f"{fname}_{k}": v for k, v in zipped_data.items()}
            rval.update(zipped_data)
        else:
            msg = f"File format not supported for {fpath}. Ignoring it."
            _logging.warning(msg)

    if len(rval) == 1:
        # a single dataset: KISS and return the data directly
        for v in rval.values():
            return v
    elif len(rval) > 1:
        # multiple dataset return the dict and remove common name prefix
        if rm_common_prefix:
            keys = list(rval.keys())
            common_prefix = _os.path.commonprefix(keys)
            rval = {k.removeprefix(common_prefix): rval[k]
                    for k in rval.keys()}
        return rval
    else:
        msg = "No data to be imported."
        _logging.error(msg)


# _rdf: pd.DataFrame to R data.frame converter (a-la dput)
# --------------------------------------------------------
def _rdf_missing(x: _pd.Series, xn: str):
    rval = f"{xn} = c(NA)"
    return rval


def _rdf_integer(x: _pd.Series, xn: str):
    data_str = x.to_string(
        na_rep="NA",
        index=False,
        header=False,
    ).replace("\n", ", ")
    rval = f"{xn} = c({data_str})"
    return rval


# placeholder
_rdf_numeric = _rdf_integer


def _rdf_factor(x: _pd.Series, xn: str):
    # to be safe it's seems to be better rather than
    # pd.Categorical
    x = _pd.Series(x, dtype="category")
    # raw data (integers)
    data = x.cat.codes
    data_str = "c({})".format(
        data.to_string(
            na_rep="NA",
            index=False,
            header=False,
        ).replace("\n", ", ")
    )
    # unique categories and labels
    categs = x.cat.categories
    levels = []
    labels = []
    for lev, lab in enumerate(categs):
        levels.append(str(lev))
        labels.append(f'"{lab}"')

    levs = ", ".join(levels)
    labs = ", ".join(labels)
    levels_str = f"levels = c({levs})"
    labels_str = f"labels = c({labs})"
    rval = f"{xn} = factor({data_str}, {levels_str}, {labels_str})"
    return rval


def _rdf_object(x: _pd.Series, xn: str):
    data_l = x.to_list()
    data_l2 = []
    for s in data_l:
        if isinstance(s, str) and s != "":
            data_l2.append(f'"{s}"')
        else:
            data_l2.append("NA")
    data_str = ", ".join(data_l2)
    rval = f"{xn} = c({data_str})"
    return rval


def _rdf_bool(x: _pd.Series, xn: str):
    ft = {True: "TRUE", False: "FALSE"}
    data_str = (
        x.map(ft).to_string(
            na_rep="NA",
            index=False,
            header=False,
        ).replace("\n", ", ")
    )
    rval = f"{xn} = c({data_str})"
    return rval


def _datetimemap(x):
    if _pd.isna(x):
        return "NA"
    else:
        return x.strftime("'%Y-%m-%d %H:%M:%S'")


def _rdf_datetime(x: _pd.Series, xn: str):
    recoded = x.map(_datetimemap)
    val = ", ".join(recoded)
    rval = f"{xn} = c({val})"
    return rval


def _datemap(x):
    if _pd.isna(x):
        return "NA"
    else:
        return x.strftime("'%Y-%m-%d'")


def _rdf_date(x: _pd.Series, xn: str):
    recoded = x.map(_datemap)
    val = ", ".join(recoded)
    rval = f"{xn} = c({val})"
    return rval


def _rdf_datetime_coercion_code(varlist, are_dates=True):
    v2 = [f"'{v}'" for v in varlist]
    v2 = ", ".join(v2)
    if are_dates:
        # dates code
        varlist = f"datevars <- c({v2});"
        importcmd = "df[, datevars] <- lapply(df[, datevars], as.Date);"
        cleaning = "rm(datevars);"
        return [varlist, importcmd, cleaning]
    else:
        # datetimes code
        varlist = f"dt_vars <- c({v2});"
        importfun = "imp_dt <- function(x) " \
            "as.POSIXct(x, format='%Y-%m-%d %H:%M:%S');"
        importcmd = "df[, dt_vars] <- lapply(df[, dt_vars], imp_dt);"
        cleaning = "rm(dt_vars, imp_dt);"
        return [varlist, importfun, importcmd, cleaning, ""]


def _rdf(df: _pd.DataFrame,
         path: str | _Path,
         dfname: str = "df"):
    """
    pd.DataFrame to R data.frame 'converter'
    """
    path = _Path(path)

    r_code = []
    date_vars = []
    datetime_vars = []
    r_code.append(f"{dfname} <- data.frame(")
    for var in df.columns:
        x = df[var]
        if _is_bool(x):
            r_code.append(_rdf_bool(x, var))
        elif _is_integer(x):
            r_code.append(_rdf_integer(x, var))
        elif _is_numeric(x):
            r_code.append(_rdf_numeric(x, var))
        elif _is_categorical(x):
            r_code.append(_rdf_factor(x, var))
        elif _is_date(x):
            r_code.append(_rdf_date(x, var))
            date_vars.append(var)
        elif _is_datetime(x):
            r_code.append(_rdf_datetime(x, var))
            datetime_vars.append(var)
        elif _is_string(x):
            r_code.append(_rdf_object(x, var))
        elif _is_all_missing(x):
            r_code.append(_rdf_missing(x, var))
        else:
            msg = f"{var}: il tipo {x.dtype!r} non è ancora gestito."
            raise ValueError(msg)
        is_last = var == df.columns[-1]
        if is_last:
            r_code.append(")\n")
        else:
            r_code.append(",\n")

    # fix some NA
    r_code = [line.replace("<NA>", "NA") for line in r_code]
    # add code for date/datetime import
    if date_vars:
        r_code += _rdf_datetime_coercion_code(date_vars, are_dates=True)
    if datetime_vars:
        r_code += _rdf_datetime_coercion_code(datetime_vars, are_dates=False)
    # finally export the surce code
    with path.open(mode="w") as f:
        f.writelines(r_code)


def _export_excel(x, xlsx_path, index):
    """
    Export excel file handling for encryption, set using .xslx.gpg in
    export_data

    """
    do_encryption = ".gpg" in xlsx_path.suffixes
    unencrypted_tempfname = _tempfile.mkstemp(suffix='.xlsx')[1]
    # export unencrypted x in temp file
    with _pd.ExcelWriter(unencrypted_tempfname) as writer:
        if isinstance(x, _pd.DataFrame):
            x.to_excel(writer,
                       sheet_name="Foglio 1",
                       index=index)
        elif isinstance(x, dict):
            for k, v in x.items():
                # preprocess dict's key to be accepted as excel sheetname
                k = _fix_varnames(k)[:31]
                v.to_excel(writer, sheet_name=k, index=index)
    # if encrypted do encryption
    if do_encryption:
        # just encrypt the file above in the target destination
        cmd = "gpg --encrypt --recipient lbraglia@gmail.com" \
            f" --output {xlsx_path} {unencrypted_tempfname}"
        _os.system(cmd)
    else:
        # just copy to destination
        _shutil.copy(unencrypted_tempfname, xlsx_path)
    # in any case drop the unencrypted version
    _Path(unencrypted_tempfname).unlink()


def export_data(x: _pd.DataFrame | dict[str, _pd.DataFrame],
                path: str | _Path,
                ext: str | list[str] = [".xlsx", ".csv",
                                        ".pkl", ".R", ".feather"],
                index=False,
                dfname="df"
                ) -> None:
    """Export a DataFrame or a dict of DataFrames as csv/xlsx

    In case of a dict is used and a csv path is given, the path is
    suffixed with dict names

    Parameters
    ----------
    x:
        dict of pandas.DataFrame
    fpath:
        file path, if extension is provided it will overwrite
        formats otherwise formats is considered
    ext:
        str or list of string with file extensions
    index:
        bool add index in exporting (typically True for results,
        False for data)
    verbose: bool
        for single variables exporting (R) say the varname which is processed
    """

    if not (isinstance(x, _pd.DataFrame) or isinstance(x, dict)):
        msg = "x deve essere un pd.DataFrame o un dict di pd.DataFrame"
        raise ValueError(msg)

    if isinstance(ext, str):  # uniforma
        ext = [ext]

    path = _Path(path)
    path_has_suffix = path.suffix != ""
    if path_has_suffix:
        # used_formats = [path.suffix.replace(".", "")]
        used_formats = ["".join(path.suffixes)]
    else:
        used_formats = ext

    # don't put ".xlsx" and ".xlsx.gpg" in the same branch otherwise only one
    # get exported
    if ".xlsx" in used_formats:
        xlsx_path = path if path_has_suffix else path.with_suffix(".xlsx")
        _export_excel(x=x, xlsx_path=xlsx_path, index=index)

    if ".xlsx.gpg" in used_formats:
        xlsx_path = path if path_has_suffix else path.with_suffix(".xlsx.gpg")
        _export_excel(x=x, xlsx_path=xlsx_path, index=index)

    if ".csv" in used_formats:
        if isinstance(x, _pd.DataFrame):
            x.to_csv(path if path_has_suffix else path.with_suffix(".csv"),
                     index=index)
        elif isinstance(x, dict):
            # use dict key as postfix
            for k, v in x.items():
                csv_path = path.parent / (str(path.stem) + f"_{k}.csv")
                v.to_csv(csv_path, index=index)

    if ".pkl" in used_formats:
        if isinstance(x, _pd.DataFrame):
            x.to_pickle(path if path_has_suffix else path.with_suffix(".pkl"))
        elif isinstance(x, dict):
            # use dict key as postfix
            for k, v in x.items():
                pkl_path = path.parent / (str(path.stem) + f"_{k}.pkl")
                v.to_pickle(pkl_path)

    if ".R" in used_formats:
        if isinstance(x, _pd.DataFrame):
            _rdf(x,
                 path if path_has_suffix else path.with_suffix(".R"),
                 dfname)
        elif isinstance(x, dict):
            # use dict key as postfix
            for k, v in x.items():
                r_path = path.parent / (str(path.stem) + f"_{k}.R")
                _rdf(v, r_path, dfname=k)

    if ".feather" in used_formats:
        if isinstance(x, _pd.DataFrame):
            x.to_feather(path if path_has_suffix
                         else path.with_suffix(".feather"))
        elif isinstance(x, dict):
            # use dict key as postfix
            for k, v in x.items():
                feather_path = path.parent / (str(path.stem) + f"_{k}.feather")
                v.to_feather(feather_path)


# ------------------------------------
# LaTeX stuff
# ------------------------------------
# Thanks PyLaTeX guys
_latex_special_chars = {
    "&": r"\&",
    "%": r"\%",
    "$": r"\$",
    "#": r"\#",
    "_": r"\_",
    "{": r"\{",
    "}": r"\}",
    "~": r"\textasciitilde{}",
    "^": r"\^{}",
    "\\": r"\textbackslash{}",
    "\n": "\\newline%\n",
    "-": r"{-}",
    "\xA0": "~",  # Non-breaking space
    "[": r"{[}",
    "]": r"{]}",
}


def _latex_escape(s):
    """
    latex_escape from PyLaTeX

    s: a str or something coercible to it
    >>> print(latex_escape("asd_foo_bar"))
    """
    string = str(s)
    return "".join(_latex_special_chars.get(c, c) for c in string)


def latex_table(
    tab,
    label: str = "",
    caption: str = "",
    position: str | None = None,
    float_format: str = "%.1f",
    column_format: str | None = None,
):
    """Print a LaTeX table

    Print a pd.DataFrame or inheriting object with to_latex method with
    sensible defaults.

    Parameters
    ----------
    tab:
        something with .to_latex method
    label: str
        posta dopo "fig:" in LaTeX
    caption: str
        caption tabella LaTeX
    position: str
        lettere posizionamento tabella (ad esempio
        https://stackoverflow.com/questions/1673942)
    """
    if (label == "") or (not isinstance(label, str)):
        msg = "Please provide a label for the table."
        raise ValueError(msg)
    caption = (
        label.capitalize().replace("_", " ") if caption == "" else caption
    )
    latex_caption = _latex_escape(caption)
    latex_label = "tab:" + label
    if isinstance(tab, _pd.DataFrame):
        tab.columns.name = None
    if (column_format is None) and isinstance(tab, _pd.DataFrame):
        ncols = tab.shape[1]
        column_format = "".join(["l"] + ["r"] * ncols)
    # per avere il centering è necessario impostare lo stile
    # https://pandas.pydata.org/docs/reference/api/pandas.io.formats.style.Styler.to_latex.html
    content = tab.to_latex(
        # fissi
        na_rep="",
        index=True,
        index_names=False,
        escape=True,
        # position_float='centering', # one day. maybe.
        # variabili
        label=latex_label,
        caption=latex_caption,
        position=position,
        float_format=float_format,
        column_format=column_format,
    )
    print(content)



def _md_to_str(x):
    # Funzione di utilita per convertire ciascuna cella di un dataframe ad una
    # stringa utile per la stampa di tabella markdown
    if _pd.isna(x) or x == "":
        return ""
    elif isinstance(x, float):
        return str(int(round(x, 0)))
    else:
        return str(x)



def md_table(
    tab,
    label: str = "",
    caption: str = "",
    position: str | None = None,
    float_format: str = "%.1f",
    column_format: str | None = None,
    **kwargs
):
    """Print a Markdown table (using pandas)

    Print a pd.DataFrame or inheriting object with to_latex method with
    sensible defaults.

    Parameters
    ----------
    tab:
        something with .to_markdown method
    label: str
        posta dopo "fig:" in LaTeX
    caption: str
        caption tabella LaTeX
    position: str
        lettere posizionamento tabella (ad esempio
        https://stackoverflow.com/questions/1673942)
    kwargs: dict
        altri parametri passati a .to_markdown()
    """
    # if (label == "") or (not isinstance(label, str)):
    #     msg = "Please provide a label for the table."
    #     raise ValueError(msg)
    # caption = (
    #     label.capitalize().replace("_", " ") if caption == "" else caption
    # )
    # latex_caption = _latex_escape(caption)
    # latex_label = "tab:" + label
    # if isinstance(tab, _pd.DataFrame):
    #     tab.columns.name = None
    # if (column_format is None) and isinstance(tab, _pd.DataFrame):
    #     ncols = tab.shape[1]
    #     column_format = "".join(["l"] + ["r"] * ncols)
    # # per avere il centering è necessario impostare lo stile
    # #
    # https://pandas.pydata.org/docs/reference/api/pandas.io.formats.style.Styler.to_latex.html
    content = tab.map(_md_to_str).to_markdown(
        # fissi
        tablefmt="pipe",
        # na_rep="",
        # index=True,
        # index_names=False,
        # escape=True,
        # # position_float='centering', # one day. maybe.
        # # variabili
        # label=latex_label,
        # caption=latex_caption,
        # position=position,
        # float_format=float_format,
        # column_format=column_format,
        **kwargs
    )
    print(content)


def export_tables(tabs_dict: dict[str, _pd.DataFrame],
                  outfile: str | _Path = "outputs/tables.xlsx"):
    """Latex print and excel Export a dict of tables (caption as key)

    Parameters
    ----------
    tabs_dict:
       dict of DataFrame to be exported
    outfile:
       path to the excel file for export

    Examples
    --------
    >>> exported = {"Descrittive": desc_df, "Analisi 1"  : analysis1_df,
    >>>             "Analisi 2"  : analysis2_df}
    >>> export_tables(exported)

    """
    # excel
    export_data(tabs_dict, outfile)
    # latex
    labels = []
    for caption, tab in tabs_dict.items():
        lab = _fix_varnames(caption)
        labels.append(lab)
        latex_table(tab, label=lab, caption=caption)
    # return latex references
    refs = ["\ref{tab:" + lab + "}" for lab in labels]
    return refs


# functions to hide certain section conditioned on a logical values
def hide_if(condition, markup="md"):
    markup = _match_arg(markup, ["tex", "md"])
    if condition:
        what = b"\\iffalse" if markup == 'tex' else "<!--"
        print(what)


def hide_fi(condition, markup="md"):
    markup = _match_arg(markup, ["tex", "md"])
    if condition:
        what = b"\\fi" if markup == 'tex' else "-->"
        print(what)
