{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE GeneralizedNewtypeDeriving, DefaultSignatures, GADTs #-}
module QuickSpec.Internal.Terminal where

import Control.Monad.Trans.Class
import Control.Monad.IO.Class
import Control.Monad.Trans.State.Strict
import Control.Monad.Trans.Reader
import qualified Test.QuickCheck.Text as Text

class Monad m => MonadTerminal m where
  putText :: String -> m ()
  putLine :: String -> m ()
  putTemp :: String -> m ()

  default putText :: (MonadTrans t, MonadTerminal m', m ~ t m') => String -> m ()
  putText = lift . putText

  default putLine :: (MonadTrans t, MonadTerminal m', m ~ t m') => String -> m ()
  putLine = lift . putLine

  default putTemp :: (MonadTrans t, MonadTerminal m', m ~ t m') => String -> m ()
  putTemp = lift . putTemp

instance MonadTerminal m => MonadTerminal (StateT s m)
instance MonadTerminal m => MonadTerminal (ReaderT r m)

putStatus :: MonadTerminal m => String -> m ()
putStatus str = putTemp ("[" ++ str ++ "...]")

clearStatus :: MonadTerminal m => m ()
clearStatus = putTemp ""

withStatus :: MonadTerminal m => String -> m a -> m a
withStatus str mx = putStatus str *> mx <* clearStatus

newtype Terminal a = Terminal (ReaderT Text.Terminal IO a)
  deriving (Functor, Applicative, Monad, MonadIO)

instance MonadTerminal Terminal where
  putText str = Terminal $ do
    term <- ask
    liftIO $ Text.putPart term str

  putLine str = Terminal $ do
    term <- ask
    liftIO $ Text.putLine term str

  putTemp str = Terminal $ do
    term <- ask
    liftIO $ Text.putTemp term str

withNullTerminal :: Terminal a -> IO a
withNullTerminal (Terminal mx) =
  Text.withNullTerminal (runReaderT mx)

withStdioTerminal :: Terminal a -> IO a
withStdioTerminal (Terminal mx) =
  Text.withStdioTerminal (runReaderT mx)