-----------------------------------------------------------
-- |
-- Module      : Control.Imperative
-- Copyright   : (C) 2015, Yu Fukuzawa
-- License     : BSD3
-- Maintainer  : minpou.primer@email.com
-- Stability   : experimental
-- Portability : portable
--
-----------------------------------------------------------

{-# LANGUAGE ConstraintKinds  #-}
{-# LANGUAGE FlexibleContexts #-}

module Control.Imperative
( var
, val
, ref
, assign
, (=:)
, MonadImperative
, BaseEff
, Indexable(..)
  -- * Control Structures
, whenR
, unlessR
, ifR
, whileR
, untilR
, doWhileR
  -- * Types
, Ref
, Size(..)
, dim1
, dim2
, dim3
  -- * Re-exports
, module Control.Monad.Trans.Loop
, module Control.Monad.Base
) where

import           Control.Monad.Base
import           Control.Imperative.Hash        (MonadHash)
import           Control.Imperative.Operators
import           Control.Imperative.Internal
import           Control.Imperative.Var         (MonadVar, var)
import           Control.Imperative.Vector.Base
import           Control.Monad
import           Control.Monad.Trans.Loop

-- | Useful constraint synonym.
type MonadImperative m = (MonadVar m, MonadVector m, MonadHash m)

-- | A when statement for 'Ref'.
whenR :: MonadBase (BaseEff m) m => Ref (BaseEff m) Bool -> m () -> m ()
whenR v m = ref v >>= flip when m
{-# INLINE whenR #-}

-- | A unless statement for 'Ref'.
unlessR :: MonadBase (BaseEff m) m => Ref (BaseEff m) Bool -> m () -> m ()
unlessR v = whenR (notR v)
{-# INLINE unlessR #-}

-- | A if statement for 'Ref'
ifR :: MonadBase (BaseEff m) m => Ref (BaseEff m) Bool -> m a -> m a -> m a
ifR v t f = do
  b <- ref v
  if b then t else f
{-# INLINE ifR #-}

-- | A while loop statement for 'Ref'.
whileR :: MonadBase (BaseEff m) m => Ref (BaseEff m) Bool -> LoopT c () m c -> m ()
whileR v = while (ref v)
{-# INLINE whileR #-}

-- | A until loop statement for 'Ref'.
untilR :: MonadBase (BaseEff m) m => Ref (BaseEff m) Bool -> LoopT c () m c -> m ()
untilR v = while (ref (notR v))
{-# INLINE untilR #-}

-- | A do-while loop statement for 'Ref'.
doWhileR :: MonadBase (BaseEff m) m => LoopT a a m a -> Ref (BaseEff m) Bool -> m a
doWhileR m v = doWhile m (ref v)
{-# INLINE doWhileR #-}