-- | Defines helpers for printing 'Sentence's.
module Language.Drasil.Printing.Import.Sentence where

import Language.Drasil hiding (neg, sec, symbol, isIn)
import Database.Drasil (ChunkDB, defResolve, refResolve, refTable)

import qualified Language.Drasil.Printing.AST as P
import Language.Drasil.Printing.PrintingInformation
  (PrintingInformation, ckdb, stg)

import Language.Drasil.Printing.Import.ModelExpr (modelExpr)
import Language.Drasil.Printing.Import.Helpers
  (lookupC, lookupT, lookupS, lookupP)
import Language.Drasil.Printing.Import.Symbol (symbol, pUnit)

import Control.Lens ((^.))
import Data.Maybe (fromMaybe)

-- * Main Function

-- | Translates 'Sentence' to the printable representation of a 'Sentence' ('Spec').
spec :: PrintingInformation -> Sentence -> P.Spec
  -- make sure these optimizations are clear
spec :: PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm (Sentence
EmptyS :+: Sentence
b)          = PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
b
spec PrintingInformation
sm (Sentence
a :+: Sentence
EmptyS)          = PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
a
spec PrintingInformation
sm (Sentence
a :+: Sentence
b)               = PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
a Spec -> Spec -> Spec
P.:+: PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
b
spec PrintingInformation
_ (S String
s)                    = (String -> Spec)
-> (String -> Spec) -> Either String String -> Spec
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either String -> Spec
forall a. HasCallStack => String -> a
error String -> Spec
P.S (Either String String -> Spec) -> Either String String -> Spec
forall a b. (a -> b) -> a -> b
$ String -> String -> Either String String
checkValidStr String
s String
invalidChars
  where invalidChars :: String
invalidChars = [Char
'<', Char
'>', Char
'\"', Char
'&', Char
'$', Char
'%', Char
'&', Char
'~', Char
'^', Char
'\\', Char
'{', Char
'}']
spec PrintingInformation
_ (Sy USymb
s)                   = Expr -> Spec
P.E (Expr -> Spec) -> Expr -> Spec
forall a b. (a -> b) -> a -> b
$ USymb -> Expr
pUnit USymb
s
spec PrintingInformation
_ Sentence
Percent                  = Expr -> Spec
P.E (Expr -> Spec) -> Expr -> Spec
forall a b. (a -> b) -> a -> b
$ Ops -> Expr
P.MO Ops
P.Perc
spec PrintingInformation
_ (P Symbol
s)                    = Expr -> Spec
P.E (Expr -> Spec) -> Expr -> Spec
forall a b. (a -> b) -> a -> b
$ Symbol -> Expr
symbol Symbol
s
spec PrintingInformation
sm (SyCh UID
s)                = Expr -> Spec
P.E (Expr -> Spec) -> Expr -> Spec
forall a b. (a -> b) -> a -> b
$ Symbol -> Expr
symbol (Symbol -> Expr) -> Symbol -> Expr
forall a b. (a -> b) -> a -> b
$ Stage -> ChunkDB -> UID -> Symbol
lookupC (PrintingInformation
sm PrintingInformation
-> Getting Stage PrintingInformation Stage -> Stage
forall s a. s -> Getting a s a -> a
^. Getting Stage PrintingInformation Stage
Lens' PrintingInformation Stage
stg) (PrintingInformation
sm PrintingInformation
-> Getting ChunkDB PrintingInformation ChunkDB -> ChunkDB
forall s a. s -> Getting a s a -> a
^. Getting ChunkDB PrintingInformation ChunkDB
Lens' PrintingInformation ChunkDB
ckdb) UID
s
spec PrintingInformation
sm (Ch SentenceStyle
TermStyle TermCapitalization
caps UID
s)   = PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm (Sentence -> Spec) -> Sentence -> Spec
forall a b. (a -> b) -> a -> b
$ ChunkDB -> UID -> TermCapitalization -> Sentence
lookupT (PrintingInformation
sm PrintingInformation
-> Getting ChunkDB PrintingInformation ChunkDB -> ChunkDB
forall s a. s -> Getting a s a -> a
^. Getting ChunkDB PrintingInformation ChunkDB
Lens' PrintingInformation ChunkDB
ckdb) UID
s TermCapitalization
caps
spec PrintingInformation
sm (Ch SentenceStyle
ShortStyle TermCapitalization
caps UID
s)  = PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm (Sentence -> Spec) -> Sentence -> Spec
forall a b. (a -> b) -> a -> b
$ ChunkDB -> UID -> TermCapitalization -> Sentence
lookupS (PrintingInformation
sm PrintingInformation
-> Getting ChunkDB PrintingInformation ChunkDB -> ChunkDB
forall s a. s -> Getting a s a -> a
^. Getting ChunkDB PrintingInformation ChunkDB
Lens' PrintingInformation ChunkDB
ckdb) UID
s TermCapitalization
caps
spec PrintingInformation
sm (Ch SentenceStyle
PluralTerm TermCapitalization
caps UID
s)  = PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm (Sentence -> Spec) -> Sentence -> Spec
forall a b. (a -> b) -> a -> b
$ ChunkDB -> UID -> TermCapitalization -> Sentence
lookupP (PrintingInformation
sm PrintingInformation
-> Getting ChunkDB PrintingInformation ChunkDB -> ChunkDB
forall s a. s -> Getting a s a -> a
^. Getting ChunkDB PrintingInformation ChunkDB
Lens' PrintingInformation ChunkDB
ckdb) UID
s TermCapitalization
caps
spec PrintingInformation
sm (Ref UID
u Sentence
EmptyS RefInfo
notes) =
  let reff :: Reference
reff = UID -> ReferenceMap -> Reference
refResolve UID
u (PrintingInformation
sm PrintingInformation
-> Getting ReferenceMap PrintingInformation ReferenceMap
-> ReferenceMap
forall s a. s -> Getting a s a -> a
^. (ChunkDB -> Const ReferenceMap ChunkDB)
-> PrintingInformation -> Const ReferenceMap PrintingInformation
Lens' PrintingInformation ChunkDB
ckdb ((ChunkDB -> Const ReferenceMap ChunkDB)
 -> PrintingInformation -> Const ReferenceMap PrintingInformation)
-> ((ReferenceMap -> Const ReferenceMap ReferenceMap)
    -> ChunkDB -> Const ReferenceMap ChunkDB)
-> Getting ReferenceMap PrintingInformation ReferenceMap
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ReferenceMap -> Const ReferenceMap ReferenceMap)
-> ChunkDB -> Const ReferenceMap ChunkDB
Lens' ChunkDB ReferenceMap
refTable) in
  case Reference
reff of 
    (Reference UID
_ (RP IRefProg
rp String
ra) ShortName
sn) ->
      LinkType -> String -> Spec -> Spec
P.Ref LinkType
P.Internal String
ra (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm (Sentence -> Spec) -> Sentence -> Spec
forall a b. (a -> b) -> a -> b
$ ChunkDB -> IRefProg -> ShortName -> Sentence
renderShortName (PrintingInformation
sm PrintingInformation
-> Getting ChunkDB PrintingInformation ChunkDB -> ChunkDB
forall s a. s -> Getting a s a -> a
^. Getting ChunkDB PrintingInformation ChunkDB
Lens' PrintingInformation ChunkDB
ckdb) IRefProg
rp ShortName
sn)
    (Reference UID
_ (Citation String
ra) ShortName
_) ->
      LinkType -> String -> Spec -> Spec
P.Ref (Spec -> LinkType
P.Cite2 (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm (RefInfo -> Sentence
renderCitInfo RefInfo
notes)))    String
ra (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm (Sentence -> Spec) -> Sentence -> Spec
forall a b. (a -> b) -> a -> b
$ String -> Sentence
S String
ra) 
    (Reference UID
_ (URI String
ra) ShortName
sn) ->
      LinkType -> String -> Spec -> Spec
P.Ref LinkType
P.External    String
ra (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm (Sentence -> Spec) -> Sentence -> Spec
forall a b. (a -> b) -> a -> b
$ PrintingInformation -> ShortName -> Sentence
forall ctx. ctx -> ShortName -> Sentence
renderURI PrintingInformation
sm ShortName
sn)
spec PrintingInformation
sm (Ref UID
u Sentence
dName RefInfo
notes) =
  let reff :: Reference
reff = UID -> ReferenceMap -> Reference
refResolve UID
u (PrintingInformation
sm PrintingInformation
-> Getting ReferenceMap PrintingInformation ReferenceMap
-> ReferenceMap
forall s a. s -> Getting a s a -> a
^. (ChunkDB -> Const ReferenceMap ChunkDB)
-> PrintingInformation -> Const ReferenceMap PrintingInformation
Lens' PrintingInformation ChunkDB
ckdb ((ChunkDB -> Const ReferenceMap ChunkDB)
 -> PrintingInformation -> Const ReferenceMap PrintingInformation)
-> ((ReferenceMap -> Const ReferenceMap ReferenceMap)
    -> ChunkDB -> Const ReferenceMap ChunkDB)
-> Getting ReferenceMap PrintingInformation ReferenceMap
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ReferenceMap -> Const ReferenceMap ReferenceMap)
-> ChunkDB -> Const ReferenceMap ChunkDB
Lens' ChunkDB ReferenceMap
refTable) in
  case Reference
reff of 
    (Reference UID
_ (RP IRefProg
_ String
ra) ShortName
_) ->
      LinkType -> String -> Spec -> Spec
P.Ref LinkType
P.Internal String
ra (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
dName)
    (Reference UID
_ (Citation String
ra) ShortName
_) ->
      LinkType -> String -> Spec -> Spec
P.Ref (Spec -> LinkType
P.Cite2 (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm (RefInfo -> Sentence
renderCitInfo RefInfo
notes)))   String
ra (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
dName) 
    (Reference UID
_ (URI String
ra) ShortName
_) ->
      LinkType -> String -> Spec -> Spec
P.Ref LinkType
P.External    String
ra (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
dName)
spec PrintingInformation
sm (Quote Sentence
q)          = Spec -> Spec
P.Quote (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
q
spec PrintingInformation
_  Sentence
EmptyS             = Spec
P.EmptyS
spec PrintingInformation
sm (E ModelExpr
e)              = Expr -> Spec
P.E (Expr -> Spec) -> Expr -> Spec
forall a b. (a -> b) -> a -> b
$ ModelExpr -> PrintingInformation -> Expr
modelExpr ModelExpr
e PrintingInformation
sm

-- * Helpers

-- | Renders the shortname of a reference/domain.
renderShortName :: ChunkDB -> IRefProg -> ShortName -> Sentence
renderShortName :: ChunkDB -> IRefProg -> ShortName -> Sentence
renderShortName ChunkDB
ctx (Deferred UID
u) ShortName
_ = String -> Sentence
S (String -> Sentence) -> String -> Sentence
forall a b. (a -> b) -> a -> b
$ String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe (String -> String
forall a. HasCallStack => String -> a
error String
"Domain has no abbreviation.") (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$
  ConceptChunk -> Maybe String
forall c. Idea c => c -> Maybe String
getA (ConceptChunk -> Maybe String) -> ConceptChunk -> Maybe String
forall a b. (a -> b) -> a -> b
$ ChunkDB -> UID -> ConceptChunk
defResolve ChunkDB
ctx UID
u --Need defResolve instead of refResolve since only ConceptInstance
  -- uses this case for domains and we want the short name from there. 
  -- Used to be: S $ getRefAdd $ refResolve u (ctx ^. refTable)
renderShortName ChunkDB
ctx (RConcat IRefProg
a IRefProg
b) ShortName
sn = ChunkDB -> IRefProg -> ShortName -> Sentence
renderShortName ChunkDB
ctx IRefProg
a ShortName
sn Sentence -> Sentence -> Sentence
:+: ChunkDB -> IRefProg -> ShortName -> Sentence
renderShortName ChunkDB
ctx IRefProg
b ShortName
sn
renderShortName ChunkDB
_ (RS String
s) ShortName
_ = String -> Sentence
S String
s
renderShortName ChunkDB
_ IRefProg
Name ShortName
sn = ShortName -> Sentence
getSentSN ShortName
sn

-- | Render a uniform resource locator as a 'Sentence'.
renderURI :: ctx -> ShortName -> Sentence
renderURI :: forall ctx. ctx -> ShortName -> Sentence
renderURI ctx
_ = ShortName -> Sentence
getSentSN

-- | Renders citation information.
renderCitInfo :: RefInfo -> Sentence
renderCitInfo :: RefInfo -> Sentence
renderCitInfo  RefInfo
None          = Sentence
EmptyS
renderCitInfo (RefNote   String
rn) = Sentence -> Sentence
sParen (String -> Sentence
S String
rn)
renderCitInfo (Equation [Int
x]) = Sentence -> Sentence
sParen (String -> Sentence
S String
"Eq." Sentence -> Sentence -> Sentence
+:+ String -> Sentence
S (Int -> String
forall a. Show a => a -> String
show Int
x))
renderCitInfo (Equation  [Int]
i ) = Sentence -> Sentence
sParen (String -> Sentence
S String
"Eqs." Sentence -> Sentence -> Sentence
+:+ String -> [Int] -> Sentence
foldNums String
"-" [Int]
i)
renderCitInfo (Page     [Int
x]) = Sentence -> Sentence
sParen (String -> Sentence
S String
"pg." Sentence -> Sentence -> Sentence
+:+ String -> Sentence
S (Int -> String
forall a. Show a => a -> String
show Int
x))
renderCitInfo (Page      [Int]
i ) = Sentence -> Sentence
sParen (String -> Sentence
S String
"pp." Sentence -> Sentence -> Sentence
+:+ String -> [Int] -> Sentence
foldNums String
"-" [Int]
i)