#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

from time import strftime, gmtime
from qpid_dispatch_internal.compat import UNICODE


def YN(val):
    if val:
        return 'Y'
    return 'N'


def Commas(value):
    sval = str(value)
    result = ""
    while True:
        if len(sval) == 0:
            return result
        left = sval[:-3]
        right = sval[-3:]
        result = right + result
        if len(left) > 0:
            result = ',' + result
        sval = left


def TimeLong(value):
    day = value // (24 * 3600)
    time = value % (24 * 3600)
    hour = time // 3600
    time %= 3600
    minutes = time // 60
    time %= 60
    seconds = time
    return "%03d:%02d:%02d:%02d" % (day, hour, minutes, seconds)


def TimeShort(value):
    return strftime("%X", gmtime(value / 1000000000))


def NumKMG(value, base=1000):
    """
    Format large numbers in a human readable summary
    """
    # IEEE 1541 numeric suffix definitions:
    SUFFIX = {1024: ('KiB', 'MiB', 'GiB', 'TiB', 'PiB'),
              1000: ('k', 'm', 'g', 't', 'p')}

    def _numCell(fp, suffix):
        # adjust the precision based on the size
        if fp < 10.0:
            return "%.2f %s" % (fp, suffix)
        if fp < 100.0:
            return "%.1f %s" % (fp, suffix)
        return "%.0f %s" % (fp, suffix)

    if value < base:
        return "%d" % value

    # round down to a power of base:
    sx = SUFFIX[base]
    for i in range(len(sx)):
        value /= float(base)
        if value < base:
            return _numCell(value, sx[i])
    return _numCell(value, sx[-1])


class Header:
    """ """
    NONE = 1
    KMG = 2    # 1000 based units
    YN = 3
    Y = 4
    TIME_LONG = 5
    TIME_SHORT = 6
    DURATION = 7
    COMMAS = 8
    # This is a plain number, no formatting
    PLAIN_NUM = 9
    KiMiGi = 10  # 1024 based units

    def __init__(self, text, format=NONE):
        self.text = text
        self.format = format

    def __repr__(self):
        return self.text

    def __str__(self):
        return self.text

    def formatted(self, value):
        try:
            if value is None:
                return ''
            if self.format == Header.NONE:
                return value
            if self.format == Header.PLAIN_NUM:
                return PlainNum(value)
            if self.format == Header.KMG:
                return NumKMG(value)
            if self.format == Header.KiMiGi:
                return NumKMG(value, base=1024)
            if self.format == Header.YN:
                if value:
                    return 'Y'
                return 'N'
            if self.format == Header.Y:
                if value:
                    return 'Y'
                return ''
            if self.format == Header.TIME_LONG:
                return TimeLong(value)
            if self.format == Header.TIME_SHORT:
                return TimeShort(value)
            if self.format == Header.DURATION:
                value = max(value, 0)
                sec = value / 1000000000
                min = sec / 60
                hour = min / 60
                day = hour / 24
                result = ""
                if day > 0:
                    result = "%dd " % day
                if hour > 0 or result != "":
                    result += "%dh " % (hour % 24)
                if min > 0 or result != "":
                    result += "%dm " % (min % 60)
                result += "%ds" % (sec % 60)
                return result
            if self.format == Header.COMMAS:
                return Commas(value)
        except:
            return "?"


def PlainNum(value):
    try:
        ret_val = "%d" % value
        return ret_val
    except:
        return "%s" % value


class BodyFormat:
    """
    Display body format chooses between:
     CLASSIC - original variable-width, unquoted, text delimited by white space
     CSV     - quoted text delimited by commas
    """
    CLASSIC = 1
    CSV = 2


class CSV_CONFIG:
    """ """
    SEPERATOR = ','
    STRING_QUOTE = '"'


class Display:
    """ Display formatting """

    def __init__(self, spacing=2, prefix="    ", bodyFormat=BodyFormat.CLASSIC):
        self.tableSpacing    = spacing
        self.tablePrefix     = prefix
        self.timestampFormat = "%X"
        if bodyFormat == BodyFormat.CLASSIC:
            self.printTable = self.table
        elif bodyFormat == BodyFormat.CSV:
            self.printTable = self.tableCsv
        else:
            raise Exception("Table body format must be CLASSIC or CSV.")

    def formattedTable(self, title, heads, rows):
        fRows = []
        for row in rows:
            fRow = []
            col = 0
            for cell in row:
                fRow.append(heads[col].formatted(cell))
                col += 1
            fRows.append(fRow)
        headtext = []
        for head in heads:
            headtext.append(head.text)
        self.printTable(title, headtext, fRows)

    def table(self, title, heads, rows):
        """ Print a table with autosized columns """

        # Pad the rows to the number of heads
        for row in rows:
            diff = len(heads) - len(row)
            for idx in range(diff):
                row.append("")

        print("%s" % title)
        if len(rows) == 0:
            return
        colWidth = []
        col      = 0
        line     = self.tablePrefix
        for head in heads:
            width = len(head)
            for row in rows:
                text = UNICODE(row[col])
                cellWidth = len(text)
                if cellWidth > width:
                    width = cellWidth
            colWidth.append(width + self.tableSpacing)
            line = line + head
            if col < len(heads) - 1:
                for i in range(colWidth[col] - len(head)):
                    line = line + " "
            col = col + 1
        print(line)
        line = self.tablePrefix
        for width in colWidth:
            for i in range(width):
                line = line + "="
        print(line)

        for row in rows:
            line = self.tablePrefix
            col  = 0
            for width in colWidth:
                text = UNICODE(row[col])
                line = line + text
                if col < len(heads) - 1:
                    for i in range(width - len(text)):
                        line = line + " "
                col = col + 1
            print(line)

    def tableCsv(self, title, heads, rows):
        """
        Print a table with CSV format.
        """

        def csvEscape(text):
            """
            Given a unicode text field, return the quoted CSV format for it
            :param text: a header field or a table row field
            :return:
            """
            if len(text) == 0:
                return ""
            else:
                text = text.replace(CSV_CONFIG.STRING_QUOTE, CSV_CONFIG.STRING_QUOTE * 2)
                return CSV_CONFIG.STRING_QUOTE + text + CSV_CONFIG.STRING_QUOTE

        print("%s" % title)
        if len(rows) == 0:
            return

        print(','.join([csvEscape(UNICODE(head)) for head in heads]))
        for row in rows:
            print(','.join([csvEscape(UNICODE(item)) for item in row]))

    def do_setTimeFormat(self, fmt):
        """ Select timestamp format """
        if fmt == "long":
            self.timestampFormat = "%c"
        elif fmt == "short":
            self.timestampFormat = "%X"

    def timestamp(self, nsec):
        """ Format a nanosecond-since-the-epoch timestamp for printing """
        return strftime(self.timestampFormat, gmtime(nsec / 1000000000))

    def duration(self, nsec):
        nsec = max(nsec, 0)
        sec = nsec / 1000000000
        min = sec / 60
        hour = min / 60
        day = hour / 24
        result = ""
        if day > 0:
            result = "%dd " % day
        if hour > 0 or result != "":
            result += "%dh " % (hour % 24)
        if min > 0 or result != "":
            result += "%dm " % (min % 60)
        result += "%ds" % (sec % 60)
        return result


class Sortable:
    """ """

    def __init__(self, row, sortIndex):
        self.row = row
        self.sortIndex = sortIndex
        if sortIndex >= len(row):
            raise Exception("sort index exceeds row boundary")

    def __lt__(self, other):
        return self.row[self.sortIndex] < other.row[self.sortIndex]

    def getRow(self):
        return self.row


class Sorter:
    """ """

    def __init__(self, heads, rows, sortCol, limit=0, inc=True):
        col = 0
        for head in heads:
            if head.text == sortCol:
                break
            col += 1
        if col == len(heads):
            raise Exception("sortCol '%s', not found in headers" % sortCol)

        list = []
        for row in rows:
            list.append(Sortable(row, col))
        list.sort()
        if not inc:
            list.reverse()
        count = 0
        self.sorted = []
        for row in list:
            self.sorted.append(row.getRow())
            count += 1
            if count == limit:
                break

    def getSorted(self):
        return self.sorted
