{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
{-
Copyright (C) 2010-2013 John MacFarlane <jgm@berkeley.edu>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

{- | Functions for writing a parsed formula as a list of Pandoc
     Inlines.
-}

module Text.TeXMath.Writers.Pandoc (writePandoc)
where
import Text.Pandoc.Definition
import qualified Data.Text as T
import Text.TeXMath.Unicode.ToUnicode
import Text.TeXMath.Types
import Text.TeXMath.Shared (getSpaceChars)

-- | Attempts to convert a formula to a list of 'Pandoc' inlines.
writePandoc :: DisplayType
         -> [Exp]
         -> Maybe [Inline]
writePandoc _ exps = expsToInlines TextNormal exps

expsToInlines :: TextType -> [Exp] -> Maybe [Inline]
expsToInlines tt xs = do
  res <- mapM (expToInlines tt) (addSpaces xs)
  return (concat res)

-- This adds spaces around certain symbols, in accord
-- with Appendix G of TeXBook.
addSpaces :: [Exp] -> [Exp]
addSpaces (ESymbol t1 s1 : ESpace r : xs)
  = ESymbol t1 s1 : ESpace r : addSpaces xs
addSpaces (ESymbol t1 s1 : xs)
  | t1 `notElem` [Bin, Op, Rel, Open, Pun]
  = ESymbol t1 s1 : addSpaces xs
addSpaces (ESymbol t1 s1 : ESymbol Pun s2 : xs)
  = ESymbol t1 s1 : ESymbol Pun s2 : addSpaces xs
addSpaces (ESymbol t2 s2 : xs)
  | not (null xs)
  = addSpace t2 (ESymbol t2 s2) ++ addSpaces xs
addSpaces (EMathOperator s : xs) =
  EMathOperator s : thinspace : addSpaces xs
addSpaces (x:xs) = x : addSpaces xs
addSpaces [] = []

addSpace :: TeXSymbolType -> Exp -> [Exp]
addSpace t x =
  case t of
      Bin -> [medspace, x, medspace]
      Rel -> [widespace, x, widespace]
      Pun -> [x, thinspace]
      _   -> [x]

thinspace, medspace, widespace :: Exp
thinspace = EText TextNormal "\x2006"
medspace  = EText TextNormal "\x2005"
widespace = EText TextNormal "\x2004"

renderStr :: TextType -> T.Text -> Inline
renderStr tt s =
  case tt of
       TextNormal       -> Str s
       TextBold         -> Strong [Str s]
       TextItalic       -> Emph   [Str s]
       TextMonospace    -> Code nullAttr s
       TextSansSerif    -> Str s
       TextDoubleStruck -> Str $ toUnicode tt s
       TextScript       -> Str $ toUnicode tt s
       TextFraktur      -> Str $ toUnicode tt s
       TextBoldItalic    -> Strong [Emph [Str s]]
       TextSansSerifBold -> Strong [Str s]
       TextBoldScript    -> Strong [Str $ toUnicode tt s]
       TextBoldFraktur   -> Strong [Str $ toUnicode tt s]
       TextSansSerifItalic -> Emph [Str s]
       TextSansSerifBoldItalic -> Strong [Emph [Str s]]

expToInlines :: TextType -> Exp -> Maybe [Inline]
expToInlines tt (ENumber s) = Just [renderStr tt s]
expToInlines TextNormal (EIdentifier s) = Just [renderStr TextItalic s]
expToInlines tt (EIdentifier s) = Just [renderStr tt s]
expToInlines tt (EMathOperator s) = Just [renderStr tt s]
expToInlines tt (ESymbol _ s) = Just [renderStr tt s]
expToInlines tt (EDelimited start end xs) = do
  xs' <- mapM (either (return . (:[]) . renderStr tt) (expToInlines tt)) xs
  return $ [renderStr tt start] ++ concat xs' ++ [renderStr tt end]
expToInlines tt (EGrouped xs)    = expsToInlines tt xs
expToInlines _ (EStyled tt' xs)  = expsToInlines tt' xs
expToInlines _ (ESpace n)        = Just [Str $ getSpaceChars n]
expToInlines _ (ESqrt _)         = Nothing
expToInlines _ (ERoot _ _)       = Nothing
expToInlines _ (EFraction _ _ _) = Nothing
expToInlines tt (ESub x y) = do
  x' <- expToInlines tt x
  y' <- expToInlines tt y
  return $ x' ++ [Subscript y']
expToInlines tt (ESuper x y) = do
  x' <- expToInlines tt x
  y' <- expToInlines tt y
  return $ x' ++ [Superscript y']
expToInlines tt (ESubsup x y z) = do
  x' <- expToInlines tt x
  y' <- expToInlines tt y
  z' <- expToInlines tt z
  return $ x' ++ [Subscript y'] ++ [Superscript z']
expToInlines _ (EText tt' x) = Just [renderStr tt' x]
expToInlines tt (EOver b (EGrouped [EIdentifier (T.unpack -> [c])]) (ESymbol Accent (T.unpack -> [accent]))) = expToInlines tt (EOver b (EIdentifier $ T.singleton c) (ESymbol Accent $ T.singleton accent))
expToInlines tt (EOver _ (EIdentifier (T.unpack -> [c])) (ESymbol Accent (T.unpack -> [accent]))) =
    case accent of
         '\x203E' -> Just [renderStr tt' $ T.pack [c,'\x0304']]  -- bar
         '\x0304' -> Just [renderStr tt' $ T.pack [c,'\x0304']]  -- bar combining
         '\x00B4' -> Just [renderStr tt' $ T.pack [c,'\x0301']]  -- acute
         '\x0301' -> Just [renderStr tt' $ T.pack [c,'\x0301']]  -- acute combining
         '\x0060' -> Just [renderStr tt' $ T.pack [c,'\x0300']]  -- grave
         '\x0300' -> Just [renderStr tt' $ T.pack [c,'\x0300']]  -- grave combining
         '\x02D8' -> Just [renderStr tt' $ T.pack [c,'\x0306']]  -- breve
         '\x0306' -> Just [renderStr tt' $ T.pack [c,'\x0306']]  -- breve combining
         '\x02C7' -> Just [renderStr tt' $ T.pack [c,'\x030C']]  -- check
         '\x030C' -> Just [renderStr tt' $ T.pack [c,'\x030C']]  -- check combining
         '.'      -> Just [renderStr tt' $ T.pack [c,'\x0307']]  -- dot
         '\x0307' -> Just [renderStr tt' $ T.pack [c,'\x0307']]  -- dot combining
         '\x00B0' -> Just [renderStr tt' $ T.pack [c,'\x030A']]  -- ring
         '\x030A' -> Just [renderStr tt' $ T.pack [c,'\x030A']]  -- ring combining
         '\x20D7' -> Just [renderStr tt' $ T.pack [c,'\x20D7']]  -- arrow right
         '\x20D6' -> Just [renderStr tt' $ T.pack [c,'\x20D6']]  -- arrow left
         '\x005E' -> Just [renderStr tt' $ T.pack [c,'\x0302']]  -- hat
         '\x0302' -> Just [renderStr tt' $ T.pack [c,'\x0302']]  -- hat combining
         '~'      -> Just [renderStr tt' $ T.pack [c,'\x0303']]  -- tilde
         '\x0303' -> Just [renderStr tt' $ T.pack [c,'\x0303']]  -- tilde combining
         _        -> Nothing
      where tt' = if tt == TextNormal then TextItalic else tt
expToInlines tt (EScaled _ e) = expToInlines tt e
expToInlines tt (EUnder convertible b e)
  | convertible = expToInlines tt (ESub b e)
  | otherwise   = Nothing
expToInlines tt (EOver convertible b e)
  | convertible = expToInlines tt (ESuper b e)
  | otherwise   = Nothing
expToInlines _ (EUnderover _ _ _ _) = Nothing
expToInlines _ (EPhantom _) = Nothing
expToInlines _ (EBoxed _) = Nothing
expToInlines _ (EArray _ _) = Nothing