{-# LANGUAGE CPP                      #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE JavaScriptFFI            #-}
{-# LANGUAGE RecursiveDo              #-}

module Reflex.Contrib.Utils where

------------------------------------------------------------------------------
import           Control.Monad
import           Reflex
------------------------------------------------------------------------------


------------------------------------------------------------------------------
-- | A small amount of convenience for return ().
end :: Monad m => m ()
end = return ()


------------------------------------------------------------------------------
-- | fmapMaybe twice
fmapMaybe2 :: Reflex t => Event t (Maybe (Maybe a)) -> Event t a
fmapMaybe2 = fmapMaybe id . fmapMaybe id


------------------------------------------------------------------------------
-- | Construct an event with a tuple of (current,updated).
attachDynSelf :: Reflex t => Dynamic t a -> Event t (a,a)
attachDynSelf d = attach (current d) (updated d)


------------------------------------------------------------------------------
-- | Partitions an event into a pair of events that fire when the predicate
-- function evaluates to True and False respectively.
partitionEvent
    :: Reflex t
    => (a -> Bool)
    -> Event t a
    -> (Event t a, Event t a)
partitionEvent f e = ( fmapMaybe (\(b, x) -> if b then Just x else Nothing) e'
                     , fmapMaybe (\(b, x) -> if b then Nothing else Just x) e'
                     )
  where e' = fmap (\x -> (f x, x)) e


------------------------------------------------------------------------------
-- | Sometimes you end up with a Dynamic t Foo where Foo contains an Event
-- field.  This function collapses the two levels of Dynamic Event into just
-- an Event.
extractEvent
    :: (Reflex t, MonadHold t m)
    => (a -> Event t b)
    -> Dynamic t a
    -> m (Event t b)
extractEvent f = liftM (switch . current) . mapDyn f


------------------------------------------------------------------------------
-- | Sometimes you end up with a Dynamic t Foo where Foo contains a Dynamic
-- field.  This function collapses the two levels of Dynamic Dynamic into a
-- single Dynamic.
extractDyn
    :: (Reflex t, MonadHold t m)
    => (a -> Dynamic t b)
    -> Dynamic t a
    -> m (Dynamic t b)
extractDyn f = liftM joinDyn . mapDyn f


------------------------------------------------------------------------------
-- | This function has the slight flaw that if (f initValue) == False, it will
-- still get through.
filterDyn
    :: (MonadHold t m, Reflex t, Functor m)
    => (a -> Bool)
    -> Dynamic t a
    -> m (Dynamic t a)
filterDyn f d = fmap joinDyn $ holdDyn d $ fmap constDyn $
    ffilter f (updated d)


------------------------------------------------------------------------------
-- | Similar to filterDyn, but here the initial value problem is visible
-- because of the Maybe.
fmapMaybeDyn
    :: (MonadHold t m, Reflex t, Functor m)
    => (a -> Maybe b)
    -> Dynamic t a
    -> m (Dynamic t (Maybe b))
fmapMaybeDyn f d = do
    d' <- mapDyn f d
    fmap joinDyn $ holdDyn d' $ fmap (constDyn . Just) $
      fmapMaybe f (updated d)