{- | Kitty Unicode placeholder support.

The Kitty graphics protocol can display a "virtual" image (transmitted with
@U=1@, see "Termtab.Graphics.Kitty".'encodeVirtual') through ordinary terminal
cells, which is what lets an image live inside a cell-based TUI like brick/vty.

Each placeholder cell holds the placeholder code point U+10EEEE. The image id is
carried in the cell's foreground color, and the cell's (row, column) within the
image is carried by combining diacritics appended to the placeholder character.
Kitty auto-increments the column for bare placeholder cells, so we only need to
stamp the row (and column 0) on the first cell of each line.
-}
module Termtab.Graphics.KittyPlacement (
    placeholderChar,
    rowColumnDiacritics,
    placeholderRows,
    foregroundIdSGR,
) where

import Data.Bits (shiftR, (.&.))

-- | The Kitty image-placeholder code point.
placeholderChar :: Char
placeholderChar :: Char
placeholderChar = Char
'\x10EEEE'

{- | Prefix of Kitty's rowcolumn diacritic table: @rowColumnDiacritics !! n@ is
the combining character that encodes the value @n@. This prefix covers the row
counts we need (staff height in cells).
-}
rowColumnDiacritics :: [Char]
rowColumnDiacritics :: String
rowColumnDiacritics =
    (Int -> Char) -> [Int] -> String
forall a b. (a -> b) -> [a] -> [b]
map
        Int -> Char
forall a. Enum a => Int -> a
toEnum
        [ Int
0x0305
        , Int
0x030D
        , Int
0x030E
        , Int
0x0310
        , Int
0x0312
        , Int
0x033D
        , Int
0x033E
        , Int
0x033F
        , Int
0x0346
        , Int
0x034A
        , Int
0x034B
        , Int
0x034C
        , Int
0x0350
        , Int
0x0351
        , Int
0x0352
        , Int
0x0357
        , Int
0x035B
        , Int
0x0363
        , Int
0x0364
        , Int
0x0365
        , Int
0x0366
        , Int
0x0367
        , Int
0x0368
        , Int
0x0369
        , Int
0x036A
        , Int
0x036B
        , Int
0x036C
        , Int
0x036D
        , Int
0x036E
        , Int
0x036F
        , Int
0x0483
        , Int
0x0484
        ]

-- | The combining diacritic encoding value @n@ (n must be within the table).
diacritic :: Int -> Char
diacritic :: Int -> Char
diacritic Int
n = String
rowColumnDiacritics String -> Int -> Char
forall a. HasCallStack => [a] -> Int -> a
!! Int
n

{- | Placeholder cell text for a @cols@×@rows@ image grid, one 'String' per row.
The first cell of each row carries the row diacritic and column-0 diacritic;
the remaining cells are bare placeholders whose columns Kitty auto-increments.
All cells must be drawn with 'foregroundIdSGR' as their foreground color.
-}
placeholderRows :: Int -> Int -> [String]
placeholderRows :: Int -> Int -> [String]
placeholderRows Int
cols Int
rows =
    [Int -> String
placeholderRow Int
r | Int
r <- [Int
0 .. Int
rows Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]]
  where
    placeholderRow :: Int -> String
placeholderRow Int
r =
        [Char
placeholderChar, Int -> Char
diacritic Int
r, Int -> Char
diacritic Int
0]
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
cols Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)) Char
placeholderChar

{- | Truecolor SGR that stores the image id in the foreground color (Kitty reads
the 24-bit value as the id). Not visible — the placeholder shows image pixels.
-}
foregroundIdSGR :: Int -> String
foregroundIdSGR :: Int -> String
foregroundIdSGR Int
i =
    let r :: Int
r = (Int
i Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftR` Int
16) Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0xFF
        g :: Int
g = (Int
i Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftR` Int
8) Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0xFF
        b :: Int
b = Int
i Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0xFF
     in String
"\ESC[38;2;" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
r String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
";" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
g String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
";" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
b String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"m"