module Parsing.ParseInline (inline) where

import Data.Maybe
import Text.Parsec
import qualified Data.Map.Strict as M

import AST
import Parsing.State
import Parsing.Text
import Parsing.TextUtils
import Parsing.Utils
import Parsing.ParseHtml

nestedBold :: Parser Inline
nestedBold = suppressErr (try (string "****")) >> fail "cannot have empty or nested bold nodes"

bold :: Parser Inline
bold = do
    state <- getState
    let currentParserStack = inlineParserStack state
    failIf $ elem "bold" currentParserStack
    s <- betweenWithErrors' "**" "**" "bold"
        $ withModifiedState (many1 inline) $ \s -> s {inlineParserStack=("bold" : currentParserStack)}
    return $ Bold s

-- The bold and italics parsers are tricky because they both use the same special character.
italics :: Parser Inline
italics = do
    state <- getState
    let currentParserStack = inlineParserStack state
    failIf $ elem "italics" currentParserStack
    s <- between
        (try ((char' '*' <?> "\"*\" (italics)") >> suppressErr (notFollowedBy (char '*'))))
        (char' '*' <?> "closing \"*\" (italics)")
        ((withModifiedState (many1 inline) $ \s -> s {inlineParserStack=("italics" : currentParserStack)}) <?>
            if elem "bold" currentParserStack
                then "content in italics node or extra \"*\" to close bold node"
                else "content in italics node")
    return $ Italics s

code :: Parser Inline
code = fmap Code $ betweenWithErrors' "`" "`" "code" $ many1 $ escapableNoneOf "`"

footnoteRef :: Parser Inline
footnoteRef = do
    identifier <- betweenWithErrors' "^[" "]" "footnote reference" $ many1 $ escapableNoneOf "[]"
    state <- getState
    let f = footnoteIndices state
    let maybeIndex = M.lookup identifier f
    if isJust maybeIndex
        then fail $ "repeated footnote identifier: " ++ (show $ fromJust maybeIndex)
        else return ()
    let index = M.size f
    let f' = M.insert identifier index f
    putState $ state {footnoteIndices=f'}
    return $ FootnoteRef index

link :: Parser Inline
link = do
    state <- getState
    let currentParserStack = inlineParserStack state
    lookAhead (char '[' <?> "\"[\" (link text)")
    if elem "link" currentParserStack
        then fail "links cannot be nested"
        else return ()
    text <- betweenWithErrors' "[" "]" "link text" $ withModifiedState (many1 inline) $ \s -> s {inlineParserStack=("link" : currentParserStack)}
    href <- betweenWithErrors' "(" ")" "link href" $ many $ escapableNoneOf "()"
    return $ Link {text=text, href=href}

inline :: Parser Inline
inline = choice [nestedBold, bold, italics, code, footnoteRef, link, fmap InlineHtml html, fmap Plaintext plaintext]