-- |
-- Module:     Control.Wire.Trans.Switch
-- Copyright:  (c) 2012 Ertugrul Soeylemez
-- License:    BSD3
-- Maintainer: Ertugrul Soeylemez <es@ertes.de>
--
-- Switching combinators.  Notice that these combinators restart time
-- when switching.

module Control.Wire.Trans.Switch
    ( -- * Simple switching
      andThen,
      switch,
      switchBy,
      (-->)
    )
    where

import Control.Arrow
import Control.Monad
import Control.Wire.Wire


-- | Infix variant of 'andThen'.
--
-- This operator is right-associative with precedence 1.

(-->) :: (Monad m) => Wire e m a b -> Wire e m a b -> Wire e m a b
(-->) = andThen

infixr 1 -->


-- | Behaves like the first wire until it inhibits.  Switches to the
-- second wire as soon as the first one inhibits.
--
-- The @`andThen`@ operator is right-associative with precedence 1.
--
-- * Depends: like currently active wire.
--
-- * Inhibits: when switched to second wire and that one inhibits.
--
-- * Time: switching restarts time.

andThen ::
    (Monad m)
    => Wire e m a b  -- ^ Wire to start with.
    -> Wire e m a b  -- ^ Wire to switch into.
    -> Wire e m a b
andThen w1' w2' =
    mkGen $ \dt x' -> do
        (mx, w1) <- stepWire w1' dt x'
        case mx of
          Left _  -> stepWire w2' dt x'
          Right _ -> return (mx, andThen w1 w2')

infixr 1 `andThen`


-- | If the first argument wire produces a wire, switch to it
-- immediately.  If not, evolve the current wire.  The second argument
-- wire is the initial wire.
--
-- * Depends: like event wire and the currently active wire.
--
-- * Inhibits: when the currently active wire inhibits.
--
-- * Time: switching restarts time.

switch ::
    (Monad m)
    => Wire e m a (Wire e m a b)  -- ^ Produces a wire to switch into.
    -> Wire e m a b               -- ^ Initial wire.
    -> Wire e m a b
switch wnew' w0 =
    mkGen $ \dt x' -> do
        (w', wnew) <- liftM (first (either (const w0) id)) (stepWire wnew' dt x')
        (mx, w) <- stepWire w' dt x'
        return (mx, switch wnew w)


-- | Whenever the given wire inhibits, a new wire is constructed using
-- the given function.
--
-- * Depends: like currently active wire.
--
-- * Time: switching restarts time.

switchBy ::
    (Monad m)
    => (e' -> Wire e' m a b)  -- ^ Wire selection function.
    -> Wire e' m a b          -- ^ Initial wire.
    -> Wire e m a b
switchBy new w0 =
    mkGen $ \dt x' ->
        let select w' = do
                (mx, w) <- stepWire w' dt x'
                case mx of
                  Left ex -> select (new ex)
                  Right x -> return (Right x, switchBy new w)
        in select w0