{-|
Module      : Builders
Description : MTL equivalent of Smarties.Builders
Copyright   : (c) Peter Lu, 2018
License     : GPL-3
Maintainer  : chippermonky@gmail.com
Stability   : experimental


-}
module Smarties.Builders (
  -- $helper1link
  Utility(..),
  UtilityT(..),
  Perception(..),
  PerceptionT(..),
  Action(..),
  ActionT(..),
  Condition(..),
  ConditionT(..),
  SelfAction(..),
  SelfActionT(..),
  fromUtility,
  fromUtilityT,
  fromPerception,
  fromPerceptionT,
  fromCondition,
  fromConditionT,
  fromAction,
  fromActionT,
  fromSelfAction,
  fromSelfActionT
) where

import           Smarties.Base


-- $helper1link
-- helpers for building NodeSequenceT out of functions

-- | Utility return utility only
data Utility g p a where
  Utility :: (g -> p -> (a, g)) -> Utility g p a
  SimpleUtility :: (p -> a) -> Utility g p a

-- | Transformer variant
data UtilityT g p m a where
  UtilityT :: (Monad m) => (g -> p -> m (a, g)) -> UtilityT g p m a
  SimpleUtilityT :: (Monad m) => (p -> m a) -> UtilityT g p m a

-- | Perception modify perception only
data Perception g p where
  Perception :: (g -> p -> (g, p)) -> Perception g p
  SimplePerception :: (p -> p) -> Perception g p
  -- TODO delete this, just use Perception + Conditional, no need to combine them
  ConditionalPerception :: (g -> p -> (Bool, g, p)) -> Perception g p

-- | Transformer variant
data PerceptionT g p m where
  PerceptionT :: (g -> p -> m (g, p)) -> PerceptionT g p m
  SimplePerceptionT :: (p -> m p) -> PerceptionT g p m
  ConditionalPerceptionT :: (g -> p -> m (Bool, g, p)) -> PerceptionT g p m

-- | Actions create output and always have status SUCCESS
data Action g p o where
  Action :: (g -> p -> (g, o)) -> Action g p o
  SimpleAction :: (p -> o) -> Action g p o

-- | Transformer variant
data ActionT g p o m where
  ActionT :: (g -> p -> m (g, o)) -> ActionT g p o m
  SimpleActionT :: (p -> m o) -> ActionT g p o m

-- | Conditions have status SUCCESS if they return true FAIL otherwise
data Condition g p where
  Condition :: (g -> p -> (Bool, g)) -> Condition g p
  SimpleCondition :: (p -> Bool) -> Condition g p

-- | Transformer variant
data ConditionT g p m where
  ConditionT :: (g -> p -> m (Bool, g)) -> ConditionT g p m
  SimpleConditionT :: (p -> m Bool) -> ConditionT g p m

-- | same as Action except output is applied to perception
data SelfAction g p o where
  SelfAction :: (SelfActionable p o) => (g -> p -> (g, o)) -> SelfAction g p o
  SimpleSelfAction :: (SelfActionable p o) => (p -> o) -> SelfAction g p o

-- | same as Action except output is applied to perception
data SelfActionT g p o m where
  SelfActionT :: (SelfActionable p o) => (g -> p -> m (g, o)) -> SelfActionT g p o m
  SimpleSelfActionT :: (SelfActionable p o) => (p -> m o) -> SelfActionT g p o m

-- | convert UtilityT to NodeSequenceT
fromUtilityT :: (Monad m) => UtilityT g p m a -> NodeSequenceT g p o m a
fromUtilityT n = NodeSequenceT $ case n of
  UtilityT f       -> func f
  SimpleUtilityT f -> func (\g p -> f p >>= \x -> return (x, g))
  where
    func f g p = do
      (a, g') <- f g p
      return (a, g', p, SUCCESS, [])

-- |
-- these methods convert to transformer variant
fromUtility :: (Monad m) => Utility g p a -> NodeSequenceT g p o m a
fromUtility n = case n of
  Utility f       -> fromUtilityT $ UtilityT (\g p -> return $ f g p)
  SimpleUtility f -> fromUtilityT $ SimpleUtilityT (return . f)

-- | converts PerceptionT to NodeSequenceT
fromPerceptionT :: (Monad m) => PerceptionT g p m -> NodeSequenceT g p o m ()
fromPerceptionT n = NodeSequenceT $ case n of
  PerceptionT f            -> func f
  SimplePerceptionT f      -> func (\g p -> f p >>= \x -> return (g, x))
  ConditionalPerceptionT f -> cfunc f
  where
    func f g p = do
      (g', p') <- f g p
      return ((), g', p', SUCCESS, [])
    cfunc f g p = do
      (b, g', p') <- f g p
      return ((), g', p', if b then SUCCESS else FAIL, [])

-- |
-- these methods convert to transformer variant
fromPerception :: (Monad m) => Perception g p -> NodeSequenceT g p o m ()
fromPerception n = case n of
  Perception f -> fromPerceptionT $ PerceptionT (\g p -> return $ f g p)
  SimplePerception f -> fromPerceptionT $ SimplePerceptionT (return . f)
  ConditionalPerception f -> fromPerceptionT $ ConditionalPerceptionT (\g p -> return $ f g p)

-- | converts ConditionT to NodeSequenceT
fromConditionT :: (Monad m) => ConditionT g p m -> NodeSequenceT g p o m ()
fromConditionT n = NodeSequenceT $ case n of
  ConditionT f       -> func f
  SimpleConditionT f -> func (\g p -> f p >>= \x -> return (x, g))
  where
    func f g p = do
      (b, g') <- f g p
      return ((), g', p, if b then SUCCESS else FAIL, [])

-- |
-- these methods convert to transformer variant
fromCondition :: (Monad m) => Condition g p -> NodeSequenceT g p o m ()
fromCondition n = case n of
  Condition f       -> fromConditionT $ ConditionT (\g p -> return $ f g p)
  SimpleCondition f -> fromConditionT $ SimpleConditionT (return . f)

-- | converts ActionT to NodeSequenceT
fromActionT :: (Monad m) => ActionT g p o m -> NodeSequenceT g p o m ()
fromActionT n = NodeSequenceT $ case n of
  ActionT f       -> func f
  SimpleActionT f -> func (\g p -> f p >>= \x -> return (g, x))
  where
    func f g p = do
      (g', o) <- f g p
      return ((), g', p, SUCCESS, [o])

-- |
-- these methods convert to transformer variant
fromAction :: (Monad m) => Action g p o -> NodeSequenceT g p o m ()
fromAction n = case n of
  Action f       -> fromActionT $ ActionT (\g p -> return $ f g p)
  SimpleAction f -> fromActionT $ SimpleActionT (return . f)




-- | converts SelftActionT to NodeSequenceT
-- WARNING: MAY BE REMOVED IN A FUTURE RELEASE
fromSelfActionT :: (Monad m) => SelfActionT g p o m -> NodeSequenceT g p o m ()
fromSelfActionT n = NodeSequenceT $ case n of
  SelfActionT f       -> func f
  SimpleSelfActionT f -> func (\g p -> f p >>= \x -> return (g, x))
  where
    func f g p = do
      (g', o) <- f g p
      return ((), g', apply o p, SUCCESS, [o])

-- |
-- these methods convert to transformer variant
-- WARNING: MAY BE REMOVED IN A FUTURE RELEASE
fromSelfAction :: (Monad m) => SelfAction g p o -> NodeSequenceT g p o m ()
fromSelfAction n = case n of
  SelfAction f -> fromSelfActionT $ SelfActionT (\g p -> return $ f g p)
  SimpleSelfAction f -> fromSelfActionT $ SimpleSelfActionT (return . f)