-- | The document type, which expresses the intent of the user about
-- what to render.
--
-- We support general layouting \/ formatting, and also constructions
-- specific to mathematical documents (similar to LaTeX).
--
-- TODO: mathemetical accents, compound symbols, 
--       user-defined styles, squiggly underlines, etc
--

{-# LANGUAGE OverloadedStrings, BangPatterns #-}
module Graphics.Rendering.MiniTypeset.Document where

--------------------------------------------------------------------------------

import Data.String

-- import qualified Data.Text as T ; import Data.Text (Text)

import Graphics.Rendering.MiniTypeset.Common
import Graphics.Rendering.MiniTypeset.Box ( WhichQuad(..) )

--------------------------------------------------------------------------------
-- * The document data type

-- | This data type describes what the user want to render.
--
-- The type parameter @ident@ is used when the user want to know positions (bounding boxes) of
-- different parts of the rendered text. It must have an 'Ord' instance.
--
data Document ident
  = Symbol       !Char                                      -- ^ a single character or symbol
  | String       !String                                    -- ^ a string 
  | Space                                                   -- ^ a space character (do we need this to be separate?)
  | HorzCat      !VAlign              [Document ident]      -- ^ horizontal concatenation
  | VertCat      !HAlign              [Document ident]      -- ^ vertical concatenation
  | Overlay      !(HAlign,VAlign)     [Document ident]      -- ^ overlaying on the top of each other
  | SubSupScript !(SubSup ident)     !(Document ident)      -- ^ add subscript and\/or superscript
  | Delimited    !Delimiter           [Document ident]      -- ^ automatically sized (when possible) delimiters
  | Fraction     !Bool !(Document ident) !(Document ident)  -- ^ fractions (the Bool signifies whether to draw the line)
  | AboveBelow   !(AboveBelow ident) !(Document ident)      -- ^ above\/below (like in a summation or limit)
  | WithColor    !Col                !(Document ident)      -- ^ change color
  | WithStyle    !BasicStyle         !(Document ident)      -- ^ change font family
  | Decorated    !TextDecoration     !(Document ident)      -- ^ add text decoration
  | AddMargin    !Margin             !(Document ident)      -- ^ an extra margin around the document fragment
  | Realign      !WhichQuad          !(Document ident)      -- ^ change the alignment box
  | Trim                             !(Document ident)      -- ^ trim the outer \& gap boxes to the bounding box
  | RePosition   !Pos                !(Document ident)      -- ^ offset (mostly for internal usage)
  | UnsafeResize !(Double,Double)    !(Document ident)      -- ^ temporary hack for internal usage, DO NOT USE IT! 
  | Identified   !ident              !(Document ident)      -- ^ user identifier so that the layout engine can return position information
  | EmptyDoc                                                -- ^ the empty document
  deriving (Eq,Ord,Show)

--------------------------------------------------------------------------------

-- | A text decoration
data TextDecoration
  = Underline
  | Overline
  | StrikeThrough
--  | SquigglyUnderline
  deriving (Eq,Ord,Show)

--------------------------------------------------------------------------------

-- | A subscript or a superscript, or both
data SubSup ident
  = Subscript         !(Document ident)
  | Superscript       !(Document ident)
  | SubAndSupscript   !(Document ident) !(Document ident)    -- ^ first is the subscript, second the superscript
  deriving (Eq,Ord,Show)

subSupDocs :: SubSup ident -> (Document ident, Document ident)
subSupDocs subsup = case subsup of
  Subscript         sub     -> (sub      , EmptyDoc)
  Superscript           sup -> (EmptyDoc , sup     )
  SubAndSupscript   sub sup -> (sub      , sup     )

--------------------------------------------------------------------------------

-- | Limits of summations and similar things.
data AboveBelow ident
  = Above         !(Document ident)
  | Below         !(Document ident)
  | AboveAndBelow !(Document ident) !(Document ident)    -- ^ first is the above, second is below
  deriving (Eq,Ord,Show)

aboveBelowDocs :: AboveBelow ident -> (Document ident, Document ident)
aboveBelowDocs abelow = case abelow of
  Above         ab    -> (ab       , EmptyDoc)
  Below            be -> (EmptyDoc , be      )
  AboveAndBelow ab be -> (ab       , be      )

--------------------------------------------------------------------------------
-- * Atomic documents

instance IsString (Document ident) where
  fromString = String

-- text :: Text -> Document a
-- text = String . T.unpack

string :: String -> Document a
string = String

char :: Char -> Document a
char = Symbol

-- | A normal space
space :: Document a
space = Space

-- | A zero-width space (hopefully your chosen unicode font supports it)
zeroWidthSpace :: Document a
zeroWidthSpace = Symbol '\x200B'

--------------------------------------------------------------------------------
-- * Document combinators

-- | Horizontal concatenation
(<|>) :: Document a -> Document a -> Document a
(<|>) x y = hcat [x,y]

-- | Vertical concatenation
(<->) :: Document a -> Document a -> Document a
(<->) x y = vcat [x,y]

-- | Overlay
(<#>) :: Document a -> Document a -> Document a
(<#>) x y = overlay [x,y]

-- | Horizontal concatenation of several document fragments
hcat :: [Document a] -> Document a
hcat = hcatB

-- | Vertical concatenation of several document fragments
vcat :: [Document a] -> Document a
vcat = vcatL

-- | Overlay of several document fragments on top of each other
overlay :: [Document a] -> Document a
overlay = Overlay (AlignLeft,AlignBottom)

hcatB :: [Document a] -> Document a
hcatB = HorzCat AlignBottom

hcatT :: [Document a] -> Document a
hcatT = HorzCat AlignTop

vcatL :: [Document a] -> Document a
vcatL = VertCat AlignLeft

vcatR :: [Document a] -> Document a
vcatR = VertCat AlignRight

-- ** Subscript and superscript

subscript :: Document a -> Document a -> Document a
subscript doc sub = SubSupScript (Subscript sub) doc

supscript :: Document a -> Document a -> Document a
supscript doc sup = SubSupScript (Superscript sup) doc

subSup :: Document a -> (Document a, Document a) -> Document a
subSup doc (sub,sup) = SubSupScript (SubAndSupscript sub sup) doc

-- ** Above and below

-- | Used for \"big\" mathematical operators (like summation)
above :: Document a -> Document a -> Document a
above doc ab = AboveBelow (Above ab) doc

below :: Document a -> Document a -> Document a
below doc be = AboveBelow (Below be) doc

aboveBelow :: Document a -> (Document a, Document a) -> Document a
aboveBelow doc (ab,be) = AboveBelow (AboveAndBelow ab be) doc

-- ** Fractions and delimiters

fraction :: Document a -> Document a -> Document a
fraction = Fraction True

-- | \"n choose k\" notation
choose :: Document a -> Document a -> Document a
choose = choose' Paren

choose' :: Delimiter -> Document a -> Document a -> Document a
choose' delim above below = delimited delim (Fraction False above below)

delimited :: Delimiter -> Document a -> Document a
delimited delim doc = Delimited delim [doc]

-- | Puts a matched vertical line between the parts
delimiteds :: Delimiter -> [Document a] -> Document a
delimiteds delim docs = Delimited delim docs

-- ** Text decoration

underline, overline, strike :: Document a -> Document a
underline = Decorated Underline
overline  = Decorated Overline
strike    = Decorated StrikeThrough

-- ** Font variations

regular, bold, italic, boldItalic :: Document a -> Document a
regular    = WithStyle Regular
bold       = WithStyle Bold
italic     = WithStyle Italic
boldItalic = WithStyle BoldItalic

-- ** Colors

white, black, red, green, blue :: Document a -> Document a
white = WithColor (Col 1 1 1)
black = WithColor (Col 0 0 0)
red   = WithColor (Col 1 0 0)
green = WithColor (Col 0 1 0)
blue  = WithColor (Col 0 0 1)

rgb :: Float -> Float -> Float -> Document a -> Document a
rgb r g b = WithColor (Col r g b)

-- ** Margins

margin :: Double -> Document a -> Document a
margin x = AddMargin (Margin x x x x)

hmargin :: Double -> Document a -> Document a
hmargin x = AddMargin (Margin x x 0 0)

vmargin :: Double -> Document a -> Document a
vmargin x = AddMargin (Margin 0 0 x x)

margin' :: Double -> Double -> Double -> Double -> Document a -> Document a
margin' l r t b = AddMargin (Margin l r t b)

--------------------------------------------------------------------------------