from . import rich


def print_long(lines):
    r"""
    Print full data without much formatting. The output looks as follows:

    .. code-block:: none

      +------------------+
      | line 1, column 1 |
      | line 1, column 2 |
      | ...              |
      +-----------------+
      | line 2, column 1 |
      | line 2, column 2 |
      | ...              |
      +------------------+

    :arguments:

      **lines** (``[ {'JOBID': '1234', ...}, ...]``)
        List of lines, with each line stored as a dictionary.
        Note that all data has to be stored as one as string,
        or as one the GooseSLURM.rich classes (no rich printing is used though).
    """

    # width of the field-names
    # - initialize
    width = 0
    # - compute
    for line in lines:
        for key in sorted(line):
            width = max(width, len(key))

    # print format
    head = "{{key:<{width:d}.{width:d}s}}".format(width=width + 1)
    fmt = "{{key:<{width:d}.{width:d}s}}: {{data:s}}".format(width=width + 1)

    # print header
    print(head.format(key="-" * 100, data=""))

    # print data
    for line in lines:
        for key in sorted(line):
            print(fmt.format(key=key, data=str(line[key])))
        print(head.format(key="-" * 100, data=""))


def print_columns(
    lines, columns, header, no_truncate=False, sep=", ", cols=None, print_header=True
):
    r"""
    Print table to fit the screen. This function can show data truncated, or even suppress columns
    if there is insufficient room.

    :arguments:

      **lines** (``[ {'JOBID': '1234', ...}, ...]``)
        List of lines, with each line stored as a dictionary.
        Note that all data has to be stored as one of the GooseSLURM.rich classes
        (to customize the color, precision, ...) or as string.

      **columns** (``[ {'key': 'JOBID', 'width': 7, 'align': '>', 'priority': True}, ...]``)
        List with print settings of each column:
        - 'key'     : the key-name used to store each line (see ``lines`` below)
        - 'width'   : minimum print width (expanded as much as possible to fit the data)
        - 'align'   : alignment of the column
        - 'priority': priority of column expansion, columns marked ``True`` are expanded first

      **header** (``{'JOBID': 'JobID', ...}``)
        Header name for each column.

    :options:

      **no_truncate** ([``False``] | ``True``)
        Disable truncation of columns. In this case each column is expanded to fit the data.

      **sep** ([``', '``] | ``<str>``)
        Separator between columns.

      **cols** ([``None``] | ``<int>``)
        Number of characters on one line. If ``None`` the current terminal's width is used.

      **print_header** ([``True``] | ``False``)
        Optionally skip printing of header.
    """

    # check available data
    # --------------------

    # function to check if a "key" present in all lines
    def inlines(lines, key):
        # - check all lines
        for line in lines:
            if key not in line:
                return False
        # - all lines passed: return True
        return True

    # select columns based on data availability
    columns = [column for column in columns if inlines(lines, column["key"])]

    # select header based on columns
    header = {column["key"]: header[column["key"]] for column in columns}

    # convert to GooseSLURM.rich
    # --------------------------

    for line in lines:
        for key in line:
            if not isinstance(line[key], rich.String):
                line[key] = rich.String(line[key])

    for key in header:
        if not isinstance(header[key], rich.String):
            header[key] = rich.String(header[key])

    # compute column-width, based on data
    # -----------------------------------

    # initialize
    for column in columns:
        column["real"] = 0

    # get actual width
    # - header
    for column in columns:
        column["real"] = max(column["real"], len(str(header[column["key"]])))
    # - data
    for line in lines:
        for column in columns:
            column["real"] = max(column["real"], len(str(line[column["key"]])))

    # auto-limit columns, auto-adjust their width
    # -------------------------------------------

    if no_truncate:

        # set all columns to the real width
        for line in lines:
            for column in columns:
                column["width"] = column["real"]

    else:

        # get the terminal size
        if cols is None:
            import shutil

            cols, _ = shutil.get_terminal_size()

        # get the cumulative minimum size of the columns (+ spacing between the columns)
        # - first entry
        columns[0]["total"] = columns[0]["width"]
        # - all other entries
        for i in range(1, len(columns)):
            columns[i]["total"] = columns[i - 1]["total"] + columns[i]["width"] + len(sep)

        # truncate at terminal size
        columns = [column for column in columns if column["total"] <= cols]

        # get the available size to expand
        room = cols - columns[-1]["total"]

        # expand minimum width, as long there is room
        # - distinguish priorities for expanding
        low = [column["key"] for column in columns if not column.get("priority", False)]
        high = [column["key"] for column in columns if column["key"] not in low]
        # - expand
        for prio in [high, low]:
            for column in columns:
                if column["key"] not in prio:
                    continue
                if room <= 0:
                    break
                dw = min(column["real"] - column["width"], room)
                if dw <= 0:
                    continue
                column["width"] += dw
                room -= dw

    # apply width
    for line in lines:
        for column in columns:
            line[column["key"]].width = column["width"]
            line[column["key"]].align = column["align"]

    # print to screen
    # ---------------

    # select header based on columns
    header = {column["key"]: header[column["key"]] for column in columns}

    # apply width to header
    for column in columns:
        header[column["key"]].width = column["width"]

    # separator
    # - copy from header
    hline = {key: rich.String(**value.__dict__) for key, value in header.items()}
    # - convert from header (print format retained)
    for key in hline:
        hline[key].data = "=" * hline[key].width

    # header
    if print_header:
        print(sep.join(hline[column["key"]].format() for column in columns))
        print(sep.join(header[column["key"]].format() for column in columns))
        print(sep.join(hline[column["key"]].format() for column in columns))
    # data
    for line in lines:
        print(sep.join(line[column["key"]].format() for column in columns))


def print_list(lines, key, sep=" "):
    r"""
    Print a single column as a list.

    :arguments:

      **lines** (``[ {'JOBID': '1234', ...}, ...]``)
        List of lines, with each line stored as a dictionary.
        Note that all data has to be stored as one as string,
        or as one the GooseSLURM.rich classes (no rich printing is used though).

      **key** (``'JOBID'``)
        Column to print.

    :options:

      **sep** ([``' '``] | ``<str>``)
        Separator between columns.
    """

    for line in lines:
        print(line[key], end=sep)

    print("")
