from enum import Enum, auto
from typing import Optional

from . import Endianness


class Color(Enum):
    # from https://en.m.wikipedia.org/wiki/ANSI_escape_code#Colors:
    #   0-7    standard colors
    #   8-15   high intensity colors
    #  16-231  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
    # 232-255  grayscale from dark to light in 24 steps

    # 3-bit colors / 4-bit colors (first half)
    BLACK   = COLOR0 = 0
    RED     = COLOR1 = auto()
    GREEN   = COLOR2 = auto()
    YELLOW  = COLOR3 = auto()
    BLUE    = COLOR4 = auto()
    MAGENTA = COLOR5 = auto()
    CYAN    = COLOR6 = auto()
    WHITE   = COLOR7 = auto()

    # 4-bit colors (second half)
    BRIGHT_BLACK   = COLOR8 = GRAY = auto()
    BRIGHT_RED     = COLOR9        = auto()
    BRIGHT_GREEN   = COLOR10       = auto()
    BRIGHT_YELLOW  = COLOR11       = auto()
    BRIGHT_BLUE    = COLOR12       = auto()
    BRIGHT_MAGENTA = COLOR13       = auto()
    BRIGHT_CYAN    = COLOR14       = auto()
    BRIGHT_WHITE   = COLOR15       = auto()

    # 8-bit colors
    #  As an aside: regarding the alternative options (to hardencoding 200+ values),
    # all of them were considered not worth it.
    #  This solution is simple (even too much), efficient (no loops whatsoever),
    # backward-compatible (this class was already an Enum).

    COLOR16 = auto()  # also "Black"

    COLOR17 = auto()
    COLOR18 = auto()
    COLOR19 = auto()
    COLOR20 = auto()
    COLOR21 = auto()  # pure blue
    COLOR22 = auto()
    COLOR23 = auto()
    COLOR24 = auto()
    COLOR25 = auto()
    COLOR26 = auto()
    COLOR27 = auto()
    COLOR28 = auto()
    COLOR29 = auto()
    COLOR30 = auto()
    COLOR31 = auto()
    COLOR32 = auto()
    COLOR33 = auto()
    COLOR34 = auto()
    COLOR35 = auto()
    COLOR36 = auto()
    COLOR37 = auto()
    COLOR38 = auto()
    COLOR39 = auto()
    COLOR40 = auto()
    COLOR41 = auto()
    COLOR42 = auto()
    COLOR43 = auto()
    COLOR44 = auto()
    COLOR45 = auto()
    COLOR46 = auto()  # pure green
    COLOR47 = auto()
    COLOR48 = auto()
    COLOR49 = auto()
    COLOR50 = auto()
    COLOR51 = auto()
    COLOR52 = auto()
    COLOR53 = auto()
    COLOR54 = auto()
    COLOR55 = auto()
    COLOR56 = auto()
    COLOR57 = auto()
    COLOR58 = auto()
    COLOR59 = auto()
    COLOR60 = auto()
    COLOR61 = auto()
    COLOR62 = auto()
    COLOR63 = auto()
    COLOR64 = auto()
    COLOR65 = auto()
    COLOR66 = auto()
    COLOR67 = auto()
    COLOR68 = auto()
    COLOR69 = auto()
    COLOR70 = auto()
    COLOR71 = auto()
    COLOR72 = auto()
    COLOR73 = auto()
    COLOR74 = auto()
    COLOR75 = auto()
    COLOR76 = auto()
    COLOR77 = auto()
    COLOR78 = auto()
    COLOR79 = auto()
    COLOR80 = auto()
    COLOR81 = auto()
    COLOR82 = auto()
    COLOR83 = auto()
    COLOR84 = auto()
    COLOR85 = auto()
    COLOR86 = auto()
    COLOR87 = auto()
    COLOR88 = auto()
    COLOR89 = auto()
    COLOR90 = auto()
    COLOR91 = auto()
    COLOR92 = auto()
    COLOR93 = auto()
    COLOR94 = auto()
    COLOR95 = auto()
    COLOR96 = auto()
    COLOR97 = auto()
    COLOR98 = auto()
    COLOR99 = auto()
    COLOR100 = auto()
    COLOR101 = auto()
    COLOR102 = auto()
    COLOR103 = auto()
    COLOR104 = auto()
    COLOR105 = auto()
    COLOR106 = auto()
    COLOR107 = auto()
    COLOR108 = auto()
    COLOR109 = auto()
    COLOR110 = auto()
    COLOR111 = auto()
    COLOR112 = auto()
    COLOR113 = auto()
    COLOR114 = auto()
    COLOR115 = auto()
    COLOR116 = auto()
    COLOR117 = auto()
    COLOR118 = auto()
    COLOR119 = auto()
    COLOR120 = auto()
    COLOR121 = auto()
    COLOR122 = auto()
    COLOR123 = auto()
    COLOR124 = auto()
    COLOR125 = auto()
    COLOR126 = auto()
    COLOR127 = auto()
    COLOR128 = auto()
    COLOR129 = auto()
    COLOR130 = auto()
    COLOR131 = auto()
    COLOR132 = auto()
    COLOR133 = auto()
    COLOR134 = auto()
    COLOR135 = auto()
    COLOR136 = auto()
    COLOR137 = auto()
    COLOR138 = auto()
    COLOR139 = auto()
    COLOR140 = auto()
    COLOR141 = auto()
    COLOR142 = auto()
    COLOR143 = auto()
    COLOR144 = auto()
    COLOR145 = auto()
    COLOR146 = auto()
    COLOR147 = auto()
    COLOR148 = auto()
    COLOR149 = auto()
    COLOR150 = auto()
    COLOR151 = auto()
    COLOR152 = auto()
    COLOR153 = auto()
    COLOR154 = auto()
    COLOR155 = auto()
    COLOR156 = auto()
    COLOR157 = auto()
    COLOR158 = auto()
    COLOR159 = auto()
    COLOR160 = auto()
    COLOR161 = auto()
    COLOR162 = auto()
    COLOR163 = auto()
    COLOR164 = auto()
    COLOR165 = auto()
    COLOR166 = auto()
    COLOR167 = auto()
    COLOR168 = auto()
    COLOR169 = auto()
    COLOR170 = auto()
    COLOR171 = auto()
    COLOR172 = auto()
    COLOR173 = auto()
    COLOR174 = auto()
    COLOR175 = auto()
    COLOR176 = auto()
    COLOR177 = auto()
    COLOR178 = auto()
    COLOR179 = auto()
    COLOR180 = auto()
    COLOR181 = auto()
    COLOR182 = auto()
    COLOR183 = auto()
    COLOR184 = auto()
    COLOR185 = auto()
    COLOR186 = auto()
    COLOR187 = auto()
    COLOR188 = auto()
    COLOR189 = auto()
    COLOR190 = auto()
    COLOR191 = auto()
    COLOR192 = auto()
    COLOR193 = auto()
    COLOR194 = auto()
    COLOR195 = auto()
    COLOR196 = auto()  # pure red
    COLOR197 = auto()
    COLOR198 = auto()
    COLOR199 = auto()
    COLOR200 = auto()
    COLOR201 = auto()
    COLOR202 = auto()
    COLOR203 = auto()
    COLOR204 = auto()
    COLOR205 = auto()
    COLOR206 = auto()
    COLOR207 = auto()
    COLOR208 = auto()
    COLOR209 = auto()
    COLOR210 = auto()
    COLOR211 = auto()
    COLOR212 = auto()
    COLOR213 = auto()
    COLOR214 = auto()
    COLOR215 = auto()
    COLOR216 = auto()
    COLOR217 = auto()
    COLOR218 = auto()
    COLOR219 = auto()
    COLOR220 = auto()
    COLOR221 = auto()
    COLOR222 = auto()
    COLOR223 = auto()
    COLOR224 = auto()
    COLOR225 = auto()
    COLOR226 = auto()
    COLOR227 = auto()
    COLOR228 = auto()
    COLOR229 = auto()
    COLOR230 = auto()

    COLOR231 = auto()  # also "White"

    # Shades of gray (1 to 24, dark to light)
    GRAY1  = COLOR232 = auto()  # darkest gray
    GRAY2  = COLOR233 = auto()
    GRAY3  = COLOR234 = auto()
    GRAY4  = COLOR235 = auto()
    GRAY5  = COLOR236 = auto()
    GRAY6  = COLOR237 = auto()
    GRAY7  = COLOR238 = auto()
    GRAY8  = COLOR239 = auto()
    GRAY9  = COLOR240 = auto()
    GRAY10 = COLOR241 = auto()
    GRAY11 = COLOR242 = auto()
    GRAY12 = COLOR243 = auto()
    GRAY13 = COLOR244 = auto()
    GRAY14 = COLOR245 = auto()
    GRAY15 = COLOR246 = auto()
    GRAY16 = COLOR247 = auto()
    GRAY17 = COLOR248 = auto()
    GRAY18 = COLOR249 = auto()
    GRAY19 = COLOR250 = auto()
    GRAY20 = COLOR251 = auto()
    GRAY21 = COLOR252 = auto()
    GRAY22 = COLOR253 = auto()
    GRAY23 = COLOR254 = auto()
    GRAY24 = COLOR255 = auto()  # lightest gray

    # default or unrecognised color
    NONE = UNKNOWN = -1

    @property
    def as_string(self) -> Optional[str]:
        return self.name.capitalize() if self is not Color.NONE else None

    def get_code(self, length: int = 2, byteorder: Endianness = "little", signed: bool = True) -> bytes:
        return self.value.to_bytes(length=length, byteorder=byteorder, signed=signed)

    @staticmethod
    def from_name(name: Optional[str]) -> "Color":
        return getattr(Color, name.upper(), Color.UNKNOWN) if name is not None else Color.NONE

    @staticmethod
    def from_code(value: bytes, byteorder: Endianness = "little", signed: bool = True) -> "Color":
        return Color.by_value(int.from_bytes(value, signed=signed, byteorder=byteorder))

    @classmethod
    def by_value(cls, value: int):
        for member in cls:
            if member.value == value:
                return member

        return Color.UNKNOWN
