module Language.Drasil.Document.Extractors (
  sentToExp, extractMExprs,
  extractSents, extractSents',
  extractChRefs,
  getSec,
  extractSectionsBib,
  resolveBibliography
) where

import Control.Lens ((^.))
import Data.List (sortBy)
import Data.Maybe (mapMaybe)
import qualified Data.Set as S

import Drasil.Database (UID, ChunkDB, find)
import Language.Drasil.Chunk.Citation (Citation, BibRef)
import Language.Drasil.Data.Citation (compareAuthYearTitle)
import Language.Drasil.Document.Core (RawContent(..), ListTuple, ItemType(..),
  ListType(..), HasContents(..))
import Language.Drasil.Document.Sections (Section(Section), SecCons(..))
import Language.Drasil.ModelExpr.Lang (ModelExpr)
import Language.Drasil.Sentence (lnames, Sentence(..), eS, eS')

-- | Extracts all referenced 'UID's from things that have 'RawContent's.
extractChRefs :: HasContents a => [a] -> S.Set UID
extractChRefs :: forall a. HasContents a => [a] -> Set UID
extractChRefs = [Set UID] -> Set UID
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
S.unions ([Set UID] -> Set UID) -> ([a] -> [Set UID]) -> [a] -> Set UID
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Sentence -> Set UID) -> [Sentence] -> [Set UID]
forall a b. (a -> b) -> [a] -> [b]
map Sentence -> Set UID
lnames ([Sentence] -> [Set UID])
-> ([a] -> [Sentence]) -> [a] -> [Set UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> [Sentence]
forall a. HasContents a => [a] -> [Sentence]
extractSents'

-- | Extracts all 'ModelExpr's mentioned in a 'Sentence'.
sentToExp :: Sentence -> [ModelExpr]
sentToExp :: Sentence -> [ModelExpr]
sentToExp ((:+:) Sentence
s1 Sentence
s2) = Sentence -> [ModelExpr]
sentToExp Sentence
s1 [ModelExpr] -> [ModelExpr] -> [ModelExpr]
forall a. [a] -> [a] -> [a]
++ Sentence -> [ModelExpr]
sentToExp Sentence
s2
sentToExp (E ModelExpr
e) = [ModelExpr
e]
sentToExp Ch{} = []
sentToExp SyCh{} = []
sentToExp Sy{} = []
sentToExp NP{} = []
sentToExp S{} = []
sentToExp P{} = []
sentToExp (Ref UID
_ Sentence
s RefInfo
_) = Sentence -> [ModelExpr]
sentToExp Sentence
s
sentToExp (Quote Sentence
s) = Sentence -> [ModelExpr]
sentToExp Sentence
s
sentToExp Sentence
Percent = []
sentToExp Sentence
EmptyS = []

-- | Extracts 'ModelExpr's from something that 'HasContents'.
extractMExprs :: HasContents a => a -> [ModelExpr]
extractMExprs :: forall a. HasContents a => a -> [ModelExpr]
extractMExprs = (Sentence -> [ModelExpr]) -> [Sentence] -> [ModelExpr]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Sentence -> [ModelExpr]
sentToExp ([Sentence] -> [ModelExpr])
-> (a -> [Sentence]) -> a -> [ModelExpr]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> [Sentence]
forall a. HasContents a => a -> [Sentence]
extractSents

-- | Extracts 'Sentence's from something that 'HasContents'.
extractSents :: HasContents a => a -> [Sentence]
extractSents :: forall a. HasContents a => a -> [Sentence]
extractSents = RawContent -> [Sentence]
go (RawContent -> [Sentence]) -> (a -> RawContent) -> a -> [Sentence]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Getting RawContent a RawContent -> RawContent
forall s a. s -> Getting a s a -> a
^. Getting RawContent a RawContent
forall c. HasContents c => Lens' c RawContent
Lens' a RawContent
accessContents)
  where
    -- | Extracts 'Sentence's from 'RawContent'.
    go :: RawContent -> [Sentence]
    go :: RawContent -> [Sentence]
go (Table [Sentence]
s1 [[Sentence]]
s2 Sentence
t Bool
_)   = Sentence
t Sentence -> [Sentence] -> [Sentence]
forall a. a -> [a] -> [a]
: [Sentence]
s1 [Sentence] -> [Sentence] -> [Sentence]
forall a. [a] -> [a] -> [a]
++ [[Sentence]] -> [Sentence]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Sentence]]
s2
    go (Paragraph Sentence
s)       = [Sentence
s]
    go (EqnBlock ModelExpr
e)        = [ModelExpr -> Sentence
eS ModelExpr
e]
    go (CodeBlock Expr
e)       = [Expr -> Sentence
forall t. Express t => t -> Sentence
eS' Expr
e]
    go (DerivBlock Sentence
h [RawContent]
d)    = Sentence
h Sentence -> [Sentence] -> [Sentence]
forall a. a -> [a] -> [a]
: (RawContent -> [Sentence]) -> [RawContent] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap RawContent -> [Sentence]
go [RawContent]
d
    go (Enumeration ListType
lst)   = ListType -> [Sentence]
goList ListType
lst
    go (Figure Sentence
l Filepath
_ MaxWidthPercent
_ HasCaption
_)    = [Sentence
l]
    go (Bib BibRef
_)             = []
    go (Graph [(Sentence, Sentence)]
sss Maybe MaxWidthPercent
_ Maybe MaxWidthPercent
_ Sentence
l)   = let ([Sentence]
ls, [Sentence]
rs) = [(Sentence, Sentence)] -> ([Sentence], [Sentence])
forall a b. [(a, b)] -> ([a], [b])
unzip [(Sentence, Sentence)]
sss
                              in Sentence
l Sentence -> [Sentence] -> [Sentence]
forall a. a -> [a] -> [a]
: [Sentence]
ls [Sentence] -> [Sentence] -> [Sentence]
forall a. [a] -> [a] -> [a]
++ [Sentence]
rs
    go (Defini [(Filepath, [Contents])]
ics)        = ((Filepath, [Contents]) -> [Sentence])
-> [(Filepath, [Contents])] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ((Contents -> [Sentence]) -> [Contents] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Contents -> [Sentence]
forall a. HasContents a => a -> [Sentence]
extractSents ([Contents] -> [Sentence])
-> ((Filepath, [Contents]) -> [Contents])
-> (Filepath, [Contents])
-> [Sentence]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Filepath, [Contents]) -> [Contents]
forall a b. (a, b) -> b
snd) [(Filepath, [Contents])]
ics

    -- | Extracts 'Sentence's from lists.
    goList :: ListType -> [Sentence]
    goList :: ListType -> [Sentence]
goList (Bullet [(ItemType, Maybe Filepath)]
it)      = ((ItemType, Maybe Filepath) -> [Sentence])
-> [(ItemType, Maybe Filepath)] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ItemType -> [Sentence]
goItems (ItemType -> [Sentence])
-> ((ItemType, Maybe Filepath) -> ItemType)
-> (ItemType, Maybe Filepath)
-> [Sentence]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ItemType, Maybe Filepath) -> ItemType
forall a b. (a, b) -> a
fst) [(ItemType, Maybe Filepath)]
it
    goList (Numeric [(ItemType, Maybe Filepath)]
it)     = ((ItemType, Maybe Filepath) -> [Sentence])
-> [(ItemType, Maybe Filepath)] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ItemType -> [Sentence]
goItems (ItemType -> [Sentence])
-> ((ItemType, Maybe Filepath) -> ItemType)
-> (ItemType, Maybe Filepath)
-> [Sentence]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ItemType, Maybe Filepath) -> ItemType
forall a b. (a, b) -> a
fst) [(ItemType, Maybe Filepath)]
it
    goList (Simple [ListTuple]
lp)      = (ListTuple -> [Sentence]) -> [ListTuple] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ListTuple -> [Sentence]
goListTitle [ListTuple]
lp
    goList (Desc [ListTuple]
lp)        = (ListTuple -> [Sentence]) -> [ListTuple] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ListTuple -> [Sentence]
goListTitle [ListTuple]
lp
    goList (Definitions [ListTuple]
lp) = (ListTuple -> [Sentence]) -> [ListTuple] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ListTuple -> [Sentence]
goListTitle [ListTuple]
lp

    -- | Extracts 'Sentence's from list headers.
    goListTitle :: ListTuple -> [Sentence]
    goListTitle :: ListTuple -> [Sentence]
goListTitle (Sentence
t, ItemType
it, Maybe Filepath
_) = Sentence
t Sentence -> [Sentence] -> [Sentence]
forall a. a -> [a] -> [a]
: ItemType -> [Sentence]
goItems ItemType
it

    -- | Extract 'Sentence's from 'ItemType's and their nested 'ListType's.
    goItems :: ItemType -> [Sentence]
    goItems :: ItemType -> [Sentence]
goItems (Flat Sentence
s) = [Sentence
s]
    goItems (Nested Sentence
h ListType
lt) = Sentence
h Sentence -> [Sentence] -> [Sentence]
forall a. a -> [a] -> [a]
: ListType -> [Sentence]
goList ListType
lt

-- | Extracts 'Sentence's from a list of 'Contents'.
extractSents' :: HasContents a => [a] -> [Sentence]
extractSents' :: forall a. HasContents a => [a] -> [Sentence]
extractSents' = (a -> [Sentence]) -> [a] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap a -> [Sentence]
forall a. HasContents a => a -> [Sentence]
extractSents

-- | Extracts 'Sentence's from a 'Section'.
getSec :: Section -> [Sentence]
getSec :: Section -> [Sentence]
getSec (Section Sentence
t [SecCons]
sc Reference
_ ) = Sentence
t Sentence -> [Sentence] -> [Sentence]
forall a. a -> [a] -> [a]
: (SecCons -> [Sentence]) -> [SecCons] -> [Sentence]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap SecCons -> [Sentence]
getSecCon [SecCons]
sc

-- | Extracts 'Sentence's from section contents.
getSecCon :: SecCons -> [Sentence]
getSecCon :: SecCons -> [Sentence]
getSecCon (Sub Section
s) = Section -> [Sentence]
getSec Section
s
getSecCon (Con Contents
c) = Contents -> [Sentence]
forall a. HasContents a => a -> [Sentence]
extractSents Contents
c

-- | Extract bibliography entries from generated sections. This version extracts
-- from fully expanded Sections, capturing citations that are only created
-- during document generation (like those in orgOfDocIntro).
extractSectionsBib :: ChunkDB -> [Section] -> BibRef
extractSectionsBib :: ChunkDB -> [Section] -> BibRef
extractSectionsBib ChunkDB
db = ChunkDB -> Set UID -> BibRef
resolveBibliography ChunkDB
db (Set UID -> BibRef)
-> ([Section] -> Set UID) -> [Section] -> BibRef
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Section] -> Set UID
extractAllSecRefs
  where
    extractAllSecRefs :: [Section] -> Set UID
extractAllSecRefs = [Set UID] -> Set UID
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
S.unions ([Set UID] -> Set UID)
-> ([Section] -> [Set UID]) -> [Section] -> Set UID
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Section -> Set UID) -> [Section] -> [Set UID]
forall a b. (a -> b) -> [a] -> [b]
map ([Set UID] -> Set UID
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
S.unions ([Set UID] -> Set UID)
-> (Section -> [Set UID]) -> Section -> Set UID
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Sentence -> Set UID) -> [Sentence] -> [Set UID]
forall a b. (a -> b) -> [a] -> [b]
map Sentence -> Set UID
lnames ([Sentence] -> [Set UID])
-> (Section -> [Sentence]) -> Section -> [Set UID]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Section -> [Sentence]
getSec)

-- | Given a 'ChunkDB' and a set of 'UID's, looks up the corresponding
-- 'Citation's and returns them sorted by author, year, and title.
--
-- FIXME: This function assumes that all 'UID's in the set correspond to
-- 'Citation's in the database. If a 'UID' does not correspond to a 'Citation',
-- it is simply ignored. This should rather rely on a set of 'UIDRef Citation's.
resolveBibliography :: ChunkDB -> S.Set UID -> [Citation]
resolveBibliography :: ChunkDB -> Set UID -> BibRef
resolveBibliography ChunkDB
db Set UID
uids = (Citation -> Citation -> Ordering) -> BibRef -> BibRef
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy Citation -> Citation -> Ordering
forall c. HasFields c => c -> c -> Ordering
compareAuthYearTitle BibRef
cites
  where
    cites :: BibRef
cites = (UID -> Maybe Citation) -> [UID] -> BibRef
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (UID -> ChunkDB -> Maybe Citation
forall a. Typeable a => UID -> ChunkDB -> Maybe a
`find` ChunkDB
db) (Set UID -> [UID]
forall a. Set a -> [a]
S.toList Set UID
uids)