-- Print tests in any of the supported formats.
-- Useful for debugging and for migrating between formats.
-- Issues:
--  converting v1 -> v2/v3
--   a >>>= 0 often gets converted to a >>>2 // or >2 //, when >= or nothing would be preferred (but semantically less accurate, therefore risky to choose automatically)
--  converting v3 -> v3
--   loses comments at the top of the file, even above an explicit < delimiter
--   may lose other data

module Print
where

import Safe (lastMay)
import Import
import Types

-- | Print a shell test. See CLI documentation for details.
-- For v3 (the preferred, lightweight format), avoid printing most unnecessary things
-- (stdout delimiter, 0 exit status value).
printShellTest
  :: String               -- ^ Shelltest format. Value of option @--print[=FORMAT]@.
  -> ShellTest            -- ^ Test to print
  -> IO ()
printShellTest format ShellTest{command=c,stdin=i,comments=comments,trailingComments=trailingComments,
               stdoutExpected=o_expected,stderrExpected=e_expected,exitCodeExpected=x_expected}
               = do
  case format of
    "v1" -> do
      printComments comments
      printCommand "" c
      printStdin "<<<" i
      printStdouterr False ">>>" o_expected
      printStdouterr False ">>>2" e_expected
      printExitStatus True True ">>>=" x_expected
      printComments trailingComments
    "v2" -> do
      printComments comments
      printStdin "<<<" i
      printCommand "$$$ " c
      printStdouterr True ">>>" o_expected
      printStdouterr True ">>>2" e_expected
      printExitStatus trailingblanklines True ">>>=" x_expected
      printComments trailingComments
    "v3" -> do
      printComments comments
      printStdin "<" i
      printCommand "$ "  c
      printStdouterr True ">" o_expected
      printStdouterr True ">2" e_expected
      printExitStatus trailingblanklines False ">=" x_expected
      printComments trailingComments
    _ -> fail $ "Unsupported --print format: " ++ format
  where
    trailingblanklines = case (o_expected, e_expected) of
      (Just (Lines _ o), Just (Lines _ e)) -> hasblanks $ if null e then o else e
      _ -> False
      where hasblanks s = maybe False null $ lastMay $ lines s

printComments :: [String] -> IO ()
printComments = mapM_ putStrLn

printStdin :: String -> Maybe String -> IO ()
printStdin _ (Just "") = return ()
printStdin _ Nothing = return ()
printStdin prefix (Just s) = printf "%s\n%s" prefix s

printCommand :: String -> TestCommand -> IO ()
printCommand prefix (ReplaceableCommand s) = printf "%s%s\n" prefix s
printCommand prefix (FixedCommand s)       = printf "%s %s\n" prefix s

-- Print an expected stdout or stderr test, prefixed with the given delimiter.
-- If no expected value is specified, print nothing if first argument is true
-- (for format 1, which ignores unspecified out/err), otherwise print a dummy test.
printStdouterr :: Bool -> String -> Maybe Matcher -> IO ()
printStdouterr alwaystest prefix Nothing                 = when alwaystest $ printf "%s //\n" prefix
printStdouterr _ _ (Just (Lines _ ""))                   = return ()
printStdouterr _ _ (Just (Numeric _))                    = fail "FATAL: Cannot handle Matcher (Numeric) for stdout/stderr."
printStdouterr _ _ (Just (NegativeNumeric _))            = fail "FATAL: Cannot handle Matcher (NegativeNumeric) for stdout/stderr."
printStdouterr _ prefix (Just (Lines _ s)) | prefix==">" = printf "%s" s  -- omit v3's > delimiter, really no need for it
printStdouterr _ prefix (Just (Lines _ s))               = printf "%s\n%s" prefix s
printStdouterr _ prefix (Just regex)                     = printf "%s %s\n" prefix (show regex)

-- | Print an expected exit status clause, prefixed with the given delimiter.
-- If zero is expected:
--  if the first argument is not true, nothing will be printed;
--  otherwise if the second argument is not true, only the delimiter will be printed.
printExitStatus :: Bool -> Bool ->  String -> Matcher ->      IO ()
printExitStatus    _       _        _         (Lines _ _)   = fail "FATAL: Cannot handle Matcher (Lines) for exit status."
printExitStatus    always  showzero prefix    (Numeric "0") = when always $ printf "%s %s\n" prefix (if showzero then "0" else "")
printExitStatus    _       _        prefix    s             = printf "%s %s\n" prefix (show s)
