module Eventloop.System.Processing where

import Control.Exception
import Control.Concurrent.ExceptionUtility
import Control.Concurrent.STM

import Eventloop.Types.Common
import Eventloop.Types.Exception
import Eventloop.Types.Events
import Eventloop.Types.System

{-
    To handle the pre-/postprocessing of in/out events
    Per module is needed: (moduleIdentifier, ioStateT, processFunc)
    Only modules with the appropriate processFunc are included if they exist
-}

processEventWithModule :: ProcessingDescription
                   -> SharedIOConstants
                   -> TVar SharedIOState
                   -> ( EventloopModuleIdentifier
                      , IOConstants
                      , TVar IOState
                      , (SharedIOConstants -> TVar SharedIOState -> IOConstants -> TVar IOState -> event -> IO [event])
                      )
                   -> event
                   -> IO [event]
processEventWithModule processingDescription sharedConst sharedIOT (moduleId, ioConst, ioStateT, processFunc) event
    = handle
        ( \exception ->
            throwIO (ProcessingException processingDescription moduleId exception)
        )
        ( do
            processFunc sharedConst sharedIOT ioConst ioStateT event
        )


processEventsWithModules :: ProcessingDescription
                         -> SharedIOConstants
                         -> TVar SharedIOState
                         -> [( EventloopModuleIdentifier
                             , IOConstants
                             , TVar IOState
                             , (SharedIOConstants -> TVar SharedIOState -> IOConstants -> TVar IOState -> event -> IO [event])
                             )]
                         -> [event]
                         -> IO [event]
processEventsWithModules _ _ _ _ []
    = return []
processEventsWithModules _ _ _ [] events
    = return events
processEventsWithModules processingDescription sharedConst sharedIOT (moduleProcessor:mps) (event:events)
    = do
        generatedEvents <- processEventWithModule processingDescription sharedConst sharedIOT moduleProcessor event
        processedEvents <- processEventsWithModules processingDescription sharedConst sharedIOT mps generatedEvents
        restProcessedEvents <- processEventsWithModules processingDescription sharedConst sharedIOT (moduleProcessor:mps) events
        return (processedEvents ++ restProcessedEvents)


processEvents :: ProcessingDescription
              -> EventloopSystemConfiguration progstateT
              -> [( EventloopModuleIdentifier
                  , IOConstants
                  , TVar IOState
                  , (SharedIOConstants -> TVar SharedIOState -> IOConstants -> TVar IOState -> event -> IO [event])
                  )]
              -> [event]
              -> IO [event]
processEvents processingDescription systemConfig moduleProcessors events
    = processEventsWithModules processingDescription sharedConst sharedIOT moduleProcessors events
    where
        sharedIOT = sharedIOStateT systemConfig
        sharedConst = sharedIOConstants systemConfig