{-# LANGUAGE TemplateHaskell #-}
-- | Defines chunks to add units to a quantity. Similar to 'UnitaryChunk'.
module Language.Drasil.Chunk.Unital (
  -- * Chunk Type
  UnitalChunk(..),
  -- * Constructors
  uc, uc', ucStaged, ucStaged', ucuc, ucw) where

import Control.Lens (makeLenses, view, (^.))

import Language.Drasil.Chunk.Concept (dccWDS,cw)
import Language.Drasil.Chunk.DefinedQuantity (DefinedQuantityDict, dqd, dqd', tempdqdWr')
import Language.Drasil.Chunk.Unitary (Unitary(..))
import Language.Drasil.Symbol
import Language.Drasil.Classes (NamedIdea(term), Idea(getA), Express(express),
  Definition(defn), ConceptDomain(cdom), Concept, IsUnit, Quantity)
import Language.Drasil.Chunk.UnitDefn (MayHaveUnit(getUnit), TempHasUnit(findUnit),  UnitDefn, unitWrapper)
import Language.Drasil.Expr.Class (sy)
import Language.Drasil.NounPhrase.Core (NP)
import Language.Drasil.Space (Space(..), HasSpace(..))
import Language.Drasil.Sentence (Sentence)
import Language.Drasil.Stages (Stage)
import Language.Drasil.UID (HasUID(..))

-- | Similar to a `DefinedQuantityDict`, UnitalChunks are concepts
-- with quantities that must have a unit definition.
-- Contains 'DefinedQuantityDict's and a 'UnitDefn'.
--
-- Ex. A pendulum arm is a tangible object with a symbol (l) and units (cm, m, etc.).
data UnitalChunk = UC { UnitalChunk -> DefinedQuantityDict
_defq' :: DefinedQuantityDict
                      , UnitalChunk -> UnitDefn
_uni :: UnitDefn
                      }
makeLenses ''UnitalChunk

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

--{BEGIN HELPER FUNCTIONS}--

-- | Used to create a 'UnitalChunk' from a 'Concept', 'Symbol', and 'Unit'.
uc :: (Concept c, IsUnit u) => c -> Symbol -> Space -> u -> UnitalChunk
uc :: forall c u.
(Concept c, IsUnit u) =>
c -> Symbol -> Space -> u -> UnitalChunk
uc c
a Symbol
sym Space
space u
c = DefinedQuantityDict -> UnitDefn -> UnitalChunk
UC (ConceptChunk -> Symbol -> Space -> UnitDefn -> DefinedQuantityDict
forall u.
IsUnit u =>
ConceptChunk -> Symbol -> Space -> u -> DefinedQuantityDict
dqd (c -> ConceptChunk
forall c. Concept c => c -> ConceptChunk
cw c
a) Symbol
sym Space
space UnitDefn
un) UnitDefn
un
 where un :: UnitDefn
un = u -> UnitDefn
forall u. IsUnit u => u -> UnitDefn
unitWrapper u
c

-- | Similar to 'uc', except it builds the 'Concept' portion of the 'UnitalChunk'
-- from a given 'UID', term, and definition (as a 'Sentence') which are its first three arguments.
uc' :: (IsUnit u) => String -> NP -> Sentence -> Symbol -> Space -> u -> UnitalChunk
uc' :: forall u.
IsUnit u =>
String -> NP -> Sentence -> Symbol -> Space -> u -> UnitalChunk
uc' String
i NP
t Sentence
d Symbol
sym Space
space u
u = DefinedQuantityDict -> UnitDefn -> UnitalChunk
UC (ConceptChunk -> Symbol -> Space -> UnitDefn -> DefinedQuantityDict
forall u.
IsUnit u =>
ConceptChunk -> Symbol -> Space -> u -> DefinedQuantityDict
dqd (String -> NP -> Sentence -> ConceptChunk
dccWDS String
i NP
t Sentence
d) Symbol
sym Space
space UnitDefn
un) UnitDefn
un
 where un :: UnitDefn
un = u -> UnitDefn
forall u. IsUnit u => u -> UnitDefn
unitWrapper u
u

-- | Similar to 'uc', but 'Symbol' is dependent on the 'Stage'.
ucStaged :: (Concept c, IsUnit u) => c ->  (Stage -> Symbol) ->
  Space -> u -> UnitalChunk
ucStaged :: forall c u.
(Concept c, IsUnit u) =>
c -> (Stage -> Symbol) -> Space -> u -> UnitalChunk
ucStaged c
a Stage -> Symbol
sym Space
space u
u = DefinedQuantityDict -> UnitDefn -> UnitalChunk
UC (ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
dqd' (c -> ConceptChunk
forall c. Concept c => c -> ConceptChunk
cw c
a) Stage -> Symbol
sym Space
space (UnitDefn -> Maybe UnitDefn
forall a. a -> Maybe a
Just UnitDefn
un)) UnitDefn
un
 where un :: UnitDefn
un = u -> UnitDefn
forall u. IsUnit u => u -> UnitDefn
unitWrapper u
u

-- | Similar to 'uc'', but 'Symbol' is dependent on the 'Stage'.
ucStaged' :: (IsUnit u) => String -> NP -> Sentence -> (Stage -> Symbol) ->
  Space -> u -> UnitalChunk
ucStaged' :: forall u.
IsUnit u =>
String
-> NP -> Sentence -> (Stage -> Symbol) -> Space -> u -> UnitalChunk
ucStaged' String
i NP
t Sentence
d Stage -> Symbol
sym Space
space u
u = DefinedQuantityDict -> UnitDefn -> UnitalChunk
UC (ConceptChunk
-> (Stage -> Symbol)
-> Space
-> Maybe UnitDefn
-> DefinedQuantityDict
dqd' (String -> NP -> Sentence -> ConceptChunk
dccWDS String
i NP
t Sentence
d) Stage -> Symbol
sym Space
space (UnitDefn -> Maybe UnitDefn
forall a. a -> Maybe a
Just UnitDefn
un)) UnitDefn
un
 where un :: UnitDefn
un = u -> UnitDefn
forall u. IsUnit u => u -> UnitDefn
unitWrapper u
u

-- | Attach units to a chunk that has a symbol and definition.
ucuc :: (Quantity c, Concept c, MayHaveUnit c) => c -> UnitDefn -> UnitalChunk
ucuc :: forall c.
(Quantity c, Concept c, MayHaveUnit c) =>
c -> UnitDefn -> UnitalChunk
ucuc c
c = DefinedQuantityDict -> UnitDefn -> UnitalChunk
UC (c -> DefinedQuantityDict
forall c.
(Quantity c, Concept c, MayHaveUnit c) =>
c -> DefinedQuantityDict
tempdqdWr' c
c)

-- | Constructs a UnitalChunk from a 'Concept' with 'Units'.
ucw :: (Unitary c, Concept c, MayHaveUnit c) => c -> UnitalChunk
ucw :: forall c. (Unitary c, Concept c, MayHaveUnit c) => c -> UnitalChunk
ucw c
c = c -> UnitDefn -> UnitalChunk
forall c.
(Quantity c, Concept c, MayHaveUnit c) =>
c -> UnitDefn -> UnitalChunk
ucuc c
c (c -> UnitDefn
forall c. Unitary c => c -> UnitDefn
unit c
c)