module Text.CSL.Output.Plain
( renderPlain
, renderPlainStrict
, procList
, (<+>)
, (<>)
, capitalize
, entityToChar
, head'
, tail'
) where
import Control.Arrow ( (&&&) )
import Data.Char
import Text.CSL.Style
renderPlain :: [FormattedOutput] -> String
renderPlain = concatMap $ render False
renderPlainStrict :: [FormattedOutput] -> String
renderPlainStrict = concatMap $ render True
render :: Bool -> FormattedOutput -> String
render _ (FPan i) = show i
render _ (FDel s) = s
render b fo
| (FS str fm ) <- fo = prefix fm <++> format fm (trim str ) <++> suffix fm
| (FN str fm ) <- fo = prefix fm <++> format fm (trim str ) <++> suffix fm
| (FUrl t fm ) <- fo = prefix fm <++> format fm (trim $ fst t ) <++> suffix fm
| (FO fm xs) <- fo = prefix fm <++> format fm (trim $ rest xs) <++> suffix fm
| otherwise = []
where
rest xs = procList xs $ concatM (render b)
trim = if b then id else unwords . words
(<++>) = if b then (++) else (<>)
concatM f = foldr (<++>) [] . map f
quote f s = if s /= [] && quotes f /= NoQuote then "\"" ++ s ++ "\"" else s
capital s = toUpper (head s) : (tail s)
format f s = quote f . text_case f $ s
text_case fm s
| "capitalize-first" <- textCase fm = procList s capital
| "capitalize-all" <- textCase fm = procList s $ unwords . map capital . words
| "lowercase" <- textCase fm = map toLower s
| "uppercase" <- textCase fm = map toUpper s
| otherwise = s
procList :: Eq a => [a] -> ([a] -> [b]) -> [b]
procList s f = if s /= [] then f s else []
(<+>) :: String -> String -> String
[] <+> ss = ss
s <+> [] = s
s <+> ss = s ++ " " ++ ss
(<>) :: String -> String -> String
sa <> sb
| sa /= [], (s:xs) <- sb
, last sa == s
, s `elem` ";:,. " = sa ++ xs
| otherwise = sa ++ sb
capitalize :: String -> String
capitalize s = if s /= [] then toUpper (head s) : tail s else []
entityToChar :: String -> String
entityToChar s
| '&':'#':xs <- s = uncurry (:) $ parseEntity xs
| x :xs <- s = x : entityToChar xs
| otherwise = []
where
parseEntity = chr . readNum . takeWhile (/= ';') &&&
entityToChar . tail' . dropWhile (/= ';')
readNum :: String -> Int
readNum ('x': n) = readNum $ "0x" ++ n
readNum n = case readsPrec 1 n of
[(x,[])] -> x
_ -> error $ "Invalid character entity:" ++ n
head' :: [a] -> [a]
head' = foldr (\x _ -> [x]) []
tail' :: Eq a => [a] -> [a]
tail' x = if x /= [] then tail x else []