-- | Printing helpers.
module Language.Drasil.Printing.Import.Helpers where

import Language.Drasil (Stage(..), codeSymb, eqSymb, Idea(..),
  NamedIdea(..), NounPhrase(..), Sentence(S), Symbol, UID,
  TermCapitalization(..), titleizeNP, titleizeNP', atStartNP, atStartNP', NP)
import Database.Drasil (ChunkDB, symbResolve, termResolve)

import qualified Language.Drasil.Printing.AST as P

import Control.Lens ((^.))
import Data.Char (toUpper)

-- * Expr-related

-- | Helper for inserting parentheses.
parens :: P.Expr -> P.Expr
parens :: Expr -> Expr
parens = Fence -> Fence -> Expr -> Expr
P.Fenced Fence
P.Paren Fence
P.Paren

-- | Processes the digits from the 'floatToDigits' function,
-- decimal point position, a counter, and exponent.
digitsProcess :: [Integer] -> Int -> Int -> Integer -> [P.Expr]
digitsProcess :: [Integer] -> Int -> Int -> Integer -> [Expr]
digitsProcess [Integer
0] Int
_ Int
_ Integer
_ = [Integer -> Expr
P.Int Integer
0, Ops -> Expr
P.MO Ops
P.Point, Integer -> Expr
P.Int Integer
0]
digitsProcess [Integer]
ds Int
pos Int
_ (-3) = [Integer -> Expr
P.Int Integer
0, Ops -> Expr
P.MO Ops
P.Point] [Expr] -> [Expr] -> [Expr]
forall a. [a] -> [a] -> [a]
++ Int -> Expr -> [Expr]
forall a. Int -> a -> [a]
replicate (Int
3 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
pos) (Integer -> Expr
P.Int Integer
0) [Expr] -> [Expr] -> [Expr]
forall a. [a] -> [a] -> [a]
++ (Integer -> Expr) -> [Integer] -> [Expr]
forall a b. (a -> b) -> [a] -> [b]
map Integer -> Expr
P.Int [Integer]
ds
digitsProcess (Integer
hd:[Integer]
tl) Int
pos Int
coun Integer
ex
  | Int
pos Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
coun = Integer -> Expr
P.Int Integer
hd Expr -> [Expr] -> [Expr]
forall a. a -> [a] -> [a]
: [Integer] -> Int -> Int -> Integer -> [Expr]
digitsProcess [Integer]
tl Int
pos (Int
coun Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Integer
ex
  | Integer
ex Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
0 = [Ops -> Expr
P.MO Ops
P.Point, Integer -> Expr
P.Int Integer
hd] [Expr] -> [Expr] -> [Expr]
forall a. [a] -> [a] -> [a]
++ (Integer -> Expr) -> [Integer] -> [Expr]
forall a b. (a -> b) -> [a] -> [b]
map Integer -> Expr
P.Int [Integer]
tl [Expr] -> [Expr] -> [Expr]
forall a. [a] -> [a] -> [a]
++ [Ops -> Expr
P.MO Ops
P.Dot, Integer -> Expr
P.Int Integer
10, Expr -> Expr
P.Sup (Expr -> Expr) -> Expr -> Expr
forall a b. (a -> b) -> a -> b
$ Integer -> Expr
P.Int Integer
ex]
  | Bool
otherwise = [Ops -> Expr
P.MO Ops
P.Point, Integer -> Expr
P.Int Integer
hd] [Expr] -> [Expr] -> [Expr]
forall a. [a] -> [a] -> [a]
++ (Integer -> Expr) -> [Integer] -> [Expr]
forall a b. (a -> b) -> [a] -> [b]
map Integer -> Expr
P.Int [Integer]
tl
digitsProcess [] Int
pos Int
coun Integer
ex
  | Int
pos Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
coun = Integer -> Expr
P.Int Integer
0 Expr -> [Expr] -> [Expr]
forall a. a -> [a] -> [a]
: [Integer] -> Int -> Int -> Integer -> [Expr]
digitsProcess [] Int
pos (Int
counInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) Integer
ex
  | Integer
ex Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
0 = [Ops -> Expr
P.MO Ops
P.Point, Integer -> Expr
P.Int Integer
0, Ops -> Expr
P.MO Ops
P.Dot, Integer -> Expr
P.Int Integer
10, Expr -> Expr
P.Sup (Expr -> Expr) -> Expr -> Expr
forall a b. (a -> b) -> a -> b
$ Integer -> Expr
P.Int Integer
ex]
  | Bool
otherwise = [Ops -> Expr
P.MO Ops
P.Point, Integer -> Expr
P.Int Integer
0]

-- | Takes the exponent and the 'Int' of the base and gives
-- the decimal point position and processed exponent.
-- This function supports transferring scientific notation to
-- engineering notation.
-- References for standard of Engineering Notation:
--
-- https://www.khanacademy.org/science/electrical-engineering/introduction-to-ee/
--    intro-to-ee/a/ee-numbers-in-electrical-engineering 
--
-- https://www.calculatorsoup.com/calculators/math/scientific-notation-converter.php
--
-- https://en.wikipedia.org/wiki/Scientific_notation
processExpo :: Int -> (Int, Int)
processExpo :: Int -> (Int, Int)
processExpo Int
a
  | Int -> Int -> Int
forall a. Integral a => a -> a -> a
mod (Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) Int
3 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = (Int
1, Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1)
  | Int -> Int -> Int
forall a. Integral a => a -> a -> a
mod (Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) Int
3 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1 = (Int
2, Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
2)
  | Int -> Int -> Int
forall a. Integral a => a -> a -> a
mod (Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) Int
3 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
2 = (Int
3, Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
3)
  | Bool
otherwise = [Char] -> (Int, Int)
forall a. HasCallStack => [Char] -> a
error [Char]
"The cases of processExpo should be exhaustive!"

-- * Lookup/Term Resolution Functions

-- | Given the stage of the symbol, looks up a character/symbol
-- inside a chunk database that matches the given 'UID'. 
lookupC :: Stage -> ChunkDB -> UID -> Symbol
lookupC :: Stage -> ChunkDB -> UID -> Symbol
lookupC Stage
Equational     ChunkDB
sm UID
c = QuantityDict -> Symbol
forall q. HasSymbol q => q -> Symbol
eqSymb   (QuantityDict -> Symbol) -> QuantityDict -> Symbol
forall a b. (a -> b) -> a -> b
$ ChunkDB -> UID -> QuantityDict
symbResolve ChunkDB
sm UID
c
lookupC Stage
Implementation ChunkDB
sm UID
c = QuantityDict -> Symbol
forall q. HasSymbol q => q -> Symbol
codeSymb (QuantityDict -> Symbol) -> QuantityDict -> Symbol
forall a b. (a -> b) -> a -> b
$ ChunkDB -> UID -> QuantityDict
symbResolve ChunkDB
sm UID
c

-- | Look up a term given a chunk database and a 'UID' associated with the term. Also specifies capitalization
lookupT :: ChunkDB -> UID -> TermCapitalization -> Sentence
lookupT :: ChunkDB -> UID -> TermCapitalization -> Sentence
lookupT ChunkDB
sm UID
c TermCapitalization
tCap = TermCapitalization -> NP -> Sentence
resolveCapT TermCapitalization
tCap (NP -> Sentence) -> NP -> Sentence
forall a b. (a -> b) -> a -> b
$ ChunkDB -> UID -> IdeaDict
termResolve ChunkDB
sm UID
c IdeaDict -> Getting NP IdeaDict NP -> NP
forall s a. s -> Getting a s a -> a
^. Getting NP IdeaDict NP
forall c. NamedIdea c => Lens' c NP
Lens' IdeaDict NP
term

-- | Look up the acronym/abbreviation of a term. Otherwise returns the singular form of a term. Takes a chunk database and a 'UID' associated with the term.
lookupS :: ChunkDB -> UID -> TermCapitalization -> Sentence
lookupS :: ChunkDB -> UID -> TermCapitalization -> Sentence
lookupS ChunkDB
sm UID
c TermCapitalization
sCap = Sentence -> ([Char] -> Sentence) -> Maybe [Char] -> Sentence
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (TermCapitalization -> NP -> Sentence
resolveCapT TermCapitalization
sCap (NP -> Sentence) -> NP -> Sentence
forall a b. (a -> b) -> a -> b
$ IdeaDict
l IdeaDict -> Getting NP IdeaDict NP -> NP
forall s a. s -> Getting a s a -> a
^. Getting NP IdeaDict NP
forall c. NamedIdea c => Lens' c NP
Lens' IdeaDict NP
term) [Char] -> Sentence
S (Maybe [Char] -> Sentence) -> Maybe [Char] -> Sentence
forall a b. (a -> b) -> a -> b
$ IdeaDict -> Maybe [Char]
forall c. Idea c => c -> Maybe [Char]
getA IdeaDict
l Maybe [Char] -> ([Char] -> Maybe [Char]) -> Maybe [Char]
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= TermCapitalization -> [Char] -> Maybe [Char]
capHelper TermCapitalization
sCap
  where l :: IdeaDict
l = ChunkDB -> UID -> IdeaDict
termResolve ChunkDB
sm UID
c

-- | Look up the plural form of a term given a chunk database and a 'UID' associated with the term.
lookupP :: ChunkDB -> UID -> TermCapitalization -> Sentence
lookupP :: ChunkDB -> UID -> TermCapitalization -> Sentence
lookupP ChunkDB
sm UID
c TermCapitalization
pCap = TermCapitalization -> NP -> Sentence
resolveCapP TermCapitalization
pCap (NP -> Sentence) -> NP -> Sentence
forall a b. (a -> b) -> a -> b
$ ChunkDB -> UID -> IdeaDict
termResolve ChunkDB
sm UID
c IdeaDict -> Getting NP IdeaDict NP -> NP
forall s a. s -> Getting a s a -> a
^. Getting NP IdeaDict NP
forall c. NamedIdea c => Lens' c NP
Lens' IdeaDict NP
term

-- | Helper to get the proper function for capitalizing a 'NP' based on its 'TermCapitalization'. Singular case.
resolveCapT :: TermCapitalization -> (NP -> Sentence)
resolveCapT :: TermCapitalization -> NP -> Sentence
resolveCapT TermCapitalization
NoCap = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
phraseNP
resolveCapT TermCapitalization
CapF = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
atStartNP
resolveCapT TermCapitalization
CapW = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
titleizeNP

-- | Helper to get the right function for capitalizing a 'NP' based on its 'TermCapitalization'. Plural case.
resolveCapP :: TermCapitalization -> (NP -> Sentence)
resolveCapP :: TermCapitalization -> NP -> Sentence
resolveCapP TermCapitalization
NoCap = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
pluralNP
resolveCapP TermCapitalization
CapF = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
atStartNP'
resolveCapP TermCapitalization
CapW = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
titleizeNP'

-- | Helper to get the capital case of an abbreviation based on 'TermCapitalization'. For sentence and title cases.
capHelper :: TermCapitalization -> String -> Maybe String
capHelper :: TermCapitalization -> [Char] -> Maybe [Char]
capHelper TermCapitalization
NoCap [Char]
s      = [Char] -> Maybe [Char]
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
s
capHelper TermCapitalization
_     []     = Maybe [Char]
forall a. Maybe a
Nothing
capHelper TermCapitalization
_     (Char
x:[Char]
xs) = [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just (Char -> Char
toUpper Char
xChar -> [Char] -> [Char]
forall a. a -> [a] -> [a]
: [Char]
xs)