{-# OPTIONS_HADDOCK not-home #-}
module Hedgehog.Internal.Region (
    Region(..)
  , newEmptyRegion
  , newOpenRegion
  , openRegion
  , setRegion
  , displayRegions
  , displayRegion
  , moveToBottom
  , finishRegion
  ) where

import           Control.Concurrent.STM (STM, TVar)
import qualified Control.Concurrent.STM.TMVar as TMVar
import qualified Control.Concurrent.STM.TVar as TVar
import           Control.Monad.Catch (MonadMask(..), bracket)
import           Control.Monad.IO.Class (MonadIO(..))

import           System.Console.Regions (ConsoleRegion, RegionLayout(..), LiftRegion(..))
import qualified System.Console.Regions as Console


data Body =
    Empty
  | Open ConsoleRegion
  | Closed

newtype Region =
  Region {
      Region -> TVar Body
unRegion :: TVar Body
    }

newEmptyRegion :: LiftRegion m => m Region
newEmptyRegion :: forall (m :: * -> *). LiftRegion m => m Region
newEmptyRegion =
  forall (m :: * -> *) a. LiftRegion m => STM a -> m a
liftRegion forall a b. (a -> b) -> a -> b
$ do
    TVar Body
ref <- forall a. a -> STM (TVar a)
TVar.newTVar Body
Empty
    forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ TVar Body -> Region
Region TVar Body
ref

newOpenRegion :: LiftRegion m => m Region
newOpenRegion :: forall (m :: * -> *). LiftRegion m => m Region
newOpenRegion =
  forall (m :: * -> *) a. LiftRegion m => STM a -> m a
liftRegion forall a b. (a -> b) -> a -> b
$ do
    ConsoleRegion
region <- forall (m :: * -> *).
LiftRegion m =>
RegionLayout -> m ConsoleRegion
Console.openConsoleRegion RegionLayout
Linear
    TVar Body
ref <- forall a. a -> STM (TVar a)
TVar.newTVar forall a b. (a -> b) -> a -> b
$ ConsoleRegion -> Body
Open ConsoleRegion
region
    forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ TVar Body -> Region
Region TVar Body
ref

openRegion :: LiftRegion m => Region -> String -> m ()
openRegion :: forall (m :: * -> *). LiftRegion m => Region -> String -> m ()
openRegion (Region TVar Body
var) String
content =
  forall (m :: * -> *) a. LiftRegion m => STM a -> m a
liftRegion forall a b. (a -> b) -> a -> b
$ do
    Body
body <- forall a. TVar a -> STM a
TVar.readTVar TVar Body
var
    case Body
body of
      Body
Empty -> do
        ConsoleRegion
region <- forall (m :: * -> *).
LiftRegion m =>
RegionLayout -> m ConsoleRegion
Console.openConsoleRegion RegionLayout
Linear
        forall a. TVar a -> a -> STM ()
TVar.writeTVar TVar Body
var forall a b. (a -> b) -> a -> b
$ ConsoleRegion -> Body
Open ConsoleRegion
region
        forall v (m :: * -> *).
(ToRegionContent v, LiftRegion m) =>
ConsoleRegion -> v -> m ()
Console.setConsoleRegion ConsoleRegion
region String
content

      Open ConsoleRegion
region ->
        forall v (m :: * -> *).
(ToRegionContent v, LiftRegion m) =>
ConsoleRegion -> v -> m ()
Console.setConsoleRegion ConsoleRegion
region String
content

      Body
Closed ->
        forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

setRegion :: LiftRegion m => Region -> String -> m ()
setRegion :: forall (m :: * -> *). LiftRegion m => Region -> String -> m ()
setRegion (Region TVar Body
var) String
content =
  forall (m :: * -> *) a. LiftRegion m => STM a -> m a
liftRegion forall a b. (a -> b) -> a -> b
$ do
    Body
body <- forall a. TVar a -> STM a
TVar.readTVar TVar Body
var
    case Body
body of
      Body
Empty ->
        forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

      Open ConsoleRegion
region ->
        forall v (m :: * -> *).
(ToRegionContent v, LiftRegion m) =>
ConsoleRegion -> v -> m ()
Console.setConsoleRegion ConsoleRegion
region String
content

      Body
Closed ->
        forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

displayRegions :: (MonadIO m, MonadMask m) => m a -> m a
displayRegions :: forall (m :: * -> *) a. (MonadIO m, MonadMask m) => m a -> m a
displayRegions m a
io =
  forall (m :: * -> *) a. (MonadIO m, MonadMask m) => m a -> m a
Console.displayConsoleRegions m a
io

displayRegion ::
     MonadIO m
  => MonadMask m
  => LiftRegion m
  => (Region -> m a)
  -> m a
displayRegion :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m, LiftRegion m) =>
(Region -> m a) -> m a
displayRegion =
  forall (m :: * -> *) a. (MonadIO m, MonadMask m) => m a -> m a
displayRegions forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a c b.
MonadMask m =>
m a -> (a -> m c) -> (a -> m b) -> m b
bracket forall (m :: * -> *). LiftRegion m => m Region
newOpenRegion forall (m :: * -> *). LiftRegion m => Region -> m ()
finishRegion

moveToBottom :: Region -> STM ()
moveToBottom :: Region -> STM ()
moveToBottom (Region TVar Body
var) =
  forall (m :: * -> *) a. LiftRegion m => STM a -> m a
liftRegion forall a b. (a -> b) -> a -> b
$ do
    Body
body <- forall a. TVar a -> STM a
TVar.readTVar TVar Body
var
    case Body
body of
      Body
Empty ->
        forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

      Open ConsoleRegion
region -> do
        Maybe [ConsoleRegion]
mxs <- forall a. TMVar a -> STM (Maybe a)
TMVar.tryTakeTMVar TMVar [ConsoleRegion]
Console.regionList
        case Maybe [ConsoleRegion]
mxs of
          Maybe [ConsoleRegion]
Nothing ->
            forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

          Just [ConsoleRegion]
xs0 ->
            let
              xs1 :: [ConsoleRegion]
xs1 =
                forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Eq a => a -> a -> Bool
/= ConsoleRegion
region) [ConsoleRegion]
xs0
            in
              forall a. TMVar a -> a -> STM ()
TMVar.putTMVar TMVar [ConsoleRegion]
Console.regionList (ConsoleRegion
region forall a. a -> [a] -> [a]
: [ConsoleRegion]
xs1)

      Body
Closed ->
        forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

finishRegion :: LiftRegion m => Region -> m ()
finishRegion :: forall (m :: * -> *). LiftRegion m => Region -> m ()
finishRegion (Region TVar Body
var) =
  forall (m :: * -> *) a. LiftRegion m => STM a -> m a
liftRegion forall a b. (a -> b) -> a -> b
$ do
    Body
body <- forall a. TVar a -> STM a
TVar.readTVar TVar Body
var
    case Body
body of
      Body
Empty -> do
        forall a. TVar a -> a -> STM ()
TVar.writeTVar TVar Body
var Body
Closed

      Open ConsoleRegion
region -> do
        Text
content <- forall (m :: * -> *). LiftRegion m => ConsoleRegion -> m Text
Console.getConsoleRegion ConsoleRegion
region
        forall v (m :: * -> *).
(Outputable v, LiftRegion m) =>
ConsoleRegion -> v -> m ()
Console.finishConsoleRegion ConsoleRegion
region Text
content
        forall a. TVar a -> a -> STM ()
TVar.writeTVar TVar Body
var Body
Closed

      Body
Closed ->
        forall (f :: * -> *) a. Applicative f => a -> f a
pure ()