module ParkBench.Internal.Terminal
  ( withTerminal,
    Terminal,
    renderToTerminal,
    blue,
    green,
    red,
  )
where

import Control.Exception (bracket_)
import qualified Data.ByteString as ByteString
import Data.IORef
import qualified Data.Text.Encoding as Text (encodeUtf8)
import ParkBench.Internal.Builder (Builder)
import qualified ParkBench.Internal.Builder as Builder
import ParkBench.Internal.Prelude
import System.IO (hSetEcho, stdin)

withTerminal :: (Terminal -> IO a) -> IO a
withTerminal :: forall a. (Terminal -> IO a) -> IO a
withTerminal Terminal -> IO a
action = do
  Terminal
terminal <- IO Terminal
newTerminal
  forall a b c. IO a -> IO b -> IO c -> IO c
bracket_ IO ()
hideCursor IO ()
showCursor do
    forall a b c. IO a -> IO b -> IO c -> IO c
bracket_ (Handle -> Bool -> IO ()
hSetEcho Handle
stdin Bool
False) (Handle -> Bool -> IO ()
hSetEcho Handle
stdin Bool
True) do
      ByteString -> IO ()
ByteString.putStr (Word8 -> ByteString
ByteString.singleton Word8
newline)
      Terminal -> IO a
action Terminal
terminal

newtype Terminal
  = Terminal (IORef Int)

newTerminal :: IO Terminal
newTerminal :: IO Terminal
newTerminal =
  coerce :: forall a b. Coercible a b => a -> b
coerce (forall a. a -> IO (IORef a)
newIORef (Int
0 :: Int))

renderToTerminal :: Terminal -> Builder -> IO ()
renderToTerminal :: Terminal -> Builder -> IO ()
renderToTerminal (Terminal IORef Int
lastLineCountRef) Builder
bytes0 = do
  Int
lastLineCount <- forall a. IORef a -> IO a
readIORef IORef Int
lastLineCountRef
  let bytes :: ByteString
bytes = Text -> ByteString
Text.encodeUtf8 (Builder -> Text
Builder.build (Int -> Builder
cursorUp Int
lastLineCount forall a. Semigroup a => a -> a -> a
<> Builder
clearFromCursor forall a. Semigroup a => a -> a -> a
<> Builder
bytes0))
  ByteString -> IO ()
ByteString.putStr ByteString
bytes
  forall a. IORef a -> a -> IO ()
writeIORef IORef Int
lastLineCountRef forall a b. (a -> b) -> a -> b
$! Word8 -> ByteString -> Int
ByteString.count Word8
newline ByteString
bytes

newline :: Word8
newline :: Word8
newline = Word8
10

hideCursor :: IO ()
hideCursor :: IO ()
hideCursor =
  ByteString -> IO ()
ByteString.putStr ByteString
"\ESC[?25l"

showCursor :: IO ()
showCursor :: IO ()
showCursor =
  ByteString -> IO ()
ByteString.putStr ByteString
"\ESC[?25h"

clearFromCursor :: Builder
clearFromCursor :: Builder
clearFromCursor =
  Builder
"\ESC[0J"

cursorUp :: Int -> Builder
cursorUp :: Int -> Builder
cursorUp Int
n =
  Builder
"\ESC[" forall a. Semigroup a => a -> a -> a
<> forall a. Integral a => a -> Builder
Builder.decimal Int
n forall a. Semigroup a => a -> a -> a
<> Builder
"F"

blue :: Builder -> Builder
blue :: Builder -> Builder
blue Builder
s =
  Builder
"\ESC[34m" forall a. Semigroup a => a -> a -> a
<> Builder
s forall a. Semigroup a => a -> a -> a
<> Builder
"\ESC[39m"

green :: Builder -> Builder
green :: Builder -> Builder
green Builder
s =
  Builder
"\ESC[32m" forall a. Semigroup a => a -> a -> a
<> Builder
s forall a. Semigroup a => a -> a -> a
<> Builder
"\ESC[39m"

red :: Builder -> Builder
red :: Builder -> Builder
red Builder
s =
  Builder
"\ESC[31m" forall a. Semigroup a => a -> a -> a
<> Builder
s forall a. Semigroup a => a -> a -> a
<> Builder
"\ESC[39m"