module Control.Event (
    -- * Synopsis
    -- | Event-driven programming in the imperative style.
        
    -- * Documentation
    Handler, Event(..),
    mapIO, filterIO, filterJust,
    newEvent, newEventsTagged
    ) where


import Data.IORef
import qualified Data.Unique -- ordinary uniques here, because they are Ord

import qualified Data.Map as Map

type Map = Map.Map

{-----------------------------------------------------------------------------
    Types
------------------------------------------------------------------------------}
-- | An /event handler/ is a function that takes an
-- /event value/ and performs some computation.
type Handler a = a -> IO ()


-- | An /event/ is a facility for registering
-- event handlers. These will be called whenever the event occurs.
-- 
-- When registering an event handler, you will also be given an action
-- that unregisters this handler again.
-- 
-- > do unregisterMyHandler <- register event myHandler
--
newtype Event a = Event { register :: Handler a -> IO (IO ()) }

{-----------------------------------------------------------------------------
    Combinators
------------------------------------------------------------------------------}
instance Functor Event where
    fmap f = mapIO (return . f)

-- | Map the event value with an 'IO' action.
mapIO :: (a -> IO b) -> Event a -> Event b
mapIO f e = Event $ \h -> register e $ \x -> f x >>= h 

-- | Filter event values that don't return 'True'.
filterIO :: (a -> IO Bool) -> Event a -> Event a
filterIO f e = Event $ \h ->
    register e $ \x -> f x >>= \b -> if b then h x else return ()

-- | Keep only those event values that are of the form 'Just'.
filterJust :: Event (Maybe a) -> Event a
filterJust e = Event $ \g -> register e (maybe (return ()) g)


{-----------------------------------------------------------------------------
    Construction
------------------------------------------------------------------------------}
-- | Build a facility to register and unregister event handlers.
-- Also yields a function that takes an event handler and runs all the registered
-- handlers.
--
-- Example:
--
-- > do
-- >     (event, fire) <- newEvent
-- >     register event (putStrLn)
-- >     fire "Hello!"
newEvent :: IO (Event a, a -> IO ())
newEvent = do
    handlers <- newIORef Map.empty
    let register handler = do
            key <- Data.Unique.newUnique
            atomicModifyIORef_ handlers $ Map.insert key handler
            return $ atomicModifyIORef_ handlers $ Map.delete key
        runHandlers a =
            mapM_ ($ a) . map snd . Map.toList =<< readIORef handlers
    return (Event register, runHandlers)

-- | Build several 'Event's from case analysis on a tag.
-- Generalization of 'newEvent'.
newEventsTagged :: Ord tag => IO (tag -> Event a, (tag, a) -> IO ())
newEventsTagged = do
    handlersRef <- newIORef Map.empty       -- :: Map key (Map Unique (Handler a))

    let register tag handler = do
            -- new identifier for this handler
            uid <- Data.Unique.newUnique
            -- add handler to map at  key
            atomicModifyIORef_ handlersRef $
                Map.alter (Just . Map.insert uid handler . maybe Map.empty id) tag
            -- remove handler from map at  key
            return $ atomicModifyIORef_ handlersRef $
                Map.adjust (Map.delete uid) tag

    let runHandlers (tag,a) = do
            handlers <- readIORef handlersRef
            case Map.lookup tag handlers of
                Just hs -> mapM_ ($ a) (Map.elems hs)
                Nothing -> return ()

    return (\tag -> Event (register tag), runHandlers)
            
{-----------------------------------------------------------------------------
    Utilities
------------------------------------------------------------------------------}
atomicModifyIORef_ ref f = atomicModifyIORef ref $ \x -> (f x, ())