-- |
-- Module:     Control.Wire.Trans.Switch
-- Copyright:  (c) 2012 Ertugrul Soeylemez
-- 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.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 ::
=> 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 ::
=> 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 ::