{-# Language TemplateHaskell #-}
-- | Contains types and functions common to aspects of generating documents.
module Language.Drasil.Document.Core where

import Language.Drasil.Chunk.Citation (BibRef)

import Language.Drasil.UID (HasUID(..))
import Language.Drasil.ShortName (HasShortName(shortname))
import Language.Drasil.ModelExpr.Lang (ModelExpr)
import Language.Drasil.CodeExpr.Lang (CodeExpr)
import Language.Drasil.Label.Type (getAdd, prepend, IRefProg,
  LblType(..), Referable(..), HasRefAddress(..))
import Language.Drasil.Reference (Reference)
import Language.Drasil.Sentence (Sentence)

import Control.Lens ((^.), makeLenses, Lens', set, view)

-- * Lists

-- | Denotes the different possible types that can be used as a list.
data ListType = Bullet      [(ItemType, Maybe String)] -- ^ Bulleted list.
              | Numeric     [(ItemType, Maybe String)] -- ^ Enumerated list.
              | Simple      [ListTuple] -- ^ Simple list with items denoted by @:@. Renders as "Title: Item"
              | Desc        [ListTuple] -- ^ Descriptive list, renders as "__Title: Item__" (see 'ListTuple').
              | Definitions [ListTuple] -- ^ Renders a list of "@'Title'@ is the @Item@".

-- | Denotes how something should behave in a list ('ListType').
data ItemType = Flat Sentence -- ^ Standard singular item.
              | Nested Header ListType -- ^ Nest a list ('ListType') as an item.

-- | MaxWidthPercent should be kept in the range 1-100.
-- Values outside this range may have unexpected results.
-- Used for specifying max figure width as
-- @pagewidth*MaxWidthPercent/100@.
type MaxWidthPercent = Float

type Title    = Sentence
type Author   = Sentence
type Header   = Sentence -- ^ Used when creating sublists.
type Depth    = Int
type Width    = Float
type Height   = Float
type ListTuple = (Title, ItemType, Maybe String) -- ^ Formats as Title: Item. For use in lists.
type Filepath = String
type Lbl      = Sentence  -- ^ Label.

-- * Contents

-- | Contents may be labelled or unlabelled.
data Contents = UlC UnlabelledContent
              | LlC LabelledContent

-- For 'Defini' below.  From DocumentLanguage.Definitions
--   tmodel, TM, mkTMField [ Para, EqnBlock, Enumeration]
--   ddefn, DD, mkDDField [Para, EqnBlock, Enumeration]
--   gdefn, General, mkGDField [Para, EqnBlock, Enumeration]
--   instanceModel, Instance, mkIMField [Para, EqnBlock, Enumeration]

-- | Types of definitions (general, instance, theory, or data).
data DType = General
           | Instance
           | Theory
           | Data

-- | Types of layout objects we deal with explicitly.
data RawContent =
    Table [Sentence] [[Sentence]] Title Bool -- ^ table has: header-row, data(rows), label/caption, and a bool that determines whether or not to show label.
  | Paragraph Sentence                       -- ^ Paragraphs are just sentences.
  | EqnBlock ModelExpr                       -- ^ Block of Equations holds an expression.
  | DerivBlock Sentence [RawContent]         -- ^ Grants the ability to label a group of 'RawContent'.
  | Enumeration ListType                     -- ^ For enumerated lists.
  | Defini DType [(Identifier, [Contents])]  -- ^ Defines something with a type, identifier, and 'Contents'.
  | Figure Lbl Filepath MaxWidthPercent      -- ^ For creating figures in a document. Should use relative file path.
  | Bib BibRef                               -- ^ Grants the ability to reference something.
  | Graph [(Sentence, Sentence)] (Maybe Width) (Maybe Height) Lbl -- ^ Contain a graph with coordinates ('Sentence's), maybe a width and height, and a label ('Sentence').
  | CodeBlock CodeExpr                       -- ^ Block for codes
               -- TODO: Fill this one in.

-- | An identifier is just a 'String'.
type Identifier = String

-- | Contains a 'Reference' and 'RawContent'.
data LabelledContent = LblC { LabelledContent -> Reference
_ref :: Reference
                            , LabelledContent -> RawContent
_ctype :: RawContent
                            }

-- | Only contains 'RawContent'.                         
newtype UnlabelledContent = UnlblC { UnlabelledContent -> RawContent
_cntnts :: RawContent }

makeLenses ''LabelledContent
makeLenses ''UnlabelledContent

-- FIXME: this is here temporarily due to import cycles
-- | Members of this class must have 'RawContent'.
class HasContents c where
  -- | Provides a 'Lens' to the 'RawContent'.
  accessContents :: Lens' c RawContent

-- | Finds 'UID' of the 'LabelledContent'.
instance HasUID        LabelledContent where uid :: Lens' LabelledContent UID
uid = Lens' LabelledContent Reference
ref forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. HasUID c => Lens' c UID
uid
-- | 'LabelledContent's are equal if their reference 'UID's are equal.
instance Eq            LabelledContent where LabelledContent
a == :: LabelledContent -> LabelledContent -> Bool
== LabelledContent
b = (LabelledContent
a forall s a. s -> Getting a s a -> a
^. forall c. HasUID c => Lens' c UID
uid) forall a. Eq a => a -> a -> Bool
== (LabelledContent
b forall s a. s -> Getting a s a -> a
^. forall c. HasUID c => Lens' c UID
uid) 
-- | Finds the reference address contained in the 'Reference' of 'LabelledContent'.
instance HasRefAddress LabelledContent where getRefAdd :: LabelledContent -> LblType
getRefAdd (LblC Reference
lb RawContent
c) = IRefProg -> String -> LblType
RP (RawContent -> IRefProg
prependLabel RawContent
c) forall a b. (a -> b) -> a -> b
$ LblType -> String
getAdd forall a b. (a -> b) -> a -> b
$ forall b. HasRefAddress b => b -> LblType
getRefAdd Reference
lb
-- | Access the 'RawContent' within the 'LabelledContent'.
instance HasContents   LabelledContent where accessContents :: Lens' LabelledContent RawContent
accessContents = Lens' LabelledContent RawContent
ctype
-- | Find the shortname of the reference address used for the 'LabelledContent'.
instance HasShortName  LabelledContent where shortname :: LabelledContent -> ShortName
shortname = forall s. HasShortName s => s -> ShortName
shortname forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Lens' LabelledContent Reference
ref

-- | Access the 'RawContent' within the 'UnlabelledContent'.
instance HasContents  UnlabelledContent where accessContents :: Lens' UnlabelledContent RawContent
accessContents = Iso' UnlabelledContent RawContent
cntnts

-- | Access the 'RawContent' within 'Contents'.
instance HasContents Contents where
  accessContents :: Lens' Contents RawContent
accessContents RawContent -> f RawContent
f (UlC UnlabelledContent
c) = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (UnlabelledContent -> Contents
UlC forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\RawContent
x -> forall s t a b. ASetter s t a b -> b -> s -> t
set Iso' UnlabelledContent RawContent
cntnts RawContent
x UnlabelledContent
c)) (RawContent -> f RawContent
f forall a b. (a -> b) -> a -> b
$ UnlabelledContent
c forall s a. s -> Getting a s a -> a
^. Iso' UnlabelledContent RawContent
cntnts)
  accessContents RawContent -> f RawContent
f (LlC LabelledContent
c) = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (LabelledContent -> Contents
LlC forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\RawContent
x -> forall s t a b. ASetter s t a b -> b -> s -> t
set Lens' LabelledContent RawContent
ctype RawContent
x LabelledContent
c)) (RawContent -> f RawContent
f forall a b. (a -> b) -> a -> b
$ LabelledContent
c forall s a. s -> Getting a s a -> a
^. Lens' LabelledContent RawContent
ctype)

-- | Finds the reference information of 'LabelledContent'.
instance Referable LabelledContent where
  refAdd :: LabelledContent -> String
refAdd       = LblType -> String
getAdd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b. HasRefAddress b => b -> LblType
getRefAdd
  renderRef :: LabelledContent -> LblType
renderRef   = forall b. HasRefAddress b => b -> LblType
getRefAdd

-- * Helper

-- | Helper to prepend labels to 'LabelledContent' when referencing.
prependLabel :: RawContent -> IRefProg
prependLabel :: RawContent -> IRefProg
prependLabel Table{}        = String -> IRefProg
prepend String
"Tab"
prependLabel Figure{}       = String -> IRefProg
prepend String
"Fig"
prependLabel Graph{}        = String -> IRefProg
prepend String
"Fig"
prependLabel Defini{}       = String -> IRefProg
prepend String
"Def"
prependLabel EqnBlock{}     = String -> IRefProg
prepend String
"EqnB"
prependLabel CodeBlock{}    = String -> IRefProg
prepend String
"CodeB"
prependLabel DerivBlock{}   = String -> IRefProg
prepend String
"Deriv"
prependLabel Enumeration{}  = String -> IRefProg
prepend String
"Lst"
prependLabel Paragraph{}    = forall a. HasCallStack => String -> a
error String
"Shouldn't reference paragraphs"
prependLabel Bib{}          = forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ 
    String
"Bibliography list of references cannot be referenced. " forall a. [a] -> [a] -> [a]
++
    String
"You must reference the Section or an individual citation."