module Parsing.Text (plaintext) where

import Data.Maybe
import Text.Parsec

import Parsing.State
import Parsing.Utils

specials = "*`<>[]|\\"
specialStrings = ["^[", "!["]
firstCharSpecials = " \t#~+\n" ++ specials

-- Parse a single non-special character, allowing for escaping and continuation.
nonSpecial :: String -> Parser Char
nonSpecial blacklist = do
    escape <- suppressErr (optionMaybe $ char '\\')
    if isJust escape
        then do
            optional $ char '\n'    -- continuation
            anyChar
        else do
            maybeSpecialStrings <- mapM (\s -> suppressErr $ optionMaybe $ lookAhead $ try $ string s) specialStrings
            let specialStringsFound = catMaybes maybeSpecialStrings
            if length specialStringsFound > 0
                then fail $ "special string sequence \"" ++ head specialStringsFound ++ "\""
                else noneOf blacklist

-- Parse one or more non-special characters, allowing for escaping and incorporating the
-- rules for special characters at the start of a line. Note that this does not consume
-- as many non-special characters as possible, for ease of implementation.
nonSpecials :: Parser String
nonSpecials = do
    state <- getState
    str <- if prevCharIsNewline state
        then do
            s <- skipPrefix state
            if null s
                then do
                    c <- nonSpecial firstCharSpecials
                    return [c]
                else return s
        else do
            s <- many $ nonSpecial ('\n' : specials)
            c <- suppressErr (optionMaybe $ char '\n')
            if isJust c
                then return (s ++ "\n")
                else if null s
                    then fail ""    -- Don't succeed without consuming any input.
                    else return s
    putState $ state {prevCharIsNewline=(last str == '\n')}
    return str

-- Parse as many non-special characters as possible.
plaintext :: Parser String
plaintext = fmap concat $ many1 nonSpecials