content: -- | Line terminal text frontend based on stdin/stdout, intended for logging
  -- tests, but may be used on a teletype terminal, or with keyboard and printer.
  module Game.LambdaHack.Client.UI.Frontend.Teletype
    ( startup, frontendName
    ) where
  
  import Prelude ()
  
  import Game.LambdaHack.Core.Prelude
  
  import           Control.Concurrent.Async
  import           Data.Char (chr, ord)
  import qualified System.IO as SIO
  
  import           Game.LambdaHack.Client.UI.Content.Screen
  import           Game.LambdaHack.Client.UI.Frame
  import           Game.LambdaHack.Client.UI.Frontend.Common
  import qualified Game.LambdaHack.Client.UI.Key as K
  import           Game.LambdaHack.Client.UI.PointUI
  import qualified Game.LambdaHack.Common.PointArray as PointArray
  import           Game.LambdaHack.Content.TileKind (floorSymbol)
  import qualified Game.LambdaHack.Definition.Color as Color
  
  -- No session data maintained by this frontend
  
  -- | The name of the frontend.
  frontendName :: String
  frontendName = "teletype"
  
  -- | Set up the frontend input and output.
  startup :: ScreenContent -> IO RawFrontend
  startup coscreen = do
    rf <- createRawFrontend coscreen (display coscreen) shutdown
    let storeKeys :: IO ()
        storeKeys = do
          c <- SIO.getChar  -- blocks here, so no polling
          SIO.hPutStrLn SIO.stderr ""  -- prevent next line starting indented
          let K.KM{..} = keyTranslate c
          saveKMP rf modifier key (PointUI 0 0)
          storeKeys
    SIO.hSetBuffering SIO.stdin SIO.NoBuffering
    SIO.hSetBuffering SIO.stderr $ SIO.BlockBuffering $
      Just $ 2 * rwidth coscreen * rheight coscreen
    void $ async storeKeys
    return $! rf
  
  shutdown :: IO ()
  shutdown = SIO.hFlush SIO.stdout >> SIO.hFlush SIO.stderr
  
  -- | Output to the screen via the frontend.
  display :: ScreenContent
          -> SingleFrame
          -> IO ()
  display coscreen SingleFrame{singleArray} = do
    let f w l =
          let acCharRaw = Color.charFromW32 w
              acChar = if acCharRaw == floorSymbol then '.' else acCharRaw
          in acChar : l
        levelChar = chunk $ PointArray.foldrA f [] singleArray
        chunk [] = []
        chunk l = let (ch, r) = splitAt (rwidth coscreen) l
                  in ch : chunk r
    SIO.hPutStr SIO.stderr $ unlines levelChar
    SIO.hFlush SIO.stderr
  
  keyTranslate :: Char -> K.KM
  keyTranslate e = (\(key, modifier) -> K.KM modifier key) $
    case e of
      '\ESC' -> (K.Esc,     K.NoModifier)
      '\n'   -> (K.Return,  K.NoModifier)
      '\r'   -> (K.Return,  K.NoModifier)
      ' '    -> (K.Space,   K.NoModifier)
      '\t'   -> (K.Tab,     K.NoModifier)
      c | ord '\^A' <= ord c && ord c <= ord '\^Z' ->
          -- Alas, only lower-case letters.
          (K.Char $ chr $ ord c - ord '\^A' + ord 'a', K.Control)
          -- Movement keys are more important than leader picking,
          -- so disabling the latter and interpreting the keypad numbers
          -- as movement:
        | c `elem` ['1'..'9'] -> (K.KP c,              K.NoModifier)
        | otherwise           -> (K.Char c,            K.NoModifier)
  