-- | Probabilistic tracing conduits using 'Debug.Trace'
module Data.MediaBus.Conduit.Trace
    ( exitAfterC
    , traceShowC
    , traceShowSink
    ) where

import           Conduit
import           Data.Conduit.List
import           Control.Monad.State.Strict as State
import           Debug.Trace
import           System.Random

-- | Receive and trace the 'Show'n value prefixed by a message and then yield
-- down the stream. Since the number of elements sent over a conduit can be
-- tremendous, a tracing probability parameter was added, that can be between
-- @0.0@ - @1.0@, and sets the probability of with which a message will be
-- traced. This uses 'Debug.Trace.traceM' internally. Do not use this for
-- logging.
traceShowC :: (Show a, Monad m) => Double -> String -> Conduit a m a
traceShowC probability msg =
    evalStateC (mkStdGen 100, 0 :: Integer) $
        awaitForever $
        \x -> do
            (g, omitted) <- State.get
            let (p, g') = randomR (0, 1) g
            if p < probability
                then do
                    let omittedmsg = if omitted == 0
                                     then ""
                                     else " *** " ++
                                         show omitted ++
                                         " messages omitted"
                    traceM ((if null msg then "" else msg ++ ": ") ++ show x ++
                                omittedmsg)
                    State.put (g', 0)
                else State.put (g', omitted + 1)
            yield x

-- | Like 'traceShowC' but implemented as a 'Consumer' that also returns all
-- received inputs as a list when the conduit terminates.
traceShowSink :: (Show a, Monad m) => Double -> String -> Consumer a m [a]
traceShowSink probability msg =
    traceShowC probability msg .| consume

-- | For profiling and debugging it can sometimes be useful, to have a circuit
-- breaker, like this conduit, that exists after an given number of items have
-- been processed, and, until then just yields all inputs.
exitAfterC :: Monad m => Int -> Conduit a m a
exitAfterC 0 = return ()
exitAfterC n = await >>= maybe (return ()) (yield >=> const (exitAfterC (n - 1)))