{- | Access helper functions in the State monad type family -}
module Data.Accessor.Monad.TF.State where

import qualified Control.Monad.State as State
import Control.Monad.State (MonadState, StateType)
import Control.Monad.Trans.Class (MonadTrans, )
import qualified Control.Monad.Trans.Class as Trans
import Control.Monad.Trans.State (State, runState, StateT, runStateT)
import qualified Data.Accessor.Basic as Accessor

-- * accessors in the form of actions in the state monad

set :: MonadState m => Accessor.T (StateType m) a -> a -> m ()
set f x = State.modify (Accessor.set f x)

get :: MonadState m => Accessor.T (StateType m) a -> m a
get f = State.gets (Accessor.get f)

modify :: MonadState m => Accessor.T (StateType m) a -> (a -> a) -> m ()
modify f g = State.modify (Accessor.modify f g)

{- |
Modify a record element and return its old value.
-}
getAndModify :: MonadState m => Accessor.T (StateType m) a -> (a -> a) -> m a
getAndModify f g =
   do x <- get f
      modify f g
      return x

{- |
Modify a record element and return its new value.
-}
modifyAndGet :: MonadState m => Accessor.T (StateType m) a -> (a -> a) -> m a
modifyAndGet f g =
   do modify f g
      get f



infix 1 %=, %:

{- |
Infix variant of 'set'.
-}
(%=) :: MonadState m => Accessor.T (StateType m) a -> a -> m ()
(%=) = set

{- |
Infix variant of 'modify'.
-}
(%:) :: MonadState m => Accessor.T (StateType m) a -> (a -> a) -> m ()
(%:) = modify



-- * lift a state monadic accessor to an accessor of a parent record

lift :: (MonadState mr) =>
   Accessor.T (StateType mr) s -> State s a -> mr a
lift f m =
   do s0 <- get f
      let (a,s1) = runState m s0
      set f s1
      return a

-- liftT :: (Monad m) =>
--    Accessor.T r s -> StateT s m a -> StateT r m a
liftT :: (Monad m, MonadTrans t, MonadState (t m)) =>
   Accessor.T (StateType (t m)) s -> StateT s m a -> t m a
liftT f m =
   do s0 <- get f
      (a,s1) <- Trans.lift $ runStateT m s0
      set f s1
      return a

{- not possible in this generality
lift :: (MonadState r mr, MonadState s ms) =>
   Accessor.T r s -> ms a -> mr a
-}