"""Simplified python code documentation."""
import warnings
from deprecation import deprecated as _deprecated
[docs]def append(txt):
    """
    Append ``txt`` in the ``target`` function docstring.
    Examples
    --------
    >>> @append('hi')
    ... def ho(): pass
    >>> ho.__doc__
    'hi'
    >>> @append(ho)
    ... def hi(): pass
    >>> hi.__doc__
    'hi'
    """
    if isinstance(txt, str):
        return append_str(txt)
    elif hasattr(txt, "__doc__"):
        return append_doc(txt)
    else:
        warnings.warn("Can't append docstring from type {}".format(type(txt)))
    return None 
[docs]def append_str(txt):
    """
    Append ``txt`` in the ``target`` function docstring.
    Parameters
    ----------
    txt : ``str``
        ``txt`` is inserted to the ``__doc__`` of the ``target``
    Examples
    --------
    >>> def ho():
    ...     '''Ho'''
    ...     print(ho.__doc__)
    >>> @append_doc(ho)
    ... def hi():
    ...     '''Hi'''
    ...     print(hi.__doc__)
    >>> hi()
    HiHo
    """
    def wrapper(target):
        if target.__doc__ is None:
            target.__doc__ = ""
        target.__doc__ += txt
        return target
    return wrapper 
[docs]def append_doc(original):
    """
    Append doc string of ``original`` to ``target`` object.
    Parameters
    ----------
    original : ``class`` or ``function``
        ``orignal.__doc__`` is appended to the ``__doc__`` of the ``target``
    Examples
    --------
    >>> def ho():
    ...     '''Ho'''
    ...     print(ho.__doc__)
    >>> @append_doc(ho)
    ... def hi():
    ...     '''Hi'''
    ...     print(hi.__doc__)
    >>> hi()
    HiHo
    """
    return append_str(original.__doc__ if original.__doc__ is not None else "") 
[docs]def insert(txt):
    """
    Insert ``txt`` in the ``target`` function docstring.
    Examples
    --------
    >>> @insert('hi')
    ... def ho(): pass
    >>> ho.__doc__
    'hi'
    >>> @insert(ho)
    ... def hi(): pass
    >>> hi.__doc__
    'hi'
    """
    if isinstance(txt, str):
        return insert_str(txt)
    elif hasattr(txt, "__doc__"):
        return insert_doc(txt)
    else:
        warnings.warn("Can't append docstring from type {}".format(type(txt)))
    return None 
[docs]def insert_str(txt):
    """
    Insert ``txt`` in the ``target`` function docstring.
    Parameters
    ----------
    txt : ``str``
        ``txt`` is inserted to the ``__doc__`` of the ``target``
    Examples
    --------
    >>> def ho():
    ...     '''Ho'''
    ...     print(ho.__doc__)
    >>> @insert_doc(ho)
    ... def hi():
    ...     '''Hi'''
    ...     print(hi.__doc__)
    >>> hi()
    HoHi
    """
    def wrapper(target):
        if target.__doc__ is None:
            target.__doc__ = ""
        target.__doc__ = txt + target.__doc__
        return target
    return wrapper 
[docs]def insert_doc(original):
    """
    Inserts the docstring from passed function ``original`` in the ``target`` function docstring.
    Parameters
    ----------
    original : ``class`` or ``function``
        ``orignal.__doc__`` is inserted to the ``__doc__`` of the ``target``
    Examples
    --------
    >>> def ho():
    ...     '''Ho'''
    ...     print(ho.__doc__)
    >>> @insert_doc(ho)
    ... def hi():
    ...     '''Hi'''
    ...     print(hi.__doc__)
    >>> hi()
    HoHi
    """
    return insert_str(original.__doc__) 
[docs]def deprecated(
    version=None,
    deprecated_in=None,
    removed_in=None,
    reason=None,
    details=None,
):
    """
    Decorator to mark a function as deprecated.
    Parameters
    ----------
    version : ``str``
        Version of the package when the function was deprecated.
    deprecated_in : ``str``
        Version of the package when the function was deprecated.
    removed_in : ``str``
        Version of the package when the function will be removed.
    reason : ``str``
        Reason for deprecation.
    details : ``str``
        Details about the deprecation.
    Examples
    --------
    >>> @deprecated('0.0.0',removed_in='0.2.0')
    ... def ho():
    ...     '''Ho'''
    ...     print(ho.__doc__)
    >>> ho()
    Ho
    <BLANKLINE>
    .. deprecated:: 0.0.0
       This will be removed in 0.2.0.
    """
    # merge details and reason
    if details is None:
        details = reason
    elif reason is not None:
        details = reason + " " + details
    # merge deprecated_in and version
    if version is None:
        version = deprecated_in
    # increment minor version
    # if removed_in is None:
    #    removed_in = ".".join(
    #        [version.split(".")[0]] + [str(int(version.split(".")[1]) + 2)] + ["0"]
    #    )
    return _deprecated(
        deprecated_in=version,
        removed_in=removed_in,
        # current_version=_version("pyfeyn2"),
        details=details,
    ) 
tab_len = 20
[docs]def table_sep(tabs=1):
    return (
        "=" * (tab_len - 2)
        + "  "
        + "=" * (tab_len - 2)
        + "  "
        + "=" * (tab_len - 2)
        + "\n"
        + "\t".join(["" for i in range(0, tabs + 1)])
    ) 
[docs]@deprecated(
    version="1.0.3.1",
    removed_in="?.?.?",
    reason="Use :func:`smpl_doc.array_table` instead.",
)
def table(dic, top=True, bottom=True, init=True, tabs=1):
    """
    Add dict= {'key': [values...]} to a simple reST table.
    Parameters
    ----------
    dic : ``dict``
        Dictionary to be converted to a table.
    top : ``bool``
        If ``True`` a top line is added.
    bottom : ``bool``
        If ``True`` a bottom line is added.
    init : ``bool``
        If ``True`` a tab is added at the beginning of the line.
    tabs : ``int``
        Number of tabs to be added at the beginning of the line.
    Returns
    -------
    ``str``
        The table as a string.
    Examples
    --------
    >>> table({'a': [1, 2, 3], 'b': [4, 5, 6]})
    '\\t==================  ==================  ==================\\n\\ta                   1                   2                   3\\n\\tb                   4                   5                   6\\n\\t==================  ==================  ==================\\n\\t\\n'
    """
    t = "\t" * tabs
    rs = ""
    if init:
        rs += t
    if top:
        rs += table_sep(tabs=tabs)
    for k, vs in dic.items():
        rs += str(k)
        p = str(k)
        for v in vs:
            sv = str(v)
            if "<function " in sv and "at 0x" in sv:
                sv = sv.split("<function ")[1].split("at 0x")[0]
            rs += "".join([" " for i in range(tab_len - len(p))]) + sv
            p = sv
        rs += "\n" + t
    if bottom:
        rs += table_sep(tabs=tabs) + "\n"
    else:
        rs += ""
    return rs 
[docs]def dict_to_table(dic):
    rt = []
    for k, vs in dic.items():
        rt += [[k, *vs]]
    return rt 
[docs]def trim_eol_spaces(s):
    """
    Trim spaces at the end of lines.
    This is only needed for the docstrings, because black does trim them.
    """
    return "\n".join([l.rstrip() for l in s.split("\n")]) 
[docs]def array_table(arr, top=True, bottom=True, init=True, tabs=1, header=True):
    """
    Produces a reST table from a numpy array or normal 2d array.
    Parameters
    ----------
    arr : ``numpy.ndarray``, ``list`` or ``dict``
        2d array or dict
    top : ``bool``
        If ``True`` a top line is added.
    bottom : ``bool``
        If ``True`` a bottom line is added.
    init : ``bool``
        If ``True`` a tab is added at the beginning of each line.
    tabs : ``int``
        Number of tabs at the beginning of each line.
    header : ``bool``
        If ``True`` the first row is used as header.
    Examples
    --------
    >>> print(trim_eol_spaces(array_table([["a","b"],["hihi", "hoho"]],tabs=0)))
    ====== ======
    a      b
    ====== ======
    hihi   hoho
    ====== ======
    <BLANKLINE>
    >>> print(trim_eol_spaces(array_table({"a":["hihi",2],"b": ["hoho",3]},tabs=0,header=False)))
    === ====== ===
    a   hihi   2
    b   hoho   3
    === ====== ===
    <BLANKLINE>
    """
    if isinstance(arr, dict):
        arr = dict_to_table(arr)
    width = len(arr[0])
    height = len(arr)
    widths = [0 for i in range(width)]
    # maximum width of each column
    for i in range(0, width):
        for j in range(0, height):
            if len(str(arr[j][i])) > widths[i]:
                widths[i] = len(str(arr[j][i]))
    rs = ""
    if init:
        rs += "\t" * tabs
    if top:
        rs += " ".join(["=" * (widths[i] + 2) for i in range(0, width)]) + "\n"
    for i in range(0, height):
        rs += "\t" * tabs
        for j in range(0, width):
            rs += str(arr[i][j]) + " " * (widths[j] - len(str(arr[i][j])) + 3)
        rs += "\n"
        if header and i == 0:
            rs += (
                "\t" * tabs
                + " ".join(["=" * (widths[i] + 2) for i in range(0, width)])
                + "\n"
            )
    if bottom:
        rs += "\t" * tabs
        rs += " ".join(["=" * (widths[i] + 2) for i in range(0, width)]) + "\n"
    return rs 
if __name__ == "__main__":
    import doctest
    doctest.testmod()