    {-# LANGUAGE DeriveGeneric #-}
    {-# LANGUAGE DeriveAnyClass #-}
    {-# LANGUAGE MultiWayIf #-}
    {-# LANGUAGE ScopedTypeVariables #-}

    module Graphics.Vty.Attributes.Color
      ( Color(..)
      , ColorMode(..)

      -- ** Fixed Colors
      -- | Standard 8-color ANSI terminal color codes.
      --
      -- Note that these map to colors in the terminal's custom palette. For
      -- instance, `white` maps to whatever the terminal color theme uses for
      -- white.
      --
      -- Use these functions if you want to make apps that fit the terminal theme.
      -- If you want access to more/stronger colors use `rgbColor`
      , black
      , red
      , green
      , yellow
      , blue
      , magenta
      , cyan
      , white

      -- | Bright/Vivid variants of the standard 8-color ANSI
      , brightBlack
      , brightRed
      , brightGreen
      , brightYellow
      , brightBlue
      , brightMagenta
      , brightCyan
      , brightWhite
      -- ** Creating Colors From RGB
      , linearColor
      , srgbColor
      , rgbColor
      , color240
      , module Graphics.Vty.Attributes.Color240
      )
    where

    import Data.Word
    import GHC.Generics
    import Control.DeepSeq

    import Graphics.Vty.Attributes.Color240

    -- | Abstract data type representing a color.
    --
    -- Currently the foreground and background color are specified as points
    -- in either a:
    --
    --  * 16 color palette. Where the first 8 colors are equal to the 8
    --  colors of the ISO 6429 (ANSI) 8 color palette and the second 8
    --  colors are bright/vivid versions of the first 8 colors.
    --
    --  * 240 color palette. This palette is a regular sampling of the full
    --  RGB colorspace for the first 224 colors. The remaining 16 colors is
    --  a greyscale palette.
    --
    -- The 8 ISO 6429 (ANSI) colors are as follows:
    --
    --      * black (0)
    --
    --      * red (1)
    --
    --      * green (2)
    --
    --      * yellow (3)
    --
    --      * blue (4)
    --
    --      * magenta (5)
    --
    --      * cyan (6)
    --
    --      * white (7)
    --
    -- The mapping from points in the 240 color palette to colors actually
    -- displayable by the terminal depends on the number of colors the
    -- terminal claims to support. Which is usually determined by the
    -- terminfo "colors" property. If this property is not being accurately
    -- reported then the color reproduction will be incorrect.
    --
    -- If the terminal reports <= 16 colors then the 240 color palette
    -- points are only mapped to the 8 color palette. I'm not sure of
    -- the RGB points for the "bright" colors which is why they are not
    -- addressable via the 240 color palette.
    --
    -- If the terminal reports > 16 colors then the 240 color palette
    -- points are mapped to the nearest points in a ("color count" - 16)
    -- subsampling of the 240 color palette.
    --
    -- All of this assumes the terminals are behaving similarly to xterm and
    -- rxvt when handling colors. And that the individual colors have not
    -- been remapped by the user. There may be a way to verify this through
    -- terminfo but I don't know it.
    --
    -- Seriously, terminal color support is INSANE.
    data Color = ISOColor !Word8 | Color240 !Word8 | RGBColor !Word8 !Word8 !Word8
        deriving ( Color -> Color -> Bool
    (Color -> Color -> Bool) -> (Color -> Color -> Bool) -> Eq Color
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: Color -> Color -> Bool
    == :: Color -> Color -> Bool
    $c/= :: Color -> Color -> Bool
    /= :: Color -> Color -> Bool
    Eq, Int -> Color -> ShowS
    [Color] -> ShowS
    Color -> String
    (Int -> Color -> ShowS)
    -> (Color -> String) -> ([Color] -> ShowS) -> Show Color
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Color -> ShowS
    showsPrec :: Int -> Color -> ShowS
    $cshow :: Color -> String
    show :: Color -> String
    $cshowList :: [Color] -> ShowS
    showList :: [Color] -> ShowS
    Show, ReadPrec [Color]
    ReadPrec Color
    Int -> ReadS Color
    ReadS [Color]
    (Int -> ReadS Color)
    -> ReadS [Color]
    -> ReadPrec Color
    -> ReadPrec [Color]
    -> Read Color
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: Int -> ReadS Color
    readsPrec :: Int -> ReadS Color
    $creadList :: ReadS [Color]
    readList :: ReadS [Color]
    $creadPrec :: ReadPrec Color
    readPrec :: ReadPrec Color
    $creadListPrec :: ReadPrec [Color]
    readListPrec :: ReadPrec [Color]
    Read, (forall x. Color -> Rep Color x)
    -> (forall x. Rep Color x -> Color) -> Generic Color
    forall x. Rep Color x -> Color
    forall x. Color -> Rep Color x
    forall a.
    (forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
    $cfrom :: forall x. Color -> Rep Color x
    from :: forall x. Color -> Rep Color x
    $cto :: forall x. Rep Color x -> Color
    to :: forall x. Rep Color x -> Color
    Generic, Color -> ()
    (Color -> ()) -> NFData Color
    forall a. (a -> ()) -> NFData a
    $crnf :: Color -> ()
    rnf :: Color -> ()
    NFData )

    data ColorMode
        = NoColor
        | ColorMode8
        | ColorMode16
        | ColorMode240 !Word8
        | FullColor
        deriving ( ColorMode -> ColorMode -> Bool
    (ColorMode -> ColorMode -> Bool)
    -> (ColorMode -> ColorMode -> Bool) -> Eq ColorMode
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: ColorMode -> ColorMode -> Bool
    == :: ColorMode -> ColorMode -> Bool
    $c/= :: ColorMode -> ColorMode -> Bool
    /= :: ColorMode -> ColorMode -> Bool
    Eq, Int -> ColorMode -> ShowS
    [ColorMode] -> ShowS
    ColorMode -> String
    (Int -> ColorMode -> ShowS)
    -> (ColorMode -> String)
    -> ([ColorMode] -> ShowS)
    -> Show ColorMode
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> ColorMode -> ShowS
    showsPrec :: Int -> ColorMode -> ShowS
    $cshow :: ColorMode -> String
    show :: ColorMode -> String
    $cshowList :: [ColorMode] -> ShowS
    showList :: [ColorMode] -> ShowS
    Show, ReadPrec [ColorMode]
    ReadPrec ColorMode
    Int -> ReadS ColorMode
    ReadS [ColorMode]
    (Int -> ReadS ColorMode)
    -> ReadS [ColorMode]
    -> ReadPrec ColorMode
    -> ReadPrec [ColorMode]
    -> Read ColorMode
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: Int -> ReadS ColorMode
    readsPrec :: Int -> ReadS ColorMode
    $creadList :: ReadS [ColorMode]
    readList :: ReadS [ColorMode]
    $creadPrec :: ReadPrec ColorMode
    readPrec :: ReadPrec ColorMode
    $creadListPrec :: ReadPrec [ColorMode]
    readListPrec :: ReadPrec [ColorMode]
    Read )

    black, red, green, yellow, blue, magenta, cyan, white :: Color
    black :: Color
    black  = Word8 -> Color
    ISOColor Word8
    0
    red :: Color
    red    = Word8 -> Color
    ISOColor Word8
    1
    green :: Color
    green  = Word8 -> Color
    ISOColor Word8
    2
    yellow :: Color
    yellow = Word8 -> Color
    ISOColor Word8
    3
    blue :: Color
    blue   = Word8 -> Color
    ISOColor Word8
    4
    magenta :: Color
    magenta= Word8 -> Color
    ISOColor Word8
    5
    cyan :: Color
    cyan   = Word8 -> Color
    ISOColor Word8
    6
    white :: Color
    white  = Word8 -> Color
    ISOColor Word8
    7

    brightBlack, brightRed, brightGreen, brightYellow :: Color
    brightBlue, brightMagenta, brightCyan, brightWhite :: Color
    brightBlack :: Color
    brightBlack  = Word8 -> Color
    ISOColor Word8
    8
    brightRed :: Color
    brightRed    = Word8 -> Color
    ISOColor Word8
    9
    brightGreen :: Color
    brightGreen  = Word8 -> Color
    ISOColor Word8
    10
    brightYellow :: Color
    brightYellow = Word8 -> Color
    ISOColor Word8
    11
    brightBlue :: Color
    brightBlue   = Word8 -> Color
    ISOColor Word8
    12
    brightMagenta :: Color
    brightMagenta= Word8 -> Color
    ISOColor Word8
    13
    brightCyan :: Color
    brightCyan   = Word8 -> Color
    ISOColor Word8
    14
    brightWhite :: Color
    brightWhite  = Word8 -> Color
    ISOColor Word8
    15

    -- | Create a color value from RGB values in the 0..255 range inclusive.
    -- No transformation of the input values is done; a color is created
    -- directly from the RGB values specified, unlike the 'srgbColor' and
    -- 'color240' functions.
    linearColor :: Integral i => i -> i -> i -> Color
    linearColor :: forall i. Integral i => i -> i -> i -> Color
    linearColor i
    r i
    g i
    b = Word8 -> Word8 -> Word8 -> Color
    RGBColor Word8
    r' Word8
    g' Word8
    b'
        where
            r' :: Word8
    r' = i -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (i -> i
    clamp i
    r) :: Word8
            g' :: Word8
    g' = i -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (i -> i
    clamp i
    g) :: Word8
            b' :: Word8
    b' = i -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (i -> i
    clamp i
    b) :: Word8
            clamp :: i -> i
    clamp = i -> i -> i
    forall a. Ord a => a -> a -> a
    min i
    255 (i -> i) -> (i -> i) -> i -> i
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . i -> i -> i
    forall a. Ord a => a -> a -> a
    max i
    0

    -- | Given RGB values in the range 0..255 inclusive, create a color
    -- using the sRGB transformation described at
    --
    -- https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation
    srgbColor :: Integral i => i -> i -> i -> Color
    srgbColor :: forall i. Integral i => i -> i -> i -> Color
    srgbColor i
    r i
    g i
    b =
        -- TODO: it may be worth translating this to a lookup table, as with color240
        let shrink :: a -> Double
    shrink a
    n = a -> Double
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral a
    n Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    255 :: Double
            -- called gamma^-1 in wiki
            gamma :: a -> a
    gamma a
    u
                | a
    u a -> a -> Bool
    forall a. Ord a => a -> a -> Bool
    <= a
    0.04045 = a
    ua -> a -> a
    forall a. Fractional a => a -> a -> a
    /a
    12.92
                | Bool
    otherwise    = ((a
    u a -> a -> a
    forall a. Num a => a -> a -> a
    + a
    0.055) a -> a -> a
    forall a. Fractional a => a -> a -> a
    / a
    1.055) a -> a -> a
    forall a. Floating a => a -> a -> a
    ** a
    2.4
            -- TODO: this is a slightly inaccurate conversion. is it worth doing proterly?
            expand :: a -> b
    expand a
    n = a -> b
    forall b. Integral b => a -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    round (a
    255 a -> a -> a
    forall a. Num a => a -> a -> a
    * a
    n)
            convert :: i -> Word8
    convert = Double -> Word8
    forall {a} {b}. (RealFrac a, Integral b) => a -> b
    expand (Double -> Word8) -> (i -> Double) -> i -> Word8
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Double -> Double
    forall {a}. (Ord a, Floating a) => a -> a
    gamma (Double -> Double) -> (i -> Double) -> i -> Double
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . i -> Double
    forall {a}. Integral a => a -> Double
    shrink
         in Word8 -> Word8 -> Word8 -> Color
    RGBColor (i -> Word8
    convert i
    r) (i -> Word8
    convert i
    g) (i -> Word8
    convert i
    b)

    color240 :: Integral i => i -> i -> i -> Color
    color240 :: forall i. Integral i => i -> i -> i -> Color
    color240 i
    r i
    g i
    b = Word8 -> Color
    Color240 (i -> i -> i -> Word8
    forall i. Integral i => i -> i -> i -> Word8
    rgbColorToColor240 i
    r i
    g i
    b)

    -- | Create a Vty 'Color' (in the 240 color set) from an RGB triple.
    -- This is a synonym for 'color240'. This function is lossy in the sense
    -- that we only internally support 240 colors but the #RRGGBB format
    -- supports 256^3 colors.
    rgbColor :: Integral i => i -> i -> i -> Color
    rgbColor :: forall i. Integral i => i -> i -> i -> Color
    rgbColor = i -> i -> i -> Color
    forall i. Integral i => i -> i -> i -> Color
    color240