-- | 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 sm :: PrintingInformation
sm (EmptyS :+: b :: Sentence
b)          = PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
b
spec sm :: PrintingInformation
sm (a :: Sentence
a :+: EmptyS)          = PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
a
spec sm :: PrintingInformation
sm (a :: Sentence
a :+: b :: Sentence
b)               = PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
a Spec -> Spec -> Spec
P.:+: PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
b
spec _ (S 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 = ['<', '>', '\"', '&', '#', '$', '%', '&', '~', '^', '\\', '{', '}']
spec _ (Sy s :: USymb
s)                   = Expr -> Spec
P.E (Expr -> Spec) -> Expr -> Spec
forall a b. (a -> b) -> a -> b
$ USymb -> Expr
pUnit USymb
s
spec _ Percent                  = Expr -> Spec
P.E (Expr -> Spec) -> Expr -> Spec
forall a b. (a -> b) -> a -> b
$ Ops -> Expr
P.MO Ops
P.Perc
spec _ (P s :: Symbol
s)                    = Expr -> Spec
P.E (Expr -> Spec) -> Expr -> Spec
forall a b. (a -> b) -> a -> b
$ Symbol -> Expr
symbol Symbol
s
spec sm :: PrintingInformation
sm (SyCh s :: 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 sm :: PrintingInformation
sm (Ch TermStyle caps :: TermCapitalization
caps s :: 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 sm :: PrintingInformation
sm (Ch ShortStyle caps :: TermCapitalization
caps s :: 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 sm :: PrintingInformation
sm (Ch PluralTerm caps :: TermCapitalization
caps s :: 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 sm :: PrintingInformation
sm (Ref u :: UID
u EmptyS notes :: 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 _ (RP rp :: IRefProg
rp ra :: String
ra) sn :: 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 _ (Citation ra :: String
ra) _) ->
      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 _ (URI ra :: String
ra) sn :: 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 sm :: PrintingInformation
sm (Ref u :: UID
u dName :: Sentence
dName notes :: 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 _ (RP _ ra :: String
ra) _) ->
      LinkType -> String -> Spec -> Spec
P.Ref LinkType
P.Internal String
ra (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
dName)
    (Reference _ (Citation ra :: String
ra) _) ->
      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 _ (URI ra :: String
ra) _) ->
      LinkType -> String -> Spec -> Spec
P.Ref LinkType
P.External    String
ra (PrintingInformation -> Sentence -> Spec
spec PrintingInformation
sm Sentence
dName)
spec sm :: PrintingInformation
sm (Quote q :: 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 _  EmptyS             = Spec
P.EmptyS
spec sm :: PrintingInformation
sm (E 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 ctx :: ChunkDB
ctx (Deferred u :: UID
u) _ = 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 "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 ctx :: ChunkDB
ctx (RConcat a :: IRefProg
a b :: IRefProg
b) sn :: 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 _ (RS s :: String
s) _ = String -> Sentence
S String
s
renderShortName _ Name sn :: ShortName
sn = ShortName -> Sentence
getSentSN ShortName
sn

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

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