{-# LANGUAGE TemplateHaskell #-}
-- | Contains types that define quantities from concepts.
module Language.Drasil.Chunk.DefinedQuantity (
  -- * Chunk Type
  DefinedQuantityDict,
  -- * Type classes
  DefinesQuantity(defLhs),
  -- * Constructors
  dqd, dqdNoUnit, dqdNoUnit', dqd', dqdQd, dqdWr,
  implVar, implVar', implVarAU, implVarAU') where

import Language.Drasil.Symbol (HasSymbol(symbol), Symbol (Empty))
import Language.Drasil.Classes (NamedIdea(term), Idea(getA), Concept, Express(..),
  Definition(defn), ConceptDomain(cdom), IsUnit, Quantity)
import Language.Drasil.Chunk.Concept (ConceptChunk, cw, dcc, dccWDS, dccA, dccAWDS, cc')
import Language.Drasil.Expr.Class (sy)
import Language.Drasil.Chunk.UnitDefn (UnitDefn, unitWrapper,
  MayHaveUnit(getUnit))
import Language.Drasil.Space (Space, HasSpace(..))
import Language.Drasil.Stages (Stage (Implementation, Equational))
import Drasil.Database.UID (HasUID(uid))

import Control.Lens ((^.), makeLenses, view, Getter)
import Language.Drasil.NounPhrase.Core (NP)
import Language.Drasil.Sentence (Sentence)

-- | DefinedQuantityDict is the combination of a 'Concept' and a 'Quantity'.
-- Contains a 'ConceptChunk', a 'Symbol' dependent on 'Stage', a 'Space', and maybe a 'UnitDefn'.
-- Used when we want to assign a quantity to a concept. Includes the space, symbol, and units for that quantity.
--
-- Ex. A pendulum arm can be defined as a concept with a symbol (l), space (Real numbers), and units (cm, m, etc.).
data DefinedQuantityDict = DQD { DefinedQuantityDict -> ConceptChunk
_con :: ConceptChunk
                               , DefinedQuantityDict -> Stage -> Symbol
_symb :: Stage -> Symbol
                               , DefinedQuantityDict -> Space
_spa :: Space
                               , DefinedQuantityDict -> Maybe UnitDefn
_unit' :: Maybe UnitDefn
                               }

makeLenses ''DefinedQuantityDict

class DefinesQuantity d where
  defLhs :: Getter d DefinedQuantityDict

-- | Finds the 'UID' of the 'ConceptChunk' used to make the 'DefinedQuantityDict'.
instance HasUID        DefinedQuantityDict where uid :: Getter DefinedQuantityDict UID
uid = (ConceptChunk -> f ConceptChunk)
-> DefinedQuantityDict -> f DefinedQuantityDict
Lens' DefinedQuantityDict ConceptChunk
con ((ConceptChunk -> f ConceptChunk)
 -> DefinedQuantityDict -> f DefinedQuantityDict)
-> ((UID -> f UID) -> ConceptChunk -> f ConceptChunk)
-> (UID -> f UID)
-> DefinedQuantityDict
-> f DefinedQuantityDict
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UID -> f UID) -> ConceptChunk -> f ConceptChunk
forall c. HasUID c => Getter c UID
Getter ConceptChunk UID
uid
-- | Equal if 'UID's are equal.
instance Eq            DefinedQuantityDict where DefinedQuantityDict
a == :: DefinedQuantityDict -> DefinedQuantityDict -> Bool
== DefinedQuantityDict
b = (DefinedQuantityDict
a DefinedQuantityDict -> Getting UID DefinedQuantityDict UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID DefinedQuantityDict UID
forall c. HasUID c => Getter c UID
Getter DefinedQuantityDict UID
uid) UID -> UID -> Bool
forall a. Eq a => a -> a -> Bool
== (DefinedQuantityDict
b DefinedQuantityDict -> Getting UID DefinedQuantityDict UID -> UID
forall s a. s -> Getting a s a -> a
^. Getting UID DefinedQuantityDict UID
forall c. HasUID c => Getter c UID
Getter DefinedQuantityDict UID
uid)
-- | Finds the term ('NP') of the 'ConceptChunk' used to make the 'DefinedQuantityDict'.
instance NamedIdea     DefinedQuantityDict where term :: Lens' DefinedQuantityDict NP
term = (ConceptChunk -> f ConceptChunk)
-> DefinedQuantityDict -> f DefinedQuantityDict
Lens' DefinedQuantityDict ConceptChunk
con ((ConceptChunk -> f ConceptChunk)
 -> DefinedQuantityDict -> f DefinedQuantityDict)
-> ((NP -> f NP) -> ConceptChunk -> f ConceptChunk)
-> (NP -> f NP)
-> DefinedQuantityDict
-> f DefinedQuantityDict
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NP -> f NP) -> ConceptChunk -> f ConceptChunk
forall c. NamedIdea c => Lens' c NP
Lens' ConceptChunk NP
term
-- | Finds the idea contained in the 'ConceptChunk' used to make the 'DefinedQuantityDict'.
instance Idea          DefinedQuantityDict where getA :: DefinedQuantityDict -> Maybe String
getA = ConceptChunk -> Maybe String
forall c. Idea c => c -> Maybe String
getA (ConceptChunk -> Maybe String)
-> (DefinedQuantityDict -> ConceptChunk)
-> DefinedQuantityDict
-> Maybe String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting ConceptChunk DefinedQuantityDict ConceptChunk
-> DefinedQuantityDict -> ConceptChunk
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting ConceptChunk DefinedQuantityDict ConceptChunk
Lens' DefinedQuantityDict ConceptChunk
con
-- | Finds the definition contained in the 'ConceptChunk' used to make the 'DefinedQuantityDict'.
instance Definition    DefinedQuantityDict where defn :: Lens' DefinedQuantityDict Sentence
defn = (ConceptChunk -> f ConceptChunk)
-> DefinedQuantityDict -> f DefinedQuantityDict
Lens' DefinedQuantityDict ConceptChunk
con ((ConceptChunk -> f ConceptChunk)
 -> DefinedQuantityDict -> f DefinedQuantityDict)
-> ((Sentence -> f Sentence) -> ConceptChunk -> f ConceptChunk)
-> (Sentence -> f Sentence)
-> DefinedQuantityDict
-> f DefinedQuantityDict
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Sentence -> f Sentence) -> ConceptChunk -> f ConceptChunk
forall c. Definition c => Lens' c Sentence
Lens' ConceptChunk Sentence
defn
-- | Finds the domain of the 'ConceptChunk' used to make the 'DefinedQuantityDict'.
instance ConceptDomain DefinedQuantityDict where cdom :: DefinedQuantityDict -> [UID]
cdom = ConceptChunk -> [UID]
forall c. ConceptDomain c => c -> [UID]
cdom (ConceptChunk -> [UID])
-> (DefinedQuantityDict -> ConceptChunk)
-> DefinedQuantityDict
-> [UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting ConceptChunk DefinedQuantityDict ConceptChunk
-> DefinedQuantityDict -> ConceptChunk
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting ConceptChunk DefinedQuantityDict ConceptChunk
Lens' DefinedQuantityDict ConceptChunk
con
-- | Finds the 'Space' of the 'DefinedQuantityDict'.
instance HasSpace      DefinedQuantityDict where typ :: Getter DefinedQuantityDict Space
typ = (Space -> f Space) -> DefinedQuantityDict -> f DefinedQuantityDict
Lens' DefinedQuantityDict Space
spa
-- | Finds the 'Stage' -> 'Symbol' of the 'DefinedQuantityDict'.
instance HasSymbol     DefinedQuantityDict where symbol :: DefinedQuantityDict -> Stage -> Symbol
symbol = Getting (Stage -> Symbol) DefinedQuantityDict (Stage -> Symbol)
-> DefinedQuantityDict -> Stage -> Symbol
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting (Stage -> Symbol) DefinedQuantityDict (Stage -> Symbol)
Lens' DefinedQuantityDict (Stage -> Symbol)
symb
-- | 'DefinedQuantityDict's have a 'Quantity'. 
instance Quantity      DefinedQuantityDict where
-- | Finds the units of the 'DefinedQuantityDict'.
instance MayHaveUnit   DefinedQuantityDict where getUnit :: DefinedQuantityDict -> Maybe UnitDefn
getUnit = Getting (Maybe UnitDefn) DefinedQuantityDict (Maybe UnitDefn)
-> DefinedQuantityDict -> Maybe UnitDefn
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting (Maybe UnitDefn) DefinedQuantityDict (Maybe UnitDefn)
Lens' DefinedQuantityDict (Maybe UnitDefn)
unit'
-- | Convert the symbol of the 'DefinedQuantityDict' to a 'ModelExpr'.
instance Express       DefinedQuantityDict where express :: DefinedQuantityDict -> ModelExpr
express = DefinedQuantityDict -> ModelExpr
forall c. (HasUID c, HasSymbol c) => c -> ModelExpr
forall r c. (ExprC r, HasUID c, HasSymbol c) => c -> r
sy

-- | Smart constructor that creates a DefinedQuantityDict with a 'ConceptChunk', a 'Symbol' independent of 'Stage', a 'Space', and a unit.
dqd :: (IsUnit u) => ConceptChunk -> Symbol -> Space -> u -> DefinedQuantityDict
dqd :: forall u.
IsUnit u =>
ConceptChunk -> Symbol -> Space -> u -> DefinedQuantityDict
dqd ConceptChunk
c Symbol
s Space
sp = ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
DQD ConceptChunk
c (Symbol -> Stage -> Symbol
forall a b. a -> b -> a
const Symbol
s) Space
sp (Maybe UnitDefn -> DefinedQuantityDict)
-> (u -> Maybe UnitDefn) -> u -> DefinedQuantityDict
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UnitDefn -> Maybe UnitDefn
forall a. a -> Maybe a
Just (UnitDefn -> Maybe UnitDefn)
-> (u -> UnitDefn) -> u -> Maybe UnitDefn
forall b c a. (b -> c) -> (a -> b) -> a -> c
. u -> UnitDefn
forall u. IsUnit u => u -> UnitDefn
unitWrapper

-- | Similar to 'dqd', but without any units.
dqdNoUnit :: ConceptChunk -> Symbol -> Space -> DefinedQuantityDict
dqdNoUnit :: ConceptChunk -> Symbol -> Space -> DefinedQuantityDict
dqdNoUnit ConceptChunk
c Symbol
s Space
sp = ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
DQD ConceptChunk
c (Symbol -> Stage -> Symbol
forall a b. a -> b -> a
const Symbol
s) Space
sp Maybe UnitDefn
forall a. Maybe a
Nothing

dqdNoUnit' :: ConceptChunk -> (Stage -> Symbol) -> Space -> DefinedQuantityDict
dqdNoUnit' :: ConceptChunk -> (Stage -> Symbol) -> Space -> DefinedQuantityDict
dqdNoUnit' ConceptChunk
c Stage -> Symbol
s Space
sp = ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
DQD ConceptChunk
c Stage -> Symbol
s Space
sp Maybe UnitDefn
forall a. Maybe a
Nothing

-- | Similar to 'dqd', but the 'Symbol' is now dependent on the 'Stage'.
dqd' :: ConceptChunk -> (Stage -> Symbol) -> Space -> Maybe UnitDefn -> DefinedQuantityDict
dqd' :: ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
dqd' = ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
DQD

-- | When the input already has all the necessary information. A 'projection' operator from some a type with instances of listed classes to a 'DefinedQuantityDict'.
dqdWr :: (Quantity c, Concept c, MayHaveUnit c) => c -> DefinedQuantityDict
dqdWr :: forall c.
(Quantity c, Concept c, MayHaveUnit c) =>
c -> DefinedQuantityDict
dqdWr c
c = ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
DQD (c -> ConceptChunk
forall c. Concept c => c -> ConceptChunk
cw c
c) (c -> Stage -> Symbol
forall c. HasSymbol c => c -> Stage -> Symbol
symbol c
c) (c
c c -> Getting Space c Space -> Space
forall s a. s -> Getting a s a -> a
^. Getting Space c Space
forall c. HasSpace c => Getter c Space
Getter c Space
typ) (c -> Maybe UnitDefn
forall u. MayHaveUnit u => u -> Maybe UnitDefn
getUnit c
c)

-- | When we want to merge a quantity and a concept. This is suspicious.
dqdQd :: (Quantity c, MayHaveUnit c) => c -> Sentence -> DefinedQuantityDict
dqdQd :: forall c.
(Quantity c, MayHaveUnit c) =>
c -> Sentence -> DefinedQuantityDict
dqdQd c
c Sentence
cc = ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
DQD (c -> Sentence -> ConceptChunk
forall c. Idea c => c -> Sentence -> ConceptChunk
cc' c
c Sentence
cc) (c -> Stage -> Symbol
forall c. HasSymbol c => c -> Stage -> Symbol
symbol c
c) (c
c c -> Getting Space c Space -> Space
forall s a. s -> Getting a s a -> a
^. Getting Space c Space
forall c. HasSpace c => Getter c Space
Getter c Space
typ) (c -> Maybe UnitDefn
forall u. MayHaveUnit u => u -> Maybe UnitDefn
getUnit c
c)

-- | Makes a variable that is implementation-only.
implVar :: String -> NP -> String -> Space -> Symbol -> DefinedQuantityDict
implVar :: String -> NP -> String -> Space -> Symbol -> DefinedQuantityDict
implVar String
i NP
ter String
desc Space
sp Symbol
sym = ConceptChunk -> (Stage -> Symbol) -> Space -> DefinedQuantityDict
dqdNoUnit' (String -> NP -> String -> ConceptChunk
dcc String
i NP
ter String
desc) Stage -> Symbol
f Space
sp
  where
    f :: Stage -> Symbol
    f :: Stage -> Symbol
f Stage
Implementation = Symbol
sym
    f Stage
Equational = Symbol
Empty

-- | Similar to 'implVar', but takes in a 'Sentence' for the description rather than a 'String'.
implVar' :: String -> NP -> Sentence -> Space -> Symbol -> DefinedQuantityDict
implVar' :: String -> NP -> Sentence -> Space -> Symbol -> DefinedQuantityDict
implVar' String
i NP
ter Sentence
desc Space
sp Symbol
sym = ConceptChunk -> (Stage -> Symbol) -> Space -> DefinedQuantityDict
dqdNoUnit' (String -> NP -> Sentence -> ConceptChunk
dccWDS String
i NP
ter Sentence
desc) Stage -> Symbol
f Space
sp
  where
    f :: Stage -> Symbol
    f :: Stage -> Symbol
f Stage
Implementation = Symbol
sym
    f Stage
Equational = Symbol
Empty

-- | Similar to 'implVar' but allows specification of abbreviation and unit.
implVarAU :: String -> NP -> String -> Maybe String -> Space -> Symbol -> 
  Maybe UnitDefn -> DefinedQuantityDict
implVarAU :: String
-> NP
-> String
-> Maybe String
-> Space
-> Symbol
-> Maybe UnitDefn
-> DefinedQuantityDict
implVarAU String
s NP
np String
desc Maybe String
a Space
t Symbol
sym = ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
dqd' (String -> NP -> String -> Maybe String -> ConceptChunk
dccA String
s NP
np String
desc Maybe String
a) Stage -> Symbol
f Space
t
  where f :: Stage -> Symbol
        f :: Stage -> Symbol
f Stage
Implementation = Symbol
sym
        f Stage
Equational = Symbol
Empty

-- | Similar to 'implVarAU' but takes a Sentence for the description rather than a String.
implVarAU' :: String -> NP -> Sentence -> Maybe String -> Space -> Symbol -> 
  Maybe UnitDefn -> DefinedQuantityDict
implVarAU' :: String
-> NP
-> Sentence
-> Maybe String
-> Space
-> Symbol
-> Maybe UnitDefn
-> DefinedQuantityDict
implVarAU' String
s NP
np Sentence
desc Maybe String
a Space
t Symbol
sym = ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
dqd' (String -> NP -> Sentence -> Maybe String -> ConceptChunk
dccAWDS String
s NP
np Sentence
desc Maybe String
a) Stage -> Symbol
f Space
t
  where f :: Stage -> Symbol
        f :: Stage -> Symbol
f Stage
Implementation = Symbol
sym
        f Stage
Equational = Symbol
Empty