{-# 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 :: Lens' UnitalChunk UID
uid = Lens' UnitalChunk DefinedQuantityDict
defq' forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. HasUID c => Lens' c UID
uid
-- | Finds term ('NP') of the 'DefinedQuantityDict' used to make the 'UnitalChunk'.
instance NamedIdea     UnitalChunk where term :: Lens' UnitalChunk NP
term = Lens' UnitalChunk DefinedQuantityDict
defq' forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. NamedIdea c => Lens' c 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
_) = 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 = Lens' UnitalChunk DefinedQuantityDict
defq' forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. Definition c => Lens' c Sentence
defn
-- | Finds the domain contained in the 'DefinedQuantityDict' used to make the 'UnitalChunk'.
instance ConceptDomain UnitalChunk where cdom :: UnitalChunk -> [UID]
cdom = forall c. ConceptDomain c => c -> [UID]
cdom forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Lens' UnitalChunk DefinedQuantityDict
defq'
-- | Finds the 'Space' of the 'DefinedQuantityDict' used to make the 'UnitalChunk'.
instance HasSpace      UnitalChunk where typ :: Getter UnitalChunk Space
typ = Lens' UnitalChunk DefinedQuantityDict
defq' forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. HasSpace c => Getter c Space
typ
-- | Finds the 'Symbol' of the 'DefinedQuantityDict' used to make the 'UnitalChunk'.
instance HasSymbol     UnitalChunk where symbol :: UnitalChunk -> Stage -> Symbol
symbol UnitalChunk
c = forall c. HasSymbol c => c -> Stage -> Symbol
symbol (UnitalChunk
cforall s a. s -> Getting a s a -> a
^.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 = forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Lens' UnitalChunk UnitDefn
uni
-- | Finds the units used to make the 'UnitalChunk'.
instance MayHaveUnit   UnitalChunk where getUnit :: UnitalChunk -> Maybe UnitDefn
getUnit = forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Lens' UnitalChunk UnitDefn
uni
-- | Finds the units used to make the 'UnitalChunk'.
instance TempHasUnit       UnitalChunk where findUnit :: UnitalChunk -> UnitDefn
findUnit = forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Lens' UnitalChunk UnitDefn
uni   
-- | Equal if 'UID's are equal.
instance Eq            UnitalChunk where UnitalChunk
c1 == :: UnitalChunk -> UnitalChunk -> Bool
== UnitalChunk
c2 = (UnitalChunk
c1 forall s a. s -> Getting a s a -> a
^. forall c. HasUID c => Lens' c UID
uid) forall a. Eq a => a -> a -> Bool
== (UnitalChunk
c2 forall s a. s -> Getting a s a -> a
^. forall c. HasUID c => Lens' c UID
uid)
-- | Convert the symbol of the 'UnitalChunk' to a 'ModelExpr'.
instance Express       UnitalChunk where express :: UnitalChunk -> ModelExpr
express = 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 (forall u.
IsUnit u =>
ConceptChunk -> Symbol -> Space -> u -> DefinedQuantityDict
dqd (forall c. Concept c => c -> ConceptChunk
cw c
a) Symbol
sym Space
space UnitDefn
un) UnitDefn
un
 where un :: UnitDefn
un = 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 (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 = 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' (forall c. Concept c => c -> ConceptChunk
cw c
a) Stage -> Symbol
sym Space
space (forall a. a -> Maybe a
Just UnitDefn
un)) UnitDefn
un
 where un :: UnitDefn
un = 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 (forall a. a -> Maybe a
Just UnitDefn
un)) UnitDefn
un
 where un :: UnitDefn
un = 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 (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 = forall c.
(Quantity c, Concept c, MayHaveUnit c) =>
c -> UnitDefn -> UnitalChunk
ucuc c
c (forall c. Unitary c => c -> UnitDefn
unit c
c)