import curses
import enum



@enum.unique
class RenditionAttribute(enum.IntEnum):
  altcharset =  curses.A_ALTCHARSET
  blink =       curses.A_BLINK
  bold =        curses.A_BOLD
  dim =         curses.A_DIM
  normal =      curses.A_NORMAL
  reverse =     curses.A_REVERSE
  standout =    curses.A_STANDOUT
  underline =   curses.A_UNDERLINE


class ColorAttribute:
  @classmethod
  def initialize(cls):
    if not curses.has_colors(): return

    cls.__rgb2attr = {}
    for color_index in range(curses.COLORS):
      pair_index = color_index + 1
      if pair_index >= curses.COLOR_PAIRS: break
      curses.init_pair(pair_index, color_index, -1)
      rgb = tuple(color * 255 // 1000
                  for color in curses.color_content(color_index))
      cls.__rgb2attr[rgb] = curses.color_pair(pair_index)

  @classmethod
  def get_best_match(cls, rgb):
    assert len(rgb) == 3
    assert all(0 <= color < 256 for color in rgb)

    if not curses.has_colors():
      return curses.A_NORMAL

    if rgb in cls.__rgb2attr:
      return cls.__rgb2attr[rgb]

    attr = cls.__find_best_match(rgb)
    cls.__rgb2attr[rgb] = attr
    return attr

  @classmethod
  def __find_best_match(cls, new_rgb):
    matched_attr = curses.color_pair(0) # white on black
    min_distance = cls.__color_distance((0, 0, 0), (255, 255, 255))
    for rgb, attr in cls.__rgb2attr.items():
      distance = cls.__color_distance(new_rgb, rgb)
      if distance < min_distance:
        min_distance = distance
        matched_attr = attr
    return matched_attr

  @staticmethod
  def __color_distance(rgb1, rgb2):
    return sum((color1 - color2) ** 2 for color1, color2 in zip(rgb1, rgb2))

@@ -359,16 +359,51 @@
 ribute:%0A
+  __colors = %5B%5D%0A  __rgb2attr = %7B%7D%0A%0A
   @class
@@ -476,32 +476,8 @@
 rn%0A%0A
-    cls.__rgb2attr = %7B%7D%0A
     
@@ -769,31 +769,35 @@
   cls.__
-rgb2attr%5Brgb%5D =
+colors.append((rgb,
  curses.
@@ -818,16 +818,42 @@
 r_index)
+))%0A    cls.__colors.sort()
 %0A%0A  @cla
@@ -908,67 +908,27 @@
 ert 
-len(rgb) == 3%0A    assert all(0 %3C= color %3C 256 for color in 
+cls.__is_valid_rgb(
 rgb)
@@ -1191,24 +1191,63 @@
 , new_rgb):%0A
+    assert cls.__is_valid_rgb(new_rgb)%0A
     matched_
@@ -1390,24 +1390,14 @@
 s.__
-rgb2attr.items()
+colors
 :%0A  
@@ -1572,14 +1572,13 @@
 %0A  @
-static
+class
 meth
@@ -1603,16 +1603,21 @@
 istance(
+cls, 
 rgb1, rg
@@ -1621,16 +1621,81 @@
  rgb2):%0A
+    assert cls.__is_valid_rgb(rgb1) and cls.__is_valid_rgb(rgb2)%0A
     retu
@@ -1763,8 +1763,180 @@
  rgb2))%0A
+%0A  @staticmethod%0A  def __is_valid_rgb(rgb):%0A    return len(rgb) == 3 and all(isinstance(color, int) and 0 %3C= color %3C 256%0A                                 for color in rgb)%0A

Fix randomness of text colors