module Eventloop.Module.Timer.Timer
    ( defaultTimerModuleConfiguration
    , defaultTimerModuleIOState
    , timerModuleIdentifier
    , timerInitializer
    , timerEventRetriever
    , timerEventSender
    , timerTeardown
    ) where

import Control.Concurrent.Timer
import Control.Concurrent.MVar
import Control.Concurrent.Suspend.Lifted
import Data.Maybe
import Data.List

import Eventloop.Types.EventTypes
import Eventloop.Module.Timer.Types


defaultTimerModuleConfiguration :: EventloopModuleConfiguration
defaultTimerModuleConfiguration = ( EventloopModuleConfiguration 
                                        timerModuleIdentifier
                                        defaultTimerModuleIOState
                                        (Just timerInitializer)
                                        (Just timerEventRetriever)
                                        Nothing
                                        Nothing
                                        (Just timerTeardown)
                                        (Just timerEventSender)
                                 )

defaultTimerModuleIOState :: IOState
defaultTimerModuleIOState = TimerState [] [] undefined undefined

timerModuleIdentifier :: EventloopModuleIdentifier
timerModuleIdentifier = "timer"

                             
timerInitializer :: Initializer
timerInitializer sharedIO _ = do
                                incTickBuff <- newMVar []
                                incITickBuff <- newMVar []
                                return (sharedIO, TimerState [] [] incITickBuff incTickBuff)
                        
                                
timerEventRetriever :: EventRetriever
timerEventRetriever sharedIO timerState = do
                                            let
                                                incTickBuff = incomingTickBuffer timerState
                                                incITickBuff = incomingIntervalTickBuffer timerState
                                            incTicks <- swapMVar incTickBuff []
                                            incITicks <- swapMVar incITickBuff []
                                            let
                                                tickedTimerIds = map (\(Tick id) -> id) incTicks
                                            startedTimers' <- foldr (\id startedTimersIO -> (startedTimersIO >>= unregisterTimer id)) (return $ startedTimers timerState) tickedTimerIds
                                            return (sharedIO, timerState{startedTimers = startedTimers'}, (map InTimer incTicks) ++ (map InTimer incITicks))
                                    

timerEventSender :: EventSender
timerEventSender sharedIO timerState (OutTimer a) = do
                                                        timerState' <- timerEventSender' timerState a
                                                        return (sharedIO, timerState')
                                    
timerEventSender' :: IOState -> TimerOut -> IO IOState
timerEventSender' timerState (SetTimer id delay) = do
                                                    let
                                                        incTickBuff = incomingTickBuffer timerState
                                                        startedTimers_ = startedTimers timerState
                                                    startedTimers' <- registerTimer startedTimers_ incTickBuff id delay (oneShotStart)
                                                    let
                                                        timerState' = timerState {startedTimers = startedTimers'}
                                                    return timerState'

timerEventSender' timerState (SetIntervalTimer id delay) = do
                                                            let
                                                                incITickBuff = incomingIntervalTickBuffer timerState
                                                                startedITimers = startedIntervalTimers timerState
                                                            startedITimers' <- registerTimer startedITimers incITickBuff id delay (repeatedStart)
                                                            let
                                                                timerState' = timerState {startedIntervalTimers = startedITimers'}
                                                            return timerState'
                                                            
timerEventSender' timerState (UnsetTimer id) = do
                                                startedTimers' <- unregisterTimer id (startedTimers timerState)
                                                startedITimers' <- unregisterTimer id (startedIntervalTimers timerState)
                                                return (timerState {startedTimers = startedTimers', startedIntervalTimers=startedITimers'})
           
           
timerTeardown :: Teardown
timerTeardown sharedIO timerState = do
                                        let
                                            allStartedTimers = (startedTimers timerState) ++ (startedIntervalTimers timerState)
                                            allStartedIds = map fst allStartedTimers
                                        sequence_ (map (\id -> unregisterTimer id allStartedTimers) allStartedIds)
                                        return (sharedIO, timerState {startedTimers = [], startedIntervalTimers = []})

           
registerTimer :: [StartedTimer] -> IncomingTickBuffer -> TimerId -> MicroSecondDelay -> TimerStartFunction -> IO [StartedTimer]
registerTimer startedTimers incTickBuff id delay startFunc = do
                                                                timer <- newTimer
                                                                startFunc timer (tick id incTickBuff) ((usDelay.fromIntegral) delay)
                                                                return (startedTimers ++ [(id, timer)])
       

unregisterTimer :: TimerId -> [StartedTimer]  -> IO [StartedTimer]
unregisterTimer id startedTimers = do
                                    let
                                        startedTimerM = findStartedTimer startedTimers id
                                        stopAction (Just (_, timer)) = stopTimer timer
                                        stopAction Nothing           = return () 
                                        startedTimers' = filter (\(id', _) -> id /= id') startedTimers
                                    stopAction startedTimerM
                                    return startedTimers'
                                    

findStartedTimer :: [StartedTimer] -> TimerId -> Maybe StartedTimer
findStartedTimer startedTimers id = find (\(id', timer) -> id == id') startedTimers
                                        
       
tick :: TimerId -> IncomingTickBuffer -> IO ()
tick id incTickBuff = modifyMVar_ incTickBuff (\ticks -> return $ ticks ++ [Tick id])