{-# LANGUAGE RankNTypes #-}
module Graphics.Rasterific.MiniLens
    ( -- * Types

      Lens
    , Lens'
    , Traversal
    , Traversal'
    , lens

      -- * Getter

    , (.^)
    , view
    , use

      -- * Setter

    , (.~)
    , (.=)
    , (%=)
    , (+=)
    , set

      -- * Helper

    , (&)
    ) where

import Control.Monad.Identity
import Control.Applicative
import Control.Monad.State        as State
import Data.Function( (&) )

infixl 8 .^
infixr 4 .~
infix  4 .=,%=,+=

-- | Does it look familiar? yes it's the official

-- Lens type.

type Lens s t a b =
    forall f. Functor f => (a -> f b) -> s -> f t

-- | Try to match the Lens' type alias.

type Lens' s a = Lens s s a a

-- | Traversal type, matched to the one of the lens

-- package.

type Traversal s t a b =
    forall f. Applicative f => (a -> f b) -> s -> f t

type Traversal' s a = Traversal s s a a

-- | Create a full lens out of setter and getter

lens :: (s -> a)
     -> (s -> b -> t)
     -> Lens s t a b
{-# INLINE lens #-}
lens accessor setter = \f src ->
  fmap (setter src) $ f (accessor src)

view :: s -> Lens s t a b -> a
{-# INLINE view #-}
view v l = getConst (l Const v)

(.^) :: s -> Lens s t a b -> a
{-# INLINE (.^) #-}
(.^) = view

set :: Lens' s a -> a -> s -> s
{-# INLINE set #-}
set l new v = runIdentity $ l (\_ -> Identity new) v

(.~) :: Lens' s a -> a -> s -> s
{-# INLINE (.~) #-}
(.~) = set

(.=) :: MonadState s m => Lens' s a -> a -> m ()
{-# INLINE (.=) #-}
(.=) l v = State.modify (l .~ v)

(%=) :: MonadState s m => Lens' s a -> (a -> a) -> m ()
{-# INLINE (%=) #-}
(%=) l f = State.modify $ \s -> s & l .~ f (s .^ l)

(+=) :: (Num a, MonadState s m) => Lens' s a -> a -> m ()
{-# INLINE (+=) #-}
(+=) l n = l %= (+ n)

use :: MonadState s m => Lens s t a b -> m a
{-# INLINE use #-}
use l = State.gets (.^ l)