
module Parse where

import Text.ParserCombinators.Parsec

data WikiText = Template String [(Maybe String, [WikiText])]
              | Link String String
              | Text String
    deriving (Eq, Show)

trim = f . f
    where f = dropWhile (`elem` " \t\n") . reverse

parseLink :: Parser WikiText
parseLink = do
    string "[["
    target <- many1 (noneOf "|]")
    (do char '|'
        title <- many (noneOf "]")
        string "]]"
        return $ Link title target) <|>
        (do string "]]"
            return $ Link target target)

parseTemplate :: Parser WikiText
parseTemplate = do
    string "{{"
    name <- many1 (noneOf "}|")
    args <- parseArgs <|> (return [])
    string "}}"
    return $ Template (trim name) args
    where parseArgs :: Parser [(Maybe String, [WikiText])]
          parseArgs = do
              spaces
              char '|'
              spaces
              arg <- try parseNamedArg <|>
                  (parseArgValue >>= return . (,) Nothing)
              (do args <- parseArgs
                  return (arg : args)) <|>
                  return [arg]
          parseArgValue = parseWiki' "}|"
          parseNamedArg = do
              spaces
              name <- many1 (noneOf "=}|\n")
              char '='
              spaces
              value <- parseArgValue
              return (Just $ trim name, value)

parseWiki' :: String -> Parser [WikiText]
parseWiki' exclude =
  (try $ do special <- (parseLink <|> parseTemplate)
            rest <- parseWiki' exclude
            return (special : rest)) <|>
  (do c <- noneOf exclude
      rest <- parseWiki' exclude
      return $ case rest of
          ((Text s): xs) -> Text (c:s) : xs
          xs -> Text [c] : xs) <|>
  return []

parseWiki :: Parser [WikiText]
parseWiki = parseWiki' ""

