text: #
  # 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:
                  if value < 0:
                      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):
          if nsec < 0:
              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(object):
      """ """
  
      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
  