-- | 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 [0] _ _ _ = [Integer -> Expr
P.Int 0, Ops -> Expr
P.MO Ops
P.Point, Integer -> Expr
P.Int 0]
digitsProcess ds :: [Integer]
ds pos :: Int
pos _ (-3) = [Integer -> Expr
P.Int 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 (3 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
pos) (Integer -> Expr
P.Int 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 (hd :: Integer
hd:tl :: [Integer]
tl) pos :: Int
pos coun :: Int
coun ex :: 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
+ 1) Integer
ex
  | Integer
ex Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= 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 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 [] pos :: Int
pos coun :: Int
coun ex :: Integer
ex
  | Int
pos Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
coun = Integer -> Expr
P.Int 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
+1) Integer
ex
  | Integer
ex Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= 0 = [Ops -> Expr
P.MO Ops
P.Point, Integer -> Expr
P.Int 0, Ops -> Expr
P.MO Ops
P.Dot, Integer -> Expr
P.Int 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 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 a :: Int
a
  | Int -> Int -> Int
forall a. Integral a => a -> a -> a
mod (Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-1) 3 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 0 = (1, Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-1)
  | Int -> Int -> Int
forall a. Integral a => a -> a -> a
mod (Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-1) 3 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 1 = (2, Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-2)
  | Int -> Int -> Int
forall a. Integral a => a -> a -> a
mod (Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-1) 3 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 2 = (3, Int
aInt -> Int -> Int
forall a. Num a => a -> a -> a
-3)
  | Bool
otherwise = [Char] -> (Int, Int)
forall a. HasCallStack => [Char] -> a
error "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 Equational     sm :: ChunkDB
sm c :: 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 Implementation sm :: ChunkDB
sm c :: 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 sm :: ChunkDB
sm c :: UID
c tCap :: 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
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 sm :: ChunkDB
sm c :: UID
c sCap :: 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
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 (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 sm :: ChunkDB
sm c :: UID
c pCap :: 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
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 NoCap = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
phraseNP
resolveCapT CapF = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
atStartNP
resolveCapT 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 NoCap = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
pluralNP
resolveCapP CapF = NP -> Sentence
forall n. NounPhrase n => n -> Sentence
atStartNP'
resolveCapP 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 NoCap s :: [Char]
s      = [Char] -> Maybe [Char]
forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
s
capHelper _     []     = Maybe [Char]
forall a. Maybe a
Nothing
capHelper _     (x :: Char
x:xs :: [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)