{-
pandoc-crossref is a pandoc filter for numbering figures,
equations, tables and cross-references to them.
Copyright (C) 2015  Nikolay Yakimov <root@livid.pp.ru>

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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-}

{-# LANGUAGE OverloadedStrings #-}
module Text.Pandoc.CrossRef.Util.Template
  ( Template
  , makeTemplate
  , makeIndexedTemplate
  , applyTemplate
  , applyTemplate'
  ) where

import Text.Pandoc.Definition
import Text.Pandoc.Builder
import Text.Pandoc.Generic
import qualified Data.Map as M hiding (toList, fromList, singleton)
import Text.Pandoc.CrossRef.Util.Meta
import Control.Applicative
import Text.Read
import qualified Data.Text as T

type VarFunc = T.Text -> Maybe MetaValue
newtype Template = Template (VarFunc -> [Inline])

makeTemplate :: Meta -> [Inline] -> Template
makeTemplate :: Meta -> [Inline] -> Template
makeTemplate Meta
dtv [Inline]
xs' = (VarFunc -> [Inline]) -> Template
Template ((VarFunc -> [Inline]) -> Template)
-> (VarFunc -> [Inline]) -> Template
forall a b. (a -> b) -> a -> b
$ \VarFunc
vf -> VarFunc -> [Inline] -> [Inline]
scan (\Text
var -> VarFunc
vf Text
var Maybe MetaValue -> Maybe MetaValue -> Maybe MetaValue
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Text -> Meta -> Maybe MetaValue
lookupMeta Text
var Meta
dtv) [Inline]
xs'
  where
  scan :: VarFunc -> [Inline] -> [Inline]
scan = ([Inline] -> [Inline]) -> [Inline] -> [Inline]
forall a b. (Data a, Data b) => (a -> a) -> b -> b
bottomUp (([Inline] -> [Inline]) -> [Inline] -> [Inline])
-> (VarFunc -> [Inline] -> [Inline])
-> VarFunc
-> [Inline]
-> [Inline]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VarFunc -> [Inline] -> [Inline]
go
  go :: VarFunc -> [Inline] -> [Inline]
go VarFunc
vf (x :: Inline
x@(Math MathType
DisplayMath Text
var):[Inline]
xs)
    | (Text
vn, Text
idxBr) <- (Char -> Bool) -> Text -> (Text, Text)
T.span (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=Char
'[') Text
var
    , Bool -> Bool
not (Text -> Bool
T.null Text
idxBr)
    , Text -> Char
T.last Text
idxBr Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
']'
    = let idxVar :: Text
idxVar = Int -> Text -> Text
T.drop Int
1 (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> Text -> Text
T.takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=Char
']') Text
idxBr
          idx :: Maybe Int
idx = String -> Maybe Int
forall a. Read a => String -> Maybe a
readMaybe (String -> Maybe Int)
-> (MetaValue -> String) -> MetaValue -> Maybe Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack (Text -> String) -> (MetaValue -> Text) -> MetaValue -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> MetaValue -> Text
toString (Text
"index variable " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
idxVar) (MetaValue -> Maybe Int) -> Maybe MetaValue -> Maybe Int
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< VarFunc
vf Text
idxVar
          arr :: Maybe MetaValue
arr = do
            Int
i <- Maybe Int
idx
            MetaValue
v <- Text -> Meta -> Maybe MetaValue
lookupMeta Text
vn Meta
dtv
            Int -> MetaValue -> Maybe MetaValue
getList Int
i MetaValue
v
      in Many Inline -> [Inline]
forall a. Many a -> [a]
toList (Many Inline -> [Inline]) -> Many Inline -> [Inline]
forall a b. (a -> b) -> a -> b
$ [Inline] -> Many Inline
forall a. [a] -> Many a
fromList (Text -> Maybe MetaValue -> [Inline] -> [Inline]
replaceVar Text
var Maybe MetaValue
arr [Inline
x]) Many Inline -> Many Inline -> Many Inline
forall a. Semigroup a => a -> a -> a
<> [Inline] -> Many Inline
forall a. [a] -> Many a
fromList [Inline]
xs
    | Bool
otherwise = Many Inline -> [Inline]
forall a. Many a -> [a]
toList (Many Inline -> [Inline]) -> Many Inline -> [Inline]
forall a b. (a -> b) -> a -> b
$ [Inline] -> Many Inline
forall a. [a] -> Many a
fromList (Text -> Maybe MetaValue -> [Inline] -> [Inline]
replaceVar Text
var (VarFunc
vf Text
var) [Inline
x]) Many Inline -> Many Inline -> Many Inline
forall a. Semigroup a => a -> a -> a
<> [Inline] -> Many Inline
forall a. [a] -> Many a
fromList [Inline]
xs
  go VarFunc
_ (Inline
x:[Inline]
xs) = Many Inline -> [Inline]
forall a. Many a -> [a]
toList (Many Inline -> [Inline]) -> Many Inline -> [Inline]
forall a b. (a -> b) -> a -> b
$ Inline -> Many Inline
forall a. a -> Many a
singleton Inline
x Many Inline -> Many Inline -> Many Inline
forall a. Semigroup a => a -> a -> a
<> [Inline] -> Many Inline
forall a. [a] -> Many a
fromList [Inline]
xs
  go VarFunc
_ [] = []
  replaceVar :: Text -> Maybe MetaValue -> [Inline] -> [Inline]
replaceVar Text
var Maybe MetaValue
val [Inline]
def' = [Inline] -> (MetaValue -> [Inline]) -> Maybe MetaValue -> [Inline]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [Inline]
def' (Text -> MetaValue -> [Inline]
toInlines (Text
"variable " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
var)) Maybe MetaValue
val

makeIndexedTemplate :: T.Text -> Meta -> T.Text -> Template
makeIndexedTemplate :: Text -> Meta -> Text -> Template
makeIndexedTemplate Text
name Meta
meta Text
subname =
  Meta -> [Inline] -> Template
makeTemplate Meta
meta ([Inline] -> Template) -> [Inline] -> Template
forall a b. (a -> b) -> a -> b
$ case Text -> Meta -> Maybe MetaValue
lookupMeta Text
name Meta
meta of
    Just (MetaMap Map Text MetaValue
m) -> case Text -> Meta -> Maybe MetaValue
lookupMeta Text
subname (Map Text MetaValue -> Meta
Meta Map Text MetaValue
m) of
      Just MetaValue
x -> Text -> MetaValue -> [Inline]
toInlines Text
name MetaValue
x
      Maybe MetaValue
Nothing -> Text -> Meta -> [Inline]
getMetaInlines Text
"default" (Map Text MetaValue -> Meta
Meta Map Text MetaValue
m)
    Just MetaValue
x -> Text -> MetaValue -> [Inline]
toInlines Text
name MetaValue
x
    Maybe MetaValue
Nothing -> []

applyTemplate' :: M.Map T.Text [Inline] -> Template -> [Inline]
applyTemplate' :: Map Text [Inline] -> Template -> [Inline]
applyTemplate' Map Text [Inline]
vars (Template VarFunc -> [Inline]
g) = VarFunc -> [Inline]
g VarFunc
internalVars
  where
  internalVars :: VarFunc
internalVars Text
x | Just [Inline]
v <- Text -> Map Text [Inline] -> Maybe [Inline]
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup Text
x Map Text [Inline]
vars = MetaValue -> Maybe MetaValue
forall a. a -> Maybe a
Just (MetaValue -> Maybe MetaValue) -> MetaValue -> Maybe MetaValue
forall a b. (a -> b) -> a -> b
$ [Inline] -> MetaValue
MetaInlines [Inline]
v
  internalVars Text
_   = Maybe MetaValue
forall a. Maybe a
Nothing

applyTemplate :: [Inline] -> [Inline] -> Template -> [Inline]
applyTemplate :: [Inline] -> [Inline] -> Template -> [Inline]
applyTemplate [Inline]
i [Inline]
t =
  Map Text [Inline] -> Template -> [Inline]
applyTemplate' ([(Text, [Inline])] -> Map Text [Inline]
forall k a. [(k, a)] -> Map k a
M.fromDistinctAscList [(Text
"i", [Inline]
i), (Text
"t", [Inline]
t)])