{-# LANGUAGE FlexibleContexts #-}
module Monad where

import           Agda.IR

import           Agda.Interaction.Base          ( IOTCM )
import           Agda.TypeChecking.Monad        ( TCMT )
import           Control.Concurrent
import           Control.Monad.Reader
import           Data.Text                      ( Text
                                                , pack
                                                )
import           Server.CommandController       ( CommandController )
import qualified Server.CommandController      as CommandController
import           Server.ResponseController      ( ResponseController )
import qualified Server.ResponseController     as ResponseController

import           Data.IORef                     ( IORef
                                                , modifyIORef'
                                                , newIORef
                                                , readIORef
                                                , writeIORef
                                                )
import           Data.Maybe                     ( isJust )
import           Language.LSP.Server            ( MonadLsp
                                                , getConfig
                                                )
import qualified Language.LSP.Types            as LSP
import           Options

--------------------------------------------------------------------------------

data Env = Env
  { Env -> Options
envOptions            :: Options
  , Env -> Bool
envDevMode            :: Bool
  , Env -> Config
envConfig             :: Config
  , Env -> Chan Text
envLogChan            :: Chan Text
  , Env -> CommandController
envCommandController  :: CommandController
  , Env -> Chan Response
envResponseChan       :: Chan Response
  , Env -> ResponseController
envResponseController :: ResponseController
  }

createInitEnv :: (MonadIO m, MonadLsp Config m) => Options -> m Env
createInitEnv :: Options -> m Env
createInitEnv Options
options =
  Options
-> Bool
-> Config
-> Chan Text
-> CommandController
-> Chan Response
-> ResponseController
-> Env
Env Options
options (Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust (Options -> Maybe Int
optViaTCP Options
options))
    (Config
 -> Chan Text
 -> CommandController
 -> Chan Response
 -> ResponseController
 -> Env)
-> m Config
-> m (Chan Text
      -> CommandController -> Chan Response -> ResponseController -> Env)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m Config
forall config (m :: * -> *). MonadLsp config m => m config
getConfig
    m (Chan Text
   -> CommandController -> Chan Response -> ResponseController -> Env)
-> m (Chan Text)
-> m (CommandController
      -> Chan Response -> ResponseController -> Env)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IO (Chan Text) -> m (Chan Text)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO (Chan Text)
forall a. IO (Chan a)
newChan
    m (CommandController -> Chan Response -> ResponseController -> Env)
-> m CommandController
-> m (Chan Response -> ResponseController -> Env)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IO CommandController -> m CommandController
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO CommandController
CommandController.new
    m (Chan Response -> ResponseController -> Env)
-> m (Chan Response) -> m (ResponseController -> Env)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IO (Chan Response) -> m (Chan Response)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO (Chan Response)
forall a. IO (Chan a)
newChan
    m (ResponseController -> Env) -> m ResponseController -> m Env
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IO ResponseController -> m ResponseController
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ResponseController
ResponseController.new

--------------------------------------------------------------------------------

-- | OUR monad
type ServerM m = ReaderT Env m

runServerM :: Env -> ServerM m a -> m a
runServerM :: Env -> ServerM m a -> m a
runServerM = (ServerM m a -> Env -> m a) -> Env -> ServerM m a -> m a
forall a b c. (a -> b -> c) -> b -> a -> c
flip ServerM m a -> Env -> m a
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT

--------------------------------------------------------------------------------

writeLog :: (Monad m, MonadIO m) => Text -> ServerM m ()
writeLog :: Text -> ServerM m ()
writeLog Text
msg = do
  Chan Text
chan <- (Env -> Chan Text) -> ReaderT Env m (Chan Text)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Env -> Chan Text
envLogChan
  IO () -> ServerM m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ServerM m ()) -> IO () -> ServerM m ()
forall a b. (a -> b) -> a -> b
$ Chan Text -> Text -> IO ()
forall a. Chan a -> a -> IO ()
writeChan Chan Text
chan Text
msg

writeLog' :: (Monad m, MonadIO m, Show a) => a -> ServerM m ()
writeLog' :: a -> ServerM m ()
writeLog' a
x = do
  Chan Text
chan <- (Env -> Chan Text) -> ReaderT Env m (Chan Text)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Env -> Chan Text
envLogChan
  IO () -> ServerM m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ServerM m ()) -> IO () -> ServerM m ()
forall a b. (a -> b) -> a -> b
$ Chan Text -> Text -> IO ()
forall a. Chan a -> a -> IO ()
writeChan Chan Text
chan (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ a -> String
forall a. Show a => a -> String
show a
x

-- | Provider
provideCommand :: (Monad m, MonadIO m) => IOTCM -> ServerM m ()
provideCommand :: IOTCM -> ServerM m ()
provideCommand IOTCM
iotcm = do
  CommandController
controller <- (Env -> CommandController) -> ReaderT Env m CommandController
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Env -> CommandController
envCommandController
  IO () -> ServerM m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ServerM m ()) -> IO () -> ServerM m ()
forall a b. (a -> b) -> a -> b
$ CommandController -> IOTCM -> IO ()
CommandController.put CommandController
controller IOTCM
iotcm

-- | Consumter
consumeCommand :: (Monad m, MonadIO m) => Env -> m IOTCM
consumeCommand :: Env -> m IOTCM
consumeCommand Env
env = IO IOTCM -> m IOTCM
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO IOTCM -> m IOTCM) -> IO IOTCM -> m IOTCM
forall a b. (a -> b) -> a -> b
$ CommandController -> IO IOTCM
CommandController.take (Env -> CommandController
envCommandController Env
env)

waitUntilResponsesSent :: (Monad m, MonadIO m) => ServerM m ()
waitUntilResponsesSent :: ServerM m ()
waitUntilResponsesSent = do
  ResponseController
controller <- (Env -> ResponseController) -> ReaderT Env m ResponseController
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Env -> ResponseController
envResponseController
  IO () -> ServerM m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ServerM m ()) -> IO () -> ServerM m ()
forall a b. (a -> b) -> a -> b
$ ResponseController -> IO ()
ResponseController.setCheckpointAndWait ResponseController
controller

signalCommandFinish :: (Monad m, MonadIO m) => ServerM m ()
signalCommandFinish :: ServerM m ()
signalCommandFinish = do
  Text -> ServerM m ()
forall (m :: * -> *). (Monad m, MonadIO m) => Text -> ServerM m ()
writeLog Text
"[Command] Finished"
  -- send `ResponseEnd`
  Env
env <- ReaderT Env m Env
forall r (m :: * -> *). MonadReader r m => m r
ask
  IO () -> ServerM m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ServerM m ()) -> IO () -> ServerM m ()
forall a b. (a -> b) -> a -> b
$ Chan Response -> Response -> IO ()
forall a. Chan a -> a -> IO ()
writeChan (Env -> Chan Response
envResponseChan Env
env) Response
ResponseEnd
  -- allow the next Command to be consumed
  IO () -> ServerM m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ServerM m ()) -> IO () -> ServerM m ()
forall a b. (a -> b) -> a -> b
$ CommandController -> IO ()
CommandController.release (Env -> CommandController
envCommandController Env
env)

-- | Sends a Response to the client via "envResponseChan"
sendResponse :: (Monad m, MonadIO m) => Env -> Response -> TCMT m ()
sendResponse :: Env -> Response -> TCMT m ()
sendResponse Env
env Response
response = IO () -> TCMT m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> TCMT m ()) -> IO () -> TCMT m ()
forall a b. (a -> b) -> a -> b
$ Chan Response -> Response -> IO ()
forall a. Chan a -> a -> IO ()
writeChan (Env -> Chan Response
envResponseChan Env
env) Response
response