{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

-- | This implements a SinkSource with keyed changes.
module Util.KeyedChanges(
   KeyedChanges,

   -- The idea is to keep track of changes to which a key is attached.
   -- When a new sink is attached, only the most recent changes for each
   -- key are returned.

   -- Producer's interface
   newKeyedChanges,
      -- :: Ord key => IO (KeyedChanges key delta)
   sendKeyedChanges,
      -- :: Ord key => key -> delta -> KeyedChanges key delta -> IO ()

   -- Used for sending changes which restore the situation to its default.
   -- If there is no entry for the key, nothing is done.  Otherwise the
   -- given delta is sent, and the entry is deleted.
   deleteKeyedChange,
      -- :: Ord key => key -> delta -> KeyedChanges key delta -> IO ()

   -- Consumer's interface
   -- instance Ord key => HasSource (KeyedChanges key delta) [delta] delta
   ) where

import qualified Data.Map as Map

import Util.Sources
import Util.Broadcaster

newtype KeyedChanges key delta
   = KeyedChanges (Broadcaster (Map.Map key delta) delta)

-- ------------------------------------------------------------------------
-- Producer's interface
-- ------------------------------------------------------------------------

newKeyedChanges :: Ord key => IO (KeyedChanges key delta)
newKeyedChanges =
   do
      broadcaster <- newBroadcaster Map.empty
      return (KeyedChanges broadcaster)

sendKeyedChanges :: Ord key => key -> delta -> KeyedChanges key delta -> IO ()
sendKeyedChanges key delta (KeyedChanges broadcaster) =
   applyUpdate broadcaster (\ map -> (Map.insert key delta map,[delta]))

deleteKeyedChange :: Ord key => key -> delta -> KeyedChanges key delta -> IO ()
deleteKeyedChange key delta (KeyedChanges broadcaster) =
   applyUpdate broadcaster (\ map -> case Map.lookup key map of
      Nothing -> (map,[])
      Just _ -> (Map.delete key map,[delta])
      )

-- ------------------------------------------------------------------------
-- Consumer's interface
-- ------------------------------------------------------------------------

instance Ord key => HasSource (KeyedChanges key delta) [delta] delta where
   toSource (KeyedChanges broadcaster) = map1 Map.elems (toSource broadcaster)