{- | One-shot notation demos used to validate the graphics pipeline in a real
terminal (Phase 0), independent of the TUI.

* 'runNotationDemo' uses direct placement (@a=T@) to prove the Kitty encoder,
  renderer, and foreground query work end to end.
* 'runPlaceholderDemo' uses a virtual placement plus Unicode placeholder cells —
  the same mechanism the brick widget will use — to prove images can be shown
  through ordinary terminal cells.
-}
module Termtab.Graphics.Demo (
    runNotationDemo,
    runPlaceholderDemo,
) where

import Codec.Picture (Image, PixelRGBA8)
import Control.Monad (forM_)
import Data.ByteString qualified as BS
import Data.Map.Strict qualified as Map
import System.IO (hFlush, hPutStrLn, stderr, stdout)

import Termtab.Defaults (defaultSong)
import Termtab.Graphics.Font
import Termtab.Graphics.Kitty (encodeImage, encodeVirtual)
import Termtab.Graphics.KittyPlacement
import Termtab.Graphics.Notation (defaultLayout, renderNotationImage)
import Termtab.Graphics.TermColor (foregroundOrDefault)
import Termtab.Types

-- | Direct placement: draw the notation image at the cursor.
runNotationDemo :: IO ()
runNotationDemo :: IO ()
runNotationDemo = (Image PixelRGBA8 -> IO ()) -> IO ()
withDefaultNotation ((Image PixelRGBA8 -> IO ()) -> IO ())
-> (Image PixelRGBA8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Image PixelRGBA8
img -> do
    Handle -> ByteString -> IO ()
BS.hPut Handle
stdout (Image PixelRGBA8 -> ByteString
encodeImage Image PixelRGBA8
img)
    Handle -> IO ()
hFlush Handle
stdout
    String -> IO ()
putStr (Int -> Char -> String
forall a. Int -> a -> [a]
replicate Int
9 Char
'\n')
    String -> IO ()
putStrLn String
"^ termtab notation (Kitty graphics protocol, foreground-colored)"

-- | Virtual placement shown through Unicode placeholder cells.
runPlaceholderDemo :: IO ()
runPlaceholderDemo :: IO ()
runPlaceholderDemo = (Image PixelRGBA8 -> IO ()) -> IO ()
withDefaultNotation ((Image PixelRGBA8 -> IO ()) -> IO ())
-> (Image PixelRGBA8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Image PixelRGBA8
img -> do
    let imgId :: Int
imgId = Int
1
        cols :: Int
cols = Int
24
        rows :: Int
rows = Int
7
    Handle -> ByteString -> IO ()
BS.hPut Handle
stdout (Int -> Int -> Int -> Image PixelRGBA8 -> ByteString
encodeVirtual Int
imgId Int
cols Int
rows Image PixelRGBA8
img)
    Handle -> IO ()
hFlush Handle
stdout
    let sgr :: String
sgr = Int -> String
foregroundIdSGR Int
imgId
    [String] -> (String -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ (Int -> Int -> [String]
placeholderRows Int
cols Int
rows) ((String -> IO ()) -> IO ()) -> (String -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \String
line ->
        String -> IO ()
putStrLn (String
sgr String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
line String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"\ESC[0m")
    String -> IO ()
putStrLn String
"^ termtab notation (Kitty Unicode placeholders)"

{- | Render the default scratch measure with foreground-colored ink and hand it
to a display action, or report why it can't.
-}
withDefaultNotation :: (Image PixelRGBA8 -> IO ()) -> IO ()
withDefaultNotation :: (Image PixelRGBA8 -> IO ()) -> IO ()
withDefaultNotation Image PixelRGBA8 -> IO ()
display = do
    mFont <- IO (Maybe String)
bravuraFontPath
    case mFont of
        Maybe String
Nothing ->
            Handle -> String -> IO ()
hPutStrLn Handle
stderr String
"TERMTAB_BRAVURA_FONT is not set (use the Nix devshell)."
        Just String
path -> do
            ink <- IO PixelRGBA8
foregroundOrDefault
            withGlyphFont path $ \GlyphFont
font -> do
                let song :: Song
song = Song
defaultSong
                case (Song -> [Track]
songTracks Song
song, Song -> [Measure]
songMeasures Song
song) of
                    (Track
track : [Track]
_, Measure
measure : [Measure]
_) -> do
                        let instr :: Instrument
instr = Track -> Instrument
trackInstrument Track
track
                            beats :: [Beat]
beats = [Beat] -> MeasureIndex -> Map MeasureIndex [Beat] -> [Beat]
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault [] (Int -> MeasureIndex
MeasureIndex Int
0) (Track -> Map MeasureIndex [Beat]
trackBeats Track
track)
                            measures :: [(MeasureIndex, [Beat], Measure)]
measures = [(Int -> MeasureIndex
MeasureIndex Int
0, [Beat]
beats, Measure
measure)]
                        img <- GlyphFont
-> NotationLayout
-> PixelRGBA8
-> Int
-> Instrument
-> [(MeasureIndex, [Beat], Measure)]
-> IO (Image PixelRGBA8)
renderNotationImage GlyphFont
font NotationLayout
defaultLayout PixelRGBA8
ink Int
4 Instrument
instr [(MeasureIndex, [Beat], Measure)]
measures
                        display img
                    ([Track], [Measure])
_ -> Handle -> String -> IO ()
hPutStrLn Handle
stderr String
"default song is missing a track or measure"