{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Hcompta.LCC.Lib.Strict where

-- import Data.Bool
-- import qualified Control.Monad.Classes as MC
import qualified Data.Either as Lazy
import Control.Applicative (Applicative(..))
import Control.Monad (Monad(..))
import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad.Trans.Class (MonadTrans(..))
import Data.Foldable (Foldable(..))
import Data.Function (($), (.), id, flip)
import Data.Functor (Functor)
import Data.Semigroup (Semigroup(..))
import Data.Strict
import qualified Control.Monad.Trans.Reader as R
import qualified Control.Monad.Trans.State.Strict as SS

-- * Class 'StrictOf'
class StrictOf a b where
	strictOf :: a -> b
instance StrictOf (Lazy.Either l r) (Either l r) where
	strictOf (Lazy.Left  l) = Left l
	strictOf (Lazy.Right r) = Right r

-- * Type 'Maybe'
instance Applicative Maybe where
	pure = Just
	Nothing <*> _       = Nothing
	_       <*> Nothing = Nothing
	Just f  <*> Just a  = Just (f a)
instance Monad Maybe where
	return = Just
	Nothing >>= _ = Nothing
	Just a  >>= f = f a

-- * Type 'Either'
left :: (l -> a) -> Either l r -> Either a r
left f = either (Left . f) Right

rights :: [Either l r] -> [r]
rights = foldr (\case Right r -> (r :); _ -> id) []

accumLeftsAndFoldlRights
 :: (Foldable t, Semigroup l)
 => (ra -> r -> ra) -> ra
 -> t (Either l r)
 -> Either l ra
accumLeftsAndFoldlRights f rempty =
	foldl' (flip $ either el er) (Right rempty)
	where
		el l (Left ls)  = Left (l <> ls)
		el l _          = Left l
		er _ (Left ls)  = Left ls
		er r (Right ra) = Right (f ra r)

instance Applicative (Either l) where
	pure = Right
	Left l  <*> _       = Left l
	_       <*> Left l  = Left l
	Right f <*> Right r = Right (f r)
instance Monad (Either l) where
	return = Right
	Left  l >>= _ = Left l
	Right r >>= f = f r

-- * Type 'StateT'
newtype StateT s m a = StateT (SS.StateT s m a)
 deriving (Functor, Applicative, Monad, MonadIO, MonadTrans)

runState :: Monad m => s -> StateT s m a -> m (a, s)
runState s (StateT t) = SS.runStateT t s
evalState :: Monad m => s -> StateT s m a -> m a
evalState s (StateT t) = SS.evalStateT t s

{- NOTE: commented out to be able to have eff being a sub-state of s.
type instance MC.CanDo (StateT s m) eff = StateCanDo s eff
type family StateCanDo s eff where
	StateCanDo s (MC.EffState  s) = 'True
	StateCanDo s (MC.EffReader s) = 'True
	StateCanDo s (MC.EffLocal  s) = 'True
	StateCanDo s (MC.EffWriter s) = 'True
	StateCanDo s eff              = 'False
-}

-- * Type 'ReaderT'
newtype ReaderT s m a = ReaderT (R.ReaderT s m a)
 deriving (Functor, Applicative, Monad, MonadIO)

runReader :: Monad m => r -> ReaderT r m a -> m a
runReader r (ReaderT t) = R.runReaderT t r