{-# Language GADTs #-}
-- | Defines types and functions for constrained values.
module Language.Drasil.Constraint (
  -- * Types
  Constraint(..), ConstraintE, ConstraintReason(..),
  -- * Functions
  physRange, sfwrRange, physElem, sfwrElem, isPhysC, isSfwrC
  ) where

import Language.Drasil.Expr.Lang
import Language.Drasil.Space (RealInterval(..))

-- | The reason behind the constraint's existence.
data ConstraintReason = Physical | Software

-- | Type synonym for 'ConstraintE'
type ConstraintE = Constraint Expr

-- | Holds constraints. May occur between an interval of 'Expr', a list of 'Double's, or a list of 'String's.
data Constraint a where
  -- | By default, physical and software constraints are ranges.
  Range          :: ConstraintReason -> RealInterval a a -> Constraint a

  Elem           :: ConstraintReason -> a -> Constraint a

-- | Smart constructor for range of 'Physical' constraints between two given expressions.
physRange :: RealInterval Expr Expr -> ConstraintE
physRange :: RealInterval Expr Expr -> ConstraintE
physRange = ConstraintReason -> RealInterval Expr Expr -> ConstraintE
forall a. ConstraintReason -> RealInterval a a -> Constraint a
Range ConstraintReason
Physical

physElem :: Expr -> ConstraintE
physElem :: Expr -> ConstraintE
physElem = ConstraintReason -> Expr -> ConstraintE
forall a. ConstraintReason -> a -> Constraint a
Elem ConstraintReason
Physical

-- | Smart constructor for range of 'Software' constraints between two given expressions.
sfwrRange :: RealInterval Expr Expr -> ConstraintE
sfwrRange :: RealInterval Expr Expr -> ConstraintE
sfwrRange = ConstraintReason -> RealInterval Expr Expr -> ConstraintE
forall a. ConstraintReason -> RealInterval a a -> Constraint a
Range ConstraintReason
Software

sfwrElem :: Expr -> ConstraintE
sfwrElem :: Expr -> ConstraintE
sfwrElem = ConstraintReason -> Expr -> ConstraintE
forall a. ConstraintReason -> a -> Constraint a
Elem ConstraintReason
Software

isPhysC, isSfwrC:: Constraint e -> Bool

-- | Helpful for filtering for Physical constraints. True if constraint is 'Physical'.
isPhysC :: forall e. Constraint e -> Bool
isPhysC (Range ConstraintReason
Physical RealInterval e e
_) = Bool
True
isPhysC (Elem ConstraintReason
Physical e
_) = Bool
True
isPhysC Constraint e
_ = Bool
False

-- | Helpful for filtering for Software constraints. True if constraint is 'Software'.
isSfwrC :: forall e. Constraint e -> Bool
isSfwrC (Range ConstraintReason
Software RealInterval e e
_) = Bool
True
isSfwrC (Elem ConstraintReason
Software e
_) = Bool
True
isSfwrC Constraint e
_ = Bool
False