module Unused.CLI.Util
    ( resetScreen
    , withRuntime
    , installChildInterruptHandler
    , module System.Console.ANSI
    ) where

import Control.Concurrent.ParallelIO
import Control.Monad (void)
import System.Console.ANSI
import System.IO (hSetBuffering, BufferMode(NoBuffering), stdout)
import Control.Exception (throwTo)
import System.Posix.Signals (Handler(Catch), installHandler, keyboardSignal)
import Control.Concurrent (ThreadId, myThreadId, killThread)
import System.Exit (ExitCode(ExitFailure))

withRuntime :: IO a -> IO a
withRuntime a = do
    hSetBuffering stdout NoBuffering
    withInterruptHandler $ withoutCursor a <* stopGlobalPool

resetScreen :: IO ()
resetScreen = do
    clearScreen
    setCursorPosition 0 0

withoutCursor :: IO a -> IO a
withoutCursor body = do
    hideCursor
    body <* showCursor

withInterruptHandler :: IO a -> IO a
withInterruptHandler body = do
    tid <- myThreadId
    void $ installHandler keyboardSignal (Catch (handleInterrupt tid)) Nothing
    body

installChildInterruptHandler :: ThreadId -> IO ()
installChildInterruptHandler tid = do
    currentThread <- myThreadId
    void $ installHandler keyboardSignal (Catch (handleChildInterrupt currentThread tid)) Nothing

handleInterrupt :: ThreadId -> IO ()
handleInterrupt tid = do
    resetScreenState
    throwTo tid $ ExitFailure interruptExitCode

handleChildInterrupt :: ThreadId -> ThreadId -> IO ()
handleChildInterrupt parentTid childTid = do
    killThread childTid
    resetScreenState
    throwTo parentTid $ ExitFailure interruptExitCode
    handleInterrupt parentTid

interruptExitCode :: Int
interruptExitCode =
    signalToInt $ 128 + keyboardSignal
  where
    signalToInt s = read $ show s :: Int

resetScreenState :: IO ()
resetScreenState = do
    resetScreen
    showCursor
    setSGR [Reset]