{-|
Module      : Text.Ogmarkup.Private.Typography
Copyright   : (c) Ogma Project, 2016
License     : MIT
Stability   : experimental

This module provides the 'Typography' datatype along with two default instances
for French and English.
-}

{-# LANGUAGE OverloadedStrings #-}

module Text.Ogmarkup.Private.Typography where

import           Data.String
import qualified Text.Ogmarkup.Private.Ast as Ast

-- * Inner spaces representation

-- | Deal with typographic spaces, especially when it comes to
--   separating two texts. Because Space derives Ord, it is possible
--   to use min and max to determine which one to use in case of
--   a conflict.
data Space =
  Normal -- ^ A normal space that can be turned into a newline for displaying.
  | Nbsp -- ^ A non breakable space, it cannot be turned into a newline.
  | None -- ^ No space at all.
    deriving (Eq,Ord)

-- * Typography definition

-- | A Typography is a data type that tells the caller what space
--   should be privileged before and after a text.
data Typography a = Typography {
  decide        :: Ast.Mark -> (Space, Space, a), -- ^ For a given 'Ast.Mark',
                                                  --  returns a tuple with the
                                                  --  spaces to use before
                                                  --  and after the
                                                  --  punctuation mark and
                                                  --  its output value.
  openDialogue  :: Bool -> Maybe Ast.Mark,        -- ^ Which mark to use to
                                                  -- open a dialogue. If
                                                  -- the parameter is True,
                                                  -- there were another
                                                  -- dialogue just before.
  closeDialogue :: Bool -> Maybe Ast.Mark         -- ^ Which mark to use to
                                                  -- close a dialogue. If
                                                  -- the parameter is True,
                                                  -- there is another
                                                  -- dialogue just after.
  }

-- | Apply the function to each 'Ast.Mark' output value
instance Functor Typography where
  f `fmap` (Typography d o c) = let d' m = let (s1, s2, x) = d m in (s1, s2, f x)
                                in Typography d' o c

-- | From a Typography, it gives the space to privilege before the
--   input Text.
beforeAtom :: Typography a
           -> Ast.Atom a
           -> Space
beforeAtom t (Ast.Punctuation m) = case decide t m of (r, _, _) -> r
beforeAtom t _ = Normal

-- | From a Typography, it gives the space to privilege after the
--   input Text.
afterAtom :: Typography a
          -> Ast.Atom a
          -> Space
afterAtom t (Ast.Punctuation m) = case decide t m of (_, r, _) -> r
afterAtom t _ = Normal

-- | Normalize the input in order to add it to a generated Text.
normalizeAtom :: Typography a
              -> Ast.Atom a
              -> a
normalizeAtom t (Ast.Punctuation m) = case decide t m of (_, _, r) -> r
normalizeAtom t (Ast.Word w) = w

-- * Ready-to-use Typography

-- | A proposal for the French typography. It can be used with several generation
--   approaches, as it remains very generic. Requires the output type to be an
--   instance of 'IsString'.
frenchTypo :: IsString a => Typography a
frenchTypo = Typography t prevT nextT
  where
    t :: IsString a => Ast.Mark -> (Space, Space, a)
    t Ast.Semicolon = (Nbsp, Normal, ";")
    t Ast.Colon = (Nbsp, Normal, ":")
    t Ast.OpenQuote = (Normal, Nbsp, "«")
    t Ast.CloseQuote = (Nbsp, Normal, "»")
    t Ast.Question = (Nbsp, Normal, "?")
    t Ast.Exclamation = (Nbsp, Normal, "!")
    t Ast.LongDash = (Normal, Normal, "—")
    t Ast.Dash = (None, None, "–")
    t Ast.Hyphen = (None, None, "-")
    t Ast.Comma = (None, Normal, ",")
    t Ast.Point = (None, Normal, ".")
    t Ast.Apostrophe = (None, None, "’")
    t Ast.SuspensionPoints = (None, Normal, "…")

    prevT True = Just Ast.LongDash
    prevT False = Just Ast.OpenQuote

    nextT True = Nothing
    nextT False = Just Ast.CloseQuote

-- | A proposal for the English typography. It can be used with several generation
--   approaches, as it remains very generic. Requires the output type to be an
--   instance of 'IsString'.
englishTypo :: IsString a => Typography a
englishTypo = Typography t (pure $ Just Ast.OpenQuote) (pure $ Just Ast.CloseQuote)
  where
    t :: IsString a => Ast.Mark -> (Space, Space, a)
    t Ast.Semicolon = (None, Normal, ";")
    t Ast.Colon = (None, Normal, ":")
    t Ast.OpenQuote = (Normal, None, "“")
    t Ast.CloseQuote = (None, Normal, "”")
    t Ast.Question = (None, Normal, "?")
    t Ast.Exclamation = (None, Normal, "!")
    t Ast.LongDash = (Normal, None, "—")
    t Ast.Dash = (None, None, "–")
    t Ast.Hyphen = (None, None, "-")
    t Ast.Comma = (None, Normal, ",")
    t Ast.Point = (None, Normal, ".")
    t Ast.Apostrophe = (None, None, "'")
    t Ast.SuspensionPoints = (None, Normal, "…")