module Control.SessionTypes.Interactive (
  interactive,
  interactiveStep
) where
import Control.SessionTypes.STTerm
import Control.SessionTypes.Types
import qualified Control.SessionTypes.Indexed as I
import Control.SessionTypes.MonadSession
import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
import Control.Monad.IO.Class    (MonadIO, liftIO)
import Data.Proxy                (Proxy (..))
import Data.Typeable             (Typeable, typeRep)
import Text.Read                 (readMaybe)
interactive :: (MonadIO m, HasConstraints '[Read, Show, Typeable] s, Show a) => STTerm m s r a -> m a
interactive (Send _ r) = interactive r
interactive r@(Recv c) = do
    liftIO $ putStr $ "Enter value of type " ++ typeShow r ++ ": "
    ma <- liftIO $ fmap readMaybe getLine
    case ma of
      Nothing -> interactive r
      Just a  -> interactive $ c a
  where typeShow :: forall m ctx a r k b. Typeable a => STTerm m ('Cap ctx (a :?> r)) k b -> String
        typeShow _ = show $ typeRep (Proxy :: Proxy a)
interactive (Sel1 s)     = interactive s
interactive (Sel2 r)     = interactive r
interactive (OffZ s)    = interactive s
interactive (OffS s xs) = do
  liftIO $ putStr $ "(L)eft or (R)ight: "
  lr <- liftIO getLine
  case lr of
    "L"     -> interactive s
    "Left"  -> interactive s
    "R"     -> interactive xs
    "Right" -> interactive xs
    _ -> do
      liftIO $ putStrLn "Invalid option"
      interactive (OffS s xs)
interactive (Rec s)  = interactive s
interactive (Weaken s)  = interactive s
interactive (Var s)  = interactive s
interactive (Lift m) = m >>= interactive
interactive (Ret a)  = return a
interactiveStep :: (MonadIO m, HasConstraints '[Read, Show, Typeable] s, Show a) => STTerm m s r a -> m (Maybe a)
interactiveStep st = runMaybeT (interactiveStep' st)
interactiveStep' :: (MonadIO m, HasConstraints '[Read, Show, Typeable] s, Show a) => STTerm m s r a -> MaybeT m a
interactiveStep' s@(Send a r) = do
  printST s
  waitStep
  interactiveStep' r
interactiveStep' s@(Recv r) = do
  printST s
  ma <- liftIO $ fmap readMaybe getLine
  case ma of
    Nothing -> interactiveStep' s
    Just a -> waitStep >> interactiveStep' (r a)
interactiveStep' s@(Sel1 r) = do
  printST s
  waitStep
  interactiveStep' r
interactiveStep' s@(Sel2 r) = do
  printST s
  waitStep
  interactiveStep' r
interactiveStep' (OffZ r) = interactiveStep' r 
interactiveStep' s@(OffS r xs) = do
  printST s
  lr <- liftIO getLine
  if lr `elem` ["L","Left"]
    then waitStep >> interactiveStep' r
    else if lr `elem` ["R","Right"]
      then waitStep >> interactiveStep' xs
      else do
        liftIO $ putStrLn "Invalid option"
        interactiveStep' (OffS s xs)
interactiveStep' s@(Rec r) = do
  printST s
  waitStep
  interactiveStep' r
interactiveStep' s@(Weaken r) = do
  printST s
  waitStep
  interactiveStep' r
interactiveStep' s@(Var r) = do
  printST s
  waitStep
  interactiveStep' r
interactiveStep' s@(Lift m) = do
  printST s
  waitStep
  MaybeT $ m >>= \st -> runMaybeT $ interactiveStep' st
interactiveStep' s@(Ret a) = do
  printST s
  return a
  
printST :: (MonadIO m, HasConstraints [Typeable, Show] s, Show a) => STTerm m s r a -> MaybeT m ()
printST (Send a _)     = liftIO $ putStrLn $ "> Send value " ++ show a
printST r@(Recv _)     = liftIO $ putStr $ "?> Enter value of type " ++ typeShow r ++ ": "
  where typeShow :: forall m ctx a r k b. Typeable a => STTerm m ('Cap ctx (a :?> r)) k b -> String
        typeShow _ = show $ typeRep (Proxy :: Proxy a)
printST (Sel1 _)      = liftIO $ putStrLn "> Select1"
printST (Sel2 _)      = liftIO $ putStrLn "> Select2"
printST (OffZ _)     = return ()
printST (OffS _ _) = liftIO $ putStr $ "?> (L)eft or (R)ight: "
printST (Rec _)        = liftIO $ putStrLn "> Recurse"
printST (Weaken _)        = liftIO $ putStrLn "> Weaken"
printST (Var _)        = liftIO $ putStrLn "> Var"
printST (Lift _)       = liftIO $ putStrLn $ "> Lifted"
printST (Ret a)        = liftIO $ putStrLn $ "> Returned: " ++ show a
waitStep :: MonadIO m => MaybeT m ()
waitStep = do
  liftIO $ putStrLn "?> Press n to continue or q to quit."
  line <- liftIO $ getLine
  case line of
    "n" -> return ()
    "q" -> MaybeT $ return Nothing
    _ -> waitStep