module Haskell.Debug.Adapter.Thread where

import Control.Monad.IO.Class
import Control.Concurrent.Async
import Control.Concurrent
import qualified System.Log.Logger as L
import Data.Maybe
import qualified Data.List as L
import Control.Monad.State.Lazy
import Control.Lens
import Control.Monad.Except

import Haskell.Debug.Adapter.Type
import Haskell.Debug.Adapter.Utility
import Haskell.Debug.Adapter.Constant


-- |
--
start :: AppStores -> [IO ()] -> IO (Async ())
start dat acts = do
  as <- mapM async acts
  async $ run dat {_asyncsAppStores = as}



-- |
--
run :: AppStores -> IO ()
run appData = do
  L.debugM _LOG_THREAD_MGR "satrt thread manager"
  runApp appData app
  L.debugM _LOG_THREAD_MGR "stop thread manager"


-- |
--
app :: AppContext ()
app = catchError go errHdl
  where
    errHdl msg = do
      criticalEV _LOG_THREAD_MGR msg
      criticalEV _LOG_THREAD_MGR "stopping all threads."
      as <- view asyncsAppStores <$> get
      liftIO $ mapM_ cancel as

    go = isStop >>= \case
      True  -> return ()
      False -> do
        takeEvent >>= \case
          Nothing -> return ()
          Just ev -> runEvent ev
        liftIO $ threadDelay _1_SEC
        go


-- |
--
isStop :: AppContext Bool
isStop = do
  as <- view asyncsAppStores <$> get
  res <- liftIO $ mapM poll as
  when (L.any isJust res) $
    liftIO $ L.debugM _LOG_THREAD_MGR $ "thread status." ++ show res

  return $ L.all isJust res


-- |
--
takeEvent :: AppContext (Maybe Event)
takeEvent = do
  appDat <- get
  let mvar = appDat^.eventStoreAppStores
  liftIO $ goIO mvar


  where
    goIO mvar = isExists mvar >>= \case
      False -> return Nothing
      True  -> take1 mvar

    isExists mvar = readMVar mvar >>= \case
      [] -> return False
      _  -> return True

    take1 mvar = takeMVar mvar >>= \case
      [] -> do
        putMVar mvar []
        return Nothing
      (x:xs) -> do
        putMVar mvar xs
        return $ Just x


-- |
--
runEvent :: Event -> AppContext ()
runEvent CriticalExitEvent = do
  liftIO $ L.criticalM _LOG_THREAD_MGR "Critical exit started."
  as <- view asyncsAppStores <$> get
  liftIO $ mapM_ cancel as