module EnvStatus.Output.Parse where

import Data.Functor (($>))
import Data.List (delete)
import Text.Parsec (many, parse, try)
import Text.Parsec.Char (anyChar, char, noneOf, oneOf, string)
import Text.Parsec.Combinator (choice, eof, manyTill, notFollowedBy)
import Text.Parsec.Prim ((<|>), (<?>))
import Text.Parsec.String (Parser)

import EnvStatus.Output.Types (Token(..))

parseOutputFormat :: String -> [Token]
parseOutputFormat outputFormat =
  case parsed of
      Right matches -> matches
      Left _ -> []
      where
        parsed = parse (manyTill tokenParser eof) "" outputFormat

tokenParser :: Parser Token
tokenParser = choice [
  commandParser
  , rawParser
  ] <?> "token"

singleOpenCurly :: Parser Char
singleOpenCurly = do
  c <- char '{'
  _ <- notFollowedBy $ char '{'
  return c

rawParser :: Parser Token
rawParser = do
  parsed <- many (try singleOpenCurly <|> noneOf ['{'])
  return $ Raw parsed

commandParser :: Parser Token
commandParser = do
  _ <- char '{'
  _ <- char '{'
  parsed <- manyTill anyChar (try $ string "}}")
  return $ SubCommand parsed

outputFormatParser :: Parser [Token]
outputFormatParser =
  manyTill tokenParser eof

-- Parser of commands ~ words but keeping the quoted parts together
quotedOption :: Parser String
quotedOption = do
  _ <- char '"'
  manyTill anyChar (char '"')


separator :: Parser Char
separator = oneOf [' ', '\t', '\n', '\r']

skipSeparator :: Parser ()
skipSeparator = separator $> ()

word :: Parser String
word = do
  _ <- many separator
  manyTill anyChar (try skipSeparator <|> eof)

commandPart :: Parser String
commandPart = try quotedOption <|> word

parseCommand :: String -> [String]
parseCommand cmd =
  case parsed of
      -- Removes the potential last match "" if the command ended with separators
      Right matches -> delete "" matches
      Left _ -> [""]
      where
        parsed = parse (manyTill commandPart eof) "" cmd