{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Wikilink syntax preprocessor.
--
--   Applied to the raw Markdown source string /before/ Pandoc parsing.
--   Transforms:
--
--   * @[[Page Title]]@           → @[Page Title](/page-title)@
--   * @[[Page Title|Display]]@   → @[Display](/page-title)@
--
--   The URL slug is derived from the page title: lowercased, spaces
--   replaced with hyphens, non-alphanumeric characters stripped.
--   nginx's @try_files $uri $uri.html@ resolves the extension-free URL.
module Filters.Wikilinks (preprocess) where

import           Data.Char    (isAlphaNum, toLower, isSpace)
import           Data.List    (intercalate)

-- | Scan the raw Markdown source for @[[…]]@ wikilinks and replace them
--   with standard Markdown link syntax.
preprocess :: String -> String
preprocess [] = []
preprocess ('[':'[':rest) =
    case break (== ']') rest of
        (inner, ']':']':after)
            | not (null inner) ->
                toMarkdownLink inner ++ preprocess after
        _ -> '[' : '[' : preprocess rest
preprocess (c:rest) = c : preprocess rest

-- | Convert the inner content of @[[…]]@ to a Markdown link.
toMarkdownLink :: String -> String
toMarkdownLink inner =
    let (title, display) = splitOnPipe inner
        url              = "/" ++ slugify title
    in "[" ++ display ++ "](" ++ url ++ ")"

-- | Split on the first @|@; if none, display = title.
splitOnPipe :: String -> (String, String)
splitOnPipe s =
    case break (== '|') s of
        (title, '|':display) -> (trim title, trim display)
        _                    -> (trim s,     trim s)

-- | Produce a URL slug: lowercase, words joined by hyphens,
--   non-alphanumeric characters removed.
slugify :: String -> String
slugify = intercalate "-" . words . map toLowerAlnum
  where
    toLowerAlnum c
        | isAlphaNum c = toLower c
        | isSpace c    = ' '
        | c == '-'     = '-'
        | otherwise    = ' '   -- replace punctuation with a space so words
                                -- split correctly and double-hyphens are
                                -- collapsed by 'words'

trim :: String -> String
trim = reverse . dropWhile (== ' ') . reverse . dropWhile (== ' ')
