{-# LANGUAGE CPP #-}
module Data.GraphViz.PreProcessing(preProcess) where
import Data.GraphViz.Exception (GraphvizException (NotDotCode), throw)
import Data.GraphViz.Parsing
import           Data.Text.Lazy         (Text)
import qualified Data.Text.Lazy         as T
import           Data.Text.Lazy.Builder (Builder)
import qualified Data.Text.Lazy.Builder as B
#if !(MIN_VERSION_base(4,8,0))
import Data.Monoid (Monoid (..), mconcat)
#endif
preProcess :: Text -> Text
preProcess t = case fst $ runParser parseOutUnwanted t of
                 (Right r) -> B.toLazyText r
                 (Left l)  -> throw (NotDotCode l)
               
parseOutUnwanted :: Parse Builder
parseOutUnwanted = mconcat <$> many getNext
  where
    getNext = parseOK
              `onFail`
              parseConcatStrings
              `onFail`
              parseHTML
              `onFail`
              parseUnwanted
              `onFail`
              fmap B.singleton next
    parseOK = B.fromLazyText
              <$> many1Satisfy (`notElem` ['\n', '\r', '\\', '/', '"', '<'])
parseUnwanted :: (Monoid m) => Parse m
parseUnwanted = oneOf [ parseLineComment
                      , parseMultiLineComment
                      , parsePreProcessor
                      , parseSplitLine
                      ]
parsePreProcessor :: (Monoid m) => Parse m
parsePreProcessor = newline *> character '#' *> consumeLine *> pure mempty
parseLineComment :: (Monoid m) => Parse m
parseLineComment = string "//"
                   
                   
                   
                   *> consumeLine
                   *> pure mempty
parseMultiLineComment :: (Monoid m) => Parse m
parseMultiLineComment = bracket start end (many inner) *> pure mempty
  where
    start = string "/*"
    end = string "*/"
    inner = (many1Satisfy ('*' /=) *> pure ())
            `onFail`
            (character '*' *> satisfy ('/' /=) *> inner)
parseConcatStrings :: Parse Builder
parseConcatStrings = wrapQuotes . mconcat <$> sepBy1 parseString parseConcat
  where
    qParse = bracket (character '"') (commit $ character '"')
    parseString = qParse (mconcat <$> many parseInner)
    parseInner = (string "\\\"" *> pure (B.fromLazyText $ T.pack "\\\""))
                 `onFail`
                 
                 
                 
                 (string "\\\\" *> pure (B.fromLazyText $ T.pack "\\\\"))
                 `onFail`
                 parseSplitLine 
                 `onFail`
                 fmap B.singleton (satisfy (quoteChar /=))
    parseConcat = parseSep *> character '+' *> parseSep
    parseSep = many $ whitespace1 `onFail` parseUnwanted
    wrapQuotes str = qc `mappend` str `mappend` qc
    qc = B.singleton '"'
parseSplitLine :: (Monoid m) => Parse m
parseSplitLine = character '\\' *> newline *> pure mempty
parseHTML :: Parse Builder
parseHTML = fmap (addAngled . mconcat)
            . parseAngled $ many inner
  where
    inner = parseHTML
            `onFail`
            (B.fromLazyText <$> many1Satisfy (\c -> c /= open && c /= close))
    addAngled str = B.singleton open `mappend` str `mappend` B.singleton close
    open = '<'
    close = '>'