module UniqueLogic.ST.System (
   -- * Preparation
   Var,
   Variable,
   Sys.globalVariable,
   plainVariable,
   -- * Posing statements
   T,
   localVariable,
   constant,
   assignment2,
   assignment3,
   Sys.Apply, arg, runApply,
   -- * Solution
   solve,
   query,
   ) where

import qualified UniqueLogic.ST.SystemLog as Sys
import UniqueLogic.ST.SystemLog (T, Variable)

import qualified Control.Monad.Trans.Writer as MW
import Control.Applicative (liftA, liftA2, )
import Control.Monad.ST (ST, )

import Data.Monoid (Monoid, )


class Var var where
   plainVariable :: var w s a -> Variable w s a
   runApply ::
      Monoid w =>
      Sys.Apply w s a -> var w s a -> T w s ()
   constant :: (Monoid w) => a -> T w s (var w s a)
   localVariable :: (Monoid w) => T w s (var w s a)

instance Var Variable where
   plainVariable = id
   runApply f = Sys.runApply (fmap return f)
   constant = Sys.constant
   localVariable = Sys.localVariable

arg :: (Var var, Monoid w) => var w s a -> Sys.Apply w s a
arg = Sys.arg . plainVariable


assignment2 ::
   (Var var, Monoid w) =>
   (a -> b) ->
   var w s a -> var w s b ->
   T w s ()
assignment2 f a =
   runApply $ liftA f (arg a)

assignment3 ::
   (Var var, Monoid w) =>
   (a -> b -> c) ->
   var w s a -> var w s b -> var w s c ->
   T w s ()
assignment3 f a b =
   runApply $ liftA2 f (arg a) (arg b)


solve :: T () s a -> ST s a
solve = fmap fst . MW.runWriterT . Sys.solve


query :: (Var var, Monoid w) => var w s a -> ST s (Maybe a)
query = Sys.query . plainVariable