{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}
--------------------------------------------------------------------------------
-- |
-- Module      :  Data.StateVar
-- Copyright   :  (c) Sven Panne 2009
--                (c) HATTORI,HIROKI 2014
-- License     :  BSD-style (see the file LICENSE)
-- 
-- Maintainer  :  HATTORI, HIROKI <seagull.kamome@gmail.com>
-- Stability   :  stable
-- Portability :  portable
--
-- Data.StateVarがIO専用なのが気にくわないので変換できるようにしただけ
-- 
--------------------------------------------------------------------------------

module Data.StateVar.Trans (
   -- * Readable State Variables
   HasGetter(..),
   GettableStateVar, makeGettableStateVar,
   -- * Writable State Variables
   HasSetter(..),
   SettableStateVar, makeSettableStateVar,
   -- * General State Variables
   StateVar, makeStateVar, makePtrVar,
   -- * Utility Functions
   ($~), ($=!), ($~!),
   (&),
   (^=), (^~), (^=!), (^~!), (^.),
   (@=)
) where

import Data.IORef (IORef, readIORef, writeIORef)
import GHC.Conc (STM, TVar, readTVar, writeTVar)
import Data.STRef (STRef, readSTRef, writeSTRef)
import Foreign.Ptr (Ptr)
import Foreign.Storable
import Control.Monad.ST.Safe (ST)
import Control.Monad.Trans (MonadTrans(..))
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Reader.Class (MonadReader(..))

--------------------------------------------------------------------------------

infixr 2 $=

--------------------------------------------------------------------------------

-- | The class of all readable state variables.
class HasGetter g m | g -> m where
   -- | Read the value of a state variable.
   get :: g a -> m a

instance HasGetter IORef IO where
  get = readIORef
  {-# INLINE get #-}
instance HasGetter TVar STM where
  get = readTVar
  {-# INLINE get #-}
instance HasGetter (STRef s) (ST s) where
  get = readSTRef
  {-# INLINE get #-}

-- | A concrete implementation of a read-only state variable, carrying an IO
-- action to read the value.
newtype GettableStateVar m a = GettableStateVar (m a)

instance HasGetter (GettableStateVar m) m where
   get (GettableStateVar g) = g
   {-# INLINE get #-}

-- | Construct a 'GettableStateVar' from an IO action.
makeGettableStateVar :: m a -> GettableStateVar m a
makeGettableStateVar = GettableStateVar
{-# INLINE makeGettableStateVar #-}

--------------------------------------------------------------------------------

-- | The class of all writable state variables.
class HasSetter s m where
   -- | Write a new value into a state variable.
   ($=) :: s a -> a -> m ()

instance HasSetter IORef IO where
  ($=) = writeIORef
  {-# INLINE ($=) #-}
instance HasSetter TVar STM where
  ($=) = writeTVar
  {-# INLINE ($=) #-}
instance HasSetter (STRef s) (ST s) where
  ($=) = writeSTRef
  {-# INLINE ($=) #-}

-- | A concrete implementation of a write-only state variable, carrying an IO
-- action to write the new value.
newtype SettableStateVar m a = SettableStateVar (a -> m ())

instance HasSetter (SettableStateVar m) m where
  ($=) (SettableStateVar s) a = s a
  {-# INLINE ($=) #-}

-- | Construct a 'SettableStateVar' from an IO action.
makeSettableStateVar :: (a -> m ()) -> SettableStateVar m a
makeSettableStateVar = SettableStateVar
{-# INLINE makeSettableStateVar #-}

--------------------------------------------------------------------------------

-- | A concrete implementation of a readable and writable state variable,
-- carrying one IO action to read the value and another IO action to write the
-- new value.
data StateVar m a =
  StateVar (GettableStateVar m a) (SettableStateVar m a)

instance HasGetter (StateVar m) m where
  get (StateVar g _) = get g
  {-# INLINE get #-}
instance HasSetter (StateVar m) m where
  ($=) (StateVar _ s) a = s $= a
  {-# INLINE ($=) #-}

-- | Construct a 'StateVar' from two IO actions, one for reading and one for
-- writing.
makeStateVar :: m a -> (a -> m ()) -> StateVar m a
makeStateVar g s = StateVar (makeGettableStateVar g) (makeSettableStateVar s)
{-# INLINE makeStateVar #-}

makePtrVar :: (MonadIO m, Storable a) => Ptr a -> StateVar m a
makePtrVar p = makeStateVar (liftIO $ peek p) (liftIO . poke p)
{-# INLINE makePtrVar #-}

--------------------------------------------------------------------------------

-- | A modificator convenience function, transforming the contents of a state
-- variable with a given funtion.

($~) :: (Monad m, HasGetter v m, HasSetter v m) => v a -> (a -> a) -> m ()
v $~ f = get v >>= ($=) v . f
{-# INLINE ($~) #-}

-- | A variant of '$=' which is strict in the value to be set.
($=!) :: (Monad m, HasSetter s m) => s a -> a -> m ()
v $=! x = x `seq` v $= x
{-# INLINE ($=!) #-}

-- | A variant of '$~' which is strict in the transformed value.
($~!) :: (Monad m, HasGetter v m, HasSetter v m) => v a -> (a -> a) -> m ()
v $~! f = get v >>= ($=!) v . f
{-# INLINE ($~!) #-}

--------------------------------------------------------------------------------

(&) :: s -> (s -> t) -> t
s & t = t s
{-# INLINE (&) #-}

infixl 8 ^=, ^~, ^=!, ^~!, ^.

(^=) :: HasSetter g m => (s -> g a) -> a -> s -> m ()
(fv ^= v) s = fv s $= v
{-# INLINE (^=) #-}

(^~) :: (Monad m, HasGetter g m, HasSetter g m) => (s -> g a) -> (a -> a) -> s -> m ()
(fv ^~ f) s = v $~ f where v = fv s
{-# INLINE (^~) #-}

(^=!) :: (Monad m, HasSetter g m) => (s -> g a) -> a -> s -> m ()
(fv ^=! x) s = v $=! x where v = fv s
{-# INLINE (^=!) #-}

(^~!) :: (Monad m, HasGetter g m, HasSetter g m) => (s -> g a) -> (a ->a) -> s -> m ()
(fv ^~! f) s = v $~! f where v = fv s
{-# INLINE (^~!) #-}

(^.) :: (Monad m, HasGetter g m, HasGetter h m) => (s -> g a) -> (a -> h b) -> s -> GettableStateVar m b
(fg ^. fh) s = makeGettableStateVar $ get (fg s) >>= get . fh
{-# INLINE (^.) #-}

--------------------------------------------------------------------------------

(@=) :: (Monad m, MonadTrans n, MonadReader s (n m), HasSetter g m) => (s -> g a) -> a -> n m ()
fv @= v = ask >>= lift . (fv ^= v)
{-# INLINE (@=) #-}