module FRP.Sodium.Plain where
import qualified FRP.Sodium.Context as R
import Control.Applicative
import Control.Concurrent.Chan
import Control.Concurrent.MVar (MVar, newEmptyMVar, takeMVar, newMVar, readMVar)
import qualified Control.Concurrent.MVar as MV
import Control.Exception (evaluate)
import Control.Monad
import Control.Monad.State.Strict
import Control.Monad.Trans
import Data.Int
import Data.IORef
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe
import Data.Set (Set)
import qualified Data.Set as S
import Data.Sequence (Seq, (|>), (><))
import qualified Data.Sequence as Seq
import GHC.Exts
import System.Mem.Weak
import System.IO.Unsafe
import Unsafe.Coerce
modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar mv f = MV.modifyMVar mv $ \a -> do
    (a', b') <- f a
    evaluate a'
    return (a', b')
modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()
modifyMVar_ mv f = MV.modifyMVar_ mv $ \a -> do
    a' <- f a
    evaluate a'
    return a'
putMVar :: MVar a -> a -> IO ()
putMVar mv a = do
    evaluate a
    MV.putMVar mv a
unsafeNewIORef :: a -> b -> IORef a
unsafeNewIORef v dummy = unsafePerformIO (newIORef v)
data Plain
partition :: Partition
partition = unsafePerformIO createPartition
  where
    createPartition :: IO Partition
    createPartition = do
        lock <- newEmptyMVar
        nextNodeIDRef <- newIORef (NodeID 0)
        return $ Partition {
                paLock       = lock,
                paNextNodeID = nextNodeIDRef
            }
type Reactive = R.Reactive Plain
type Event = R.Event Plain
type Behavior = R.Behavior Plain
type Behaviour = R.Behavior Plain
data Sample a = Sample {
        unSample :: IO a,
        sDep     :: Dep,
        sampleKeepAlive :: IORef ()
    }
instance R.Context Plain where
    newtype Reactive Plain a = Reactive (StateT ReactiveState IO a)
    data Event Plain a = Event {  
            
            
            getListenRaw :: Reactive (Listen a),
            evCacheRef   :: IORef (Maybe (Listen a)),
            eDep         :: Dep
        }
    data Behavior Plain a = Behavior {
            updates_   :: Event a,  
                                    
            
            sampleImpl :: Sample a
        }
    sync = sync
    newEvent = newEvent
    listen = listen
    never = never
    merge = merge
    filterJust = filterJust
    hold = hold
    updates = updates 
    value = value
    snapshot = snapshot
    switchE = switchE
    switch = switch
    execute = execute
    sample = sample
    coalesce = coalesce
    once = once
    split = split
updates :: Behavior a -> Event a
updates = updates_
sync :: Reactive a -> IO a
sync task = do
    let loop :: StateT ReactiveState IO () = do
            queue1 <- gets asQueue1
            if not $ Seq.null queue1 then do
                let Reactive task = Seq.index queue1 0
                modify $ \as -> as { asQueue1 = Seq.drop 1 queue1 }
                task
                loop
              else do
                queue2 <- gets asQueue2
                mTask <- lift $ popPriorityQueue queue2
                case mTask of
                    Just (Reactive task) -> do
                        task
                        loop
                    Nothing -> do
                        final <- gets asFinal
                        if not $ Seq.null final then do
                            let Reactive task = Seq.index final 0
                            modify $ \as -> as { asFinal = Seq.drop 1 final }
                            task
                            loop
                          else
                            return ()
            post <- gets asPost
            unless (Seq.null post) $ do
                let Reactive task = post `Seq.index` 0
                modify $ \as -> as { asPost = Seq.drop 1 post }
                task
                loop
    outVar <- newIORef undefined
    let lock = paLock partition
    putMVar lock ()
    q <- newPriorityQueue
    evalStateT loop $ ReactiveState {
            asQueue1 = Seq.singleton (task >>= ioReactive . writeIORef outVar),
            asQueue2 = q,
            asFinal = Seq.empty,
            asPost = Seq.empty
        }
    takeMVar lock
    readIORef outVar
newEvent :: Reactive (Event a, a -> Reactive ())  
newEvent = do
    (ev, push, _) <- ioReactive $ newEventLinked undefined
    return (ev, push)
listen :: Event a -> (a -> IO ()) -> Reactive (IO ())
listen ev handle = listenTrans ev $ \a -> ioReactive (handle a >> touch ev)
never :: Event a
never = Event {
        getListenRaw = return $ Listen (\_ _ _ -> return (return ())) undefined, 
        evCacheRef   = unsafeNewIORef Nothing undefined,
        eDep         = undefined
    }
merge :: Event a -> Event a -> Event a
merge ea eb = Event gl cacheRef (dep (ea, eb))
  where
    cacheRef = unsafeNewIORef Nothing eb
    gl = do
        (l, push, nodeRef) <- ioReactive newEventImpl
        unlistener <- later $ do
            u1 <- linkedListen ea (Just nodeRef) False push
            u2 <- linkedListen eb (Just nodeRef) False push
            return (u1 >> u2)
        addCleanup_Listen unlistener l
filterJust :: Event (Maybe a) -> Event a
filterJust ema = Event gl cacheRef (dep ema)
  where
    cacheRef = unsafeNewIORef Nothing ema
    gl = do
        (l, push, nodeRef) <- ioReactive newEventImpl
        unlistener <- later $ linkedListen ema (Just nodeRef) False $ \ma -> case ma of
            Just a -> push a
            Nothing -> return ()
        addCleanup_Listen unlistener l
hold :: a -> Event a -> Reactive (Behavior a)
hold initA ea = do
    bsRef <- ioReactive $ newIORef $ initA `seq` BehaviorState initA Nothing
    unlistener <- later $ linkedListen ea Nothing False $ \a -> do
        bs <- ioReactive $ readIORef bsRef
        ioReactive $ writeIORef bsRef $ a `seq` bs { bsUpdate = Just a }
        when (isNothing (bsUpdate bs)) $ scheduleLast $ ioReactive $ do
            bs <- readIORef bsRef
            let newCurrent = fromJust (bsUpdate bs)
            writeIORef bsRef $ newCurrent `seq` BehaviorState newCurrent Nothing
    keepAliveRef <- ioReactive $ newIORef ()
    sample <- ioReactive $ addCleanup_Sample unlistener
        (Sample (bsCurrent <$> readIORef bsRef) (dep ea) keepAliveRef)
    let beh = sample `seq` Behavior {
                updates_   = ea,
                sampleImpl = sample
            }
    return beh
value :: Behavior a -> Event a
value ba = sa `seq` ea `seq` eventify (listenValueRaw ba) (dep (sa, ea))
  where
    sa = sampleImpl ba
    ea = updates ba
snapshot :: (a -> b -> c) -> Event a -> Behavior b -> Event c
snapshot f ea bb = sample' `seq` Event gl cacheRef (dep (ea, sample))
  where
    cacheRef = unsafeNewIORef Nothing bb
    sample = sampleImpl bb
    sample' = unSample sample
    gl = do
        (l, push, nodeRef) <- ioReactive newEventImpl
        unlistener <- later $ linkedListen ea (Just nodeRef) False $ \a -> do
                b <- ioReactive sample'
                push (f a b)
        addCleanup_Listen unlistener l
switchE :: Behavior (Event a) -> Event a
switchE bea = eea `seq` Event gl cacheRef (dep (eea, depRef))
  where
    eea      = updates bea
    cacheRef = unsafeNewIORef Nothing bea
    depRef   = unsafeNewIORef undefined bea
    gl = do
        (l, push, nodeRef) <- ioReactive newEventImpl
        unlisten2Ref <- ioReactive $ newIORef Nothing
        let doUnlisten2 = do
                mUnlisten2 <- readIORef unlisten2Ref
                fromMaybe (return ()) mUnlisten2
        unlistener1 <- later $ do
            initEa <- sample bea
            (ioReactive . writeIORef unlisten2Ref) =<< (Just <$> linkedListen initEa (Just nodeRef) False push)
            unlisten1 <- linkedListen eea (Just nodeRef) False $ \ea -> scheduleLast $ do
                ioReactive $ do
                    doUnlisten2
                    writeIORef depRef ea
                (ioReactive . writeIORef unlisten2Ref) =<< (Just <$> linkedListen ea (Just nodeRef) True push)
            return $ unlisten1 >> doUnlisten2
        addCleanup_Listen unlistener1 l
switch :: Behavior (Behavior a) -> Reactive (Behavior a)
switch bba = do
    ba <- sample bba
    depRef <- ioReactive $ newIORef ba
    za <- sample ba
    let eba = updates bba
    ioReactive $ evaluate eba
    (ev, push, nodeRef) <- ioReactive $ newEventLinked (dep (bba, depRef))
    unlisten2Ref <- ioReactive $ newIORef Nothing
    let doUnlisten2 = do
            mUnlisten2 <- readIORef unlisten2Ref
            fromMaybe (return ()) mUnlisten2
    unlisten1 <- listenValueRaw bba (Just nodeRef) False $ \ba -> do
        ioReactive $ do
            doUnlisten2
            writeIORef depRef ba
        (ioReactive . writeIORef unlisten2Ref . Just) =<< listenValueRaw ba (Just nodeRef) False push
    hold za $ finalizeEvent ev (unlisten1 >> doUnlisten2)
execute :: Event (Reactive a) -> Event a
execute ev = Event gl cacheRef (dep ev)
  where
    cacheRef = unsafeNewIORef Nothing ev
    gl = do
        (l, push, nodeRef) <- ioReactive newEventImpl
        unlistener <- later $ linkedListen ev (Just nodeRef) False $ \action -> action >>= push
        addCleanup_Listen unlistener l
sample :: Behavior a -> Reactive a
sample = ioReactive . unSample . sampleImpl
coalesce :: (a -> a -> a) -> Event a -> Event a
coalesce combine e = Event gl cacheRef (dep e)
  where
    cacheRef = unsafeNewIORef Nothing e
    gl = do
        (l, push, nodeRef) <- ioReactive newEventImpl
        outRef <- ioReactive $ newIORef Nothing  
        unlistener <- later $ linkedListen e (Just nodeRef) False $ \a -> do
            first <- isNothing <$> ioReactive (readIORef outRef)
            ioReactive $ modifyIORef outRef $ \ma -> Just $ case ma of
                Just a0 -> a0 `combine` a
                Nothing -> a
            when first $ schedulePrioritized (Just nodeRef) $ do
                Just out <- ioReactive $ readIORef outRef
                ioReactive $ writeIORef outRef Nothing
                push out
        addCleanup_Listen unlistener l
once :: Event a -> Event a
once e = Event gl cacheRef (dep e)
  where
    cacheRef = unsafeNewIORef Nothing e
    gl = do
        (l, push, nodeRef) <- ioReactive newEventImpl
        aliveRef <- ioReactive $ newIORef True
        unlistener <- later $ do
            rec
                unlisten <- linkedListen e (Just nodeRef) False $ \a -> do
                    alive <- ioReactive $ readIORef aliveRef
                    when alive $ do
                        ioReactive $ writeIORef aliveRef False
                        scheduleLast $ ioReactive unlisten
                        push a
            return unlisten
        addCleanup_Listen unlistener l
split :: Event [a] -> Event a
split esa = Event gl cacheRef (dep esa)
  where
    cacheRef = unsafeNewIORef Nothing esa
    gl = do
        (l, push, nodeRef) <- ioReactive newEventImpl
        unlistener <- later $ linkedListen esa (Just nodeRef) False $ \as ->
            schedulePost $ map push as
        addCleanup_Listen unlistener l
newBehavior :: a  
            -> Reactive (Behavior a, a -> Reactive ())
newBehavior = R.newBehavior
newBehaviour :: a  
            -> Reactive (Behavior a, a -> Reactive ())
newBehaviour = R.newBehaviour
mergeWith :: (a -> a -> a) -> Event a -> Event a -> Event a
mergeWith = R.mergeWith
filterE :: (a -> Bool) -> Event a -> Event a
filterE = R.filterE
gate :: Event a -> Behavior Bool -> Event a
gate = R.gate
collectE :: (a -> s -> (b, s)) -> s -> Event a -> Reactive (Event b)
collectE = R.collectE
collect :: (a -> s -> (b, s)) -> s -> Behavior a -> Reactive (Behavior b)
collect = R.collect
accum :: a -> Event (a -> a) -> Reactive (Behavior a)
accum = R.accum
class PriorityQueueable k where
    priorityOf :: k -> IO Int64
newtype Sequence = Sequence Int64 deriving (Eq, Ord, Enum)
data PriorityQueue k v = PriorityQueue {
        pqNextSeq :: IORef Sequence,
        pqDirty   :: IORef Bool,
        pqQueue   :: IORef (Map (Int64, Sequence) v),
        pqData    :: IORef (Map Sequence (k, v))
    }
newPriorityQueue :: IO (PriorityQueue k v)
newPriorityQueue =
    PriorityQueue <$> newIORef (Sequence 0) <*> newIORef False
                  <*> newIORef M.empty <*> newIORef M.empty
pushPriorityQueue :: PriorityQueueable k => PriorityQueue k v -> k -> v -> IO ()
pushPriorityQueue pq k v = do
    prio <- priorityOf k
    seq <- readIORef (pqNextSeq pq)
    modifyIORef (pqNextSeq pq) succ
    modifyIORef (pqQueue pq) (M.insert (prio, seq) v)
    modifyIORef (pqData pq)  (M.insert seq (k, v))
dirtyPriorityQueue :: PriorityQueue k v -> IO ()
dirtyPriorityQueue pq = writeIORef (pqDirty pq) True
popPriorityQueue :: PriorityQueueable k => PriorityQueue k v -> IO (Maybe v)
popPriorityQueue pq = do
    maybeRegen
    q <- readIORef (pqQueue pq)
    if M.null q
        then return Nothing
        else do
            let (pseq@(prio, seq), v) = M.findMin q
            modifyIORef (pqQueue pq) (M.delete pseq)
            modifyIORef (pqData pq)  (M.delete seq)
            return $ Just v
  where
    maybeRegen = do
        dirty <- readIORef (pqDirty pq)
        when dirty $ do
            writeIORef (pqDirty pq) False
            dat <- readIORef (pqData pq)
            writeIORef (pqQueue pq) M.empty
            forM_ (M.assocs dat) $ \(seq,(k,v)) -> do
                prio <- priorityOf k
                modifyIORef (pqQueue pq) (M.insert (prio, seq) v)
type ID = Int64
instance PriorityQueueable (Maybe (MVar Node)) where
    priorityOf (Just nodeRef) = noRank <$> readMVar nodeRef
    priorityOf Nothing        = return maxBound
data ReactiveState = ReactiveState {
        asQueue1 :: Seq (Reactive ()),
        asQueue2 :: PriorityQueue (Maybe (MVar Node)) (Reactive ()),
        asFinal  :: Seq (Reactive ()),
        asPost   :: Seq (Reactive ())
    }
instance Functor (R.Reactive Plain) where
    fmap f rm = Reactive (fmap f (unReactive rm))
unReactive :: Reactive a -> StateT ReactiveState IO a
unReactive (Reactive m) = m
instance Applicative (R.Reactive Plain) where
    pure a = Reactive $ return a
    rf <*> rm = Reactive $ unReactive rf <*> unReactive rm
instance Monad (R.Reactive Plain) where
    return a = Reactive $ return a
    rma >>= kmb = Reactive $ do
        a <- unReactive rma
        unReactive (kmb a)
instance MonadFix (R.Reactive Plain) where
    mfix f = Reactive $ mfix $ \a -> unReactive (f a)
ioReactive :: IO a -> Reactive a
ioReactive io = Reactive $ liftIO io
newtype NodeID = NodeID Int deriving (Eq, Ord, Enum)
data Partition = Partition {
        paLock       :: MVar (),
        paNextNodeID :: IORef NodeID
    }
scheduleEarly :: Reactive () -> Reactive ()
scheduleEarly task = Reactive $ modify $ \as -> as { asQueue1 = asQueue1 as |> task }
scheduleLast :: Reactive () -> Reactive ()
scheduleLast task = Reactive $ modify $ \as -> as { asFinal = asFinal as |> task }
schedulePost :: [Reactive ()] -> Reactive ()
schedulePost tasks = Reactive $ modify $ \as -> as { asPost = Seq.fromList tasks >< asPost as }
data Listen a = Listen { runListen_ :: Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())
                       , listenerKeepAlive  :: IORef ()
                       }
getListen :: Event a -> Reactive (Listen a)
getListen (Event getLRaw cacheRef _) = do
    mL <- ioReactive $ readIORef cacheRef
    case mL of
        Just l -> return l
        Nothing -> do
            l <- getLRaw
            ioReactive $ writeIORef cacheRef (Just l)
            return l
linkedListen :: Event a -> Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())
linkedListen ev mv suppressEarlierFirings handle = do
    ioReactive $ evaluate ev
    l <- getListen ev
    unlisten <- runListen_ l mv suppressEarlierFirings handle
    
    _ <- ioReactive $ touch l
    return unlisten
listenTrans :: Event a -> (a -> Reactive ()) -> Reactive (IO ())
listenTrans ev handle = linkedListen ev Nothing False handle
data Observer p a = Observer {
        obNextID    :: ID,
        obListeners :: Map ID (a -> Reactive ()),
        obFirings   :: [a]
    }
data Node = Node {
        noID        :: NodeID,
        noRank      :: Int64,
        noListeners :: Map ID (MVar Node)
    }
newNode :: IO (MVar Node)
newNode = do
    nodeID <- readIORef (paNextNodeID partition)
    modifyIORef (paNextNodeID partition) succ
    newMVar (Node nodeID 0 M.empty)
wrap :: (Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())) -> IO (Listen a)
wrap l = Listen l <$> newIORef ()
touch :: a -> IO ()
touch a = evaluate a >> return ()
linkNode :: MVar Node -> ID -> MVar Node -> IO Bool
linkNode nodeRef iD mvTarget = do
    no <- readMVar nodeRef
    modified <- ensureBiggerThan S.empty mvTarget (noRank no)
    modifyMVar_ nodeRef $ \no -> return $
        let listeners' = M.insert iD mvTarget (noListeners no)
        in  listeners' `seq` no { noListeners = listeners' }
    return modified
ensureBiggerThan :: Set NodeID -> MVar Node -> Int64 -> IO Bool
ensureBiggerThan visited nodeRef limit = do
    no <- takeMVar nodeRef
    if noRank no > limit || noID no `S.member` visited then do
            putMVar nodeRef no
            return False
        else do
            let newSerial = succ limit
            
            putMVar nodeRef $ newSerial `seq` no { noRank = newSerial }
            forM_ (M.elems . noListeners $ no) $ \mvTarget -> do
                ensureBiggerThan (S.insert (noID no) visited) mvTarget newSerial
            return True
unlinkNode :: MVar Node -> ID -> IO ()
unlinkNode nodeRef iD = do
    modifyMVar_ nodeRef $ \no -> do
        let listeners' = M.delete iD (noListeners no)
        return $ listeners' `seq` no { noListeners = listeners' }
newtype Dep = Dep Any
dep :: a -> Dep
dep = Dep . unsafeCoerce
newEventImpl :: forall p a . IO (Listen a, a -> Reactive (), MVar Node)
newEventImpl = do
    nodeRef <- newNode
    mvObs <- newMVar (Observer 0 M.empty [])
    cacheRef <- newIORef Nothing
    rec
        let l mMvTarget suppressEarlierFirings handle = do
                (firings, unlisten, iD) <- ioReactive $ modifyMVar mvObs $ \ob -> do
                    let iD = obNextID ob
                        nextID' = succ iD
                        listeners' = M.insert iD handle (obListeners ob)
                        ob' = nextID' `seq` listeners' `seq` 
                              ob { obNextID    = nextID',
                                   obListeners = listeners' }
                        unlisten = do
                            modifyMVar_ mvObs $ \ob -> do
                                let listeners' = M.delete iD (obListeners ob)
                                return $ listeners' `seq` ob { obListeners = listeners' }
                            unlinkNode nodeRef iD
                            
                            return ()
                    return (ob', (reverse . obFirings $ ob, unlisten, iD))
                modified <- case mMvTarget of
                    Just mvTarget -> ioReactive $ linkNode nodeRef iD mvTarget
                    Nothing       -> return False
                
                
                when modified $ dirtyPrioritized
                unless suppressEarlierFirings $ mapM_ handle firings
                return unlisten
        listen <- wrap l  
    let push a = do
            ioReactive $ evaluate a
            ob <- ioReactive $ modifyMVar mvObs $ \ob -> return $
                (ob { obFirings = a : obFirings ob }, ob)
            
            when (null (obFirings ob)) $ scheduleLast $ ioReactive $ do
                modifyMVar_ mvObs $ \ob -> return $ ob { obFirings = [] }
            mapM_ ($ a) (M.elems . obListeners $ ob)
    return (listen, push, nodeRef)
newEventLinked :: Dep -> IO (Event a, a -> Reactive (), MVar Node)
newEventLinked d = do
    (listen, push, nodeRef) <- newEventImpl
    cacheRef <- newIORef Nothing
    let ev = Event {
                getListenRaw = return listen,
                evCacheRef = cacheRef,
                eDep = d
            }
    return (ev, push, nodeRef)
instance Functor (R.Event Plain) where
    f `fmap` e = Event getListen' cacheRef (dep e)
      where
        cacheRef = unsafeNewIORef Nothing e
        getListen' =
            return $ Listen (\mNodeRef suppressEarlierFirings handle -> do
                linkedListen e mNodeRef suppressEarlierFirings (handle . f)) undefined
instance Functor (R.Behavior Plain) where
    f `fmap` Behavior e s =
        fs `seq` fe `seq` Behavior fe fs
      where
        fe = f `fmap` e
        s' = unSample s
        fs = s' `seq` Sample (f `fmap` s') (dep s) undefined
constant :: a -> Behavior a
constant a = Behavior {
        updates_   = never,
        sampleImpl = Sample (return a) undefined undefined
    }
data BehaviorState a = BehaviorState {
        bsCurrent :: a,
        bsUpdate  :: Maybe a
    }
finalizeEvent :: Event a -> IO () -> Event a
finalizeEvent ea unlisten = ea { getListenRaw = gl }
  where
    gl = do
        l <- getListen ea
        ioReactive $ finalizeListen l unlisten
finalizeListen :: Listen a -> IO () -> IO (Listen a)
finalizeListen l unlisten = do
    mkWeakIORef (listenerKeepAlive l) unlisten
    return l
finalizeSample :: Sample a -> IO () -> IO (Sample a)
finalizeSample s unlisten = do
    mkWeakIORef (sampleKeepAlive s) unlisten
    return s
newtype Unlistener = Unlistener (MVar (Maybe (IO ())))
later :: Reactive (IO ()) -> Reactive Unlistener
later doListen = do
    unlistener@(Unlistener ref) <- newUnlistener
    
    
    scheduleEarly $ do
        mOldUnlisten <- ioReactive $ takeMVar ref
        case mOldUnlisten of
            Just _ -> do
                unlisten <- doListen
                ioReactive $ putMVar ref (Just unlisten)
            Nothing -> ioReactive $ putMVar ref mOldUnlisten
    return unlistener
  where
    newUnlistener :: Reactive Unlistener
    newUnlistener = Unlistener <$> ioReactive (newMVar (Just $ return ()))
addCleanup_Listen :: Unlistener -> Listen a -> Reactive (Listen a)
addCleanup_Listen (Unlistener ref) l = ioReactive $ finalizeListen l $ do
    mUnlisten <- takeMVar ref
    fromMaybe (return ()) mUnlisten
    putMVar ref Nothing
addCleanup_Sample :: Unlistener -> Sample a -> IO (Sample a)
addCleanup_Sample (Unlistener ref) s = finalizeSample s $ do
    mUnlisten <- takeMVar ref
    fromMaybe (return ()) mUnlisten
    putMVar ref Nothing
listenValueRaw :: Behavior a -> Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())
listenValueRaw ba = lastFiringOnly $ \mNodeRef suppressEarlierFirings handle -> do
    a <- sample ba
    handle a
    linkedListen (updates ba) mNodeRef suppressEarlierFirings handle
schedulePrioritized :: Maybe (MVar Node)
                    -> Reactive ()
                    -> Reactive ()
schedulePrioritized mNodeRef task = Reactive $ do
    q <- gets asQueue2
    lift $ pushPriorityQueue q mNodeRef task
dirtyPrioritized :: Reactive ()
dirtyPrioritized = Reactive $ do
    q <- gets asQueue2
    lift $ dirtyPriorityQueue q
lastFiringOnly :: (Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ()))
                -> Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())
lastFiringOnly listen mNodeRef suppressEarlierFirings handle = do
    aRef <- ioReactive $ newIORef Nothing
    listen mNodeRef suppressEarlierFirings $ \a -> do
        ma <- ioReactive $ readIORef aRef
        ioReactive $ writeIORef aRef (Just a)
        when (isNothing ma) $ schedulePrioritized mNodeRef $ do
            Just a <- ioReactive $ readIORef aRef
            ioReactive $ writeIORef aRef Nothing
            handle a
eventify :: (Maybe (MVar Node) -> Bool -> (a -> Reactive ()) -> Reactive (IO ())) -> Dep -> Event a
eventify listen d = Event gl cacheRef d
  where
    cacheRef = unsafeNewIORef Nothing listen
    gl = do
        (l, push, nodeRef) <- ioReactive newEventImpl
        unlistener <- later $ listen (Just nodeRef) False push
        addCleanup_Listen unlistener l
instance Applicative (R.Behavior Plain) where
    pure = constant
    b1@(Behavior e1 s1) <*> b2@(Behavior e2 s2) = Behavior u s
      where
        cacheRef = unsafeNewIORef Nothing s2
        keepaliveRef = unsafeNewIORef () s2
        u = Event gl cacheRef (dep (e1,e2))
        s1' = unSample s1
        s2' = unSample s2
        gl = do
            fRef <- ioReactive $ newIORef =<< unSample s1
            aRef <- ioReactive $ newIORef =<< unSample s2
            (l, push, nodeRef) <- ioReactive newEventImpl
            unlistener <- later $ do
                un1 <- linkedListen e1 (Just nodeRef) False $ \f -> do
                    ioReactive $ writeIORef fRef f
                    a <- ioReactive $ readIORef aRef
                    push (f a)
                un2 <- linkedListen e2 (Just nodeRef) False $ \a -> do
                    f <- ioReactive $ readIORef fRef
                    ioReactive $ writeIORef aRef a
                    push (f a)
                return (un1 >> un2)
            addCleanup_Listen unlistener l
        s = s1' `seq` s2' `seq` Sample (($) <$> s1' <*> s2') (dep (s1, s2)) keepaliveRef
changes :: Behavior a -> Event a
changes = updates
values :: Behavior a -> Event a
values = value
snapshotWith :: (a -> b -> c) -> Event a -> Behavior b -> Event c
snapshotWith = snapshot
count :: Event a -> Reactive (Behavior Int)
count = accum 0 . (const (1+) <$>)