-- |Functions and types that heavily used by the Frenetic implementation.
module Frenetic.Common
  ( Set
  , Map
  , MultiSet
  , ByteString
  , module Control.Concurrent.Chan
  , module Control.Concurrent
  , module System.Log.Logger
  , module Data.Monoid
  , select
  , both
  , catMaybes
  ) where

import System.Log.Logger hiding (Priority)
import Control.Concurrent.Chan
import Control.Concurrent
import Control.Monad
import Data.Monoid
import Data.Set (Set)
import Data.Map (Map)
import Data.MultiSet
import Data.ByteString.Lazy (ByteString)
import Data.Maybe (catMaybes)

-- |Produce a new channel that carries updates from both of the input channels,
-- but does not wait for both to be ready.  Analogous to Unix SELECT(2) followed
-- by READ(2) on the ready file descriptor.
select :: Chan a -> Chan b -> IO (Chan (Either a b))
select chan1 chan2 = do
  mergedChan <- newChan
  forkIO $ forever $ do
    v <- readChan chan1
    writeChan mergedChan (Left v)
  forkIO $ forever $ do
    v <- readChan chan2
    writeChan mergedChan (Right v)
  return mergedChan

-- |Produce a new channel that waits for both input channels to produce a value,
-- and then yields the latest version of both values.  If one channel produces
-- multiple values before the other produces any, then the early values are
-- discarded.  Afterwards, whenever one channel updates, the output channel
-- yields that update along with whatever the current version of the other
-- channel is.
both :: Chan a -> Chan b -> IO (Chan (a, b))
both chan1 chan2 = do
  merged <- select chan1 chan2
  result <- newChan
  let loop a b = do
        v <- readChan merged
        case (v, a, b) of
          (Left a, _, Nothing) -> loop (Just a) Nothing
          (Left a, _, Just b) -> do
            writeChan result (a, b)
            loop (Just a) (Just b)
          (Right b, Nothing, _) -> loop Nothing (Just b)
          (Right b, Just a, _) -> do
            writeChan result (a, b)
            loop (Just a) (Just b)
  forkIO (loop Nothing Nothing)
  return result