{-# LANGUAGE CPP #-}

module Test.Chell.Output
	( Output
	, outputStart
	, outputResult
	
	, ColorMode(..)
	
	, plainOutput
	, colorOutput
	) where

import           Control.Monad (forM_, unless, when)

#ifdef MIN_VERSION_ansi_terminal
import qualified System.Console.ANSI as AnsiTerminal
#endif

import           Test.Chell.Types

data Output = Output
	{ outputStart :: Test -> IO ()
	, outputResult :: Test -> TestResult -> IO ()
	}

plainOutput :: Bool -> Output
plainOutput v = Output
	{ outputStart = plainOutputStart v
	, outputResult = plainOutputResult v
	}

plainOutputStart :: Bool -> Test -> IO ()
plainOutputStart v t = when v $ do
	putStr "[ RUN   ] "
	putStrLn (testName t)

plainOutputResult :: Bool -> Test -> TestResult -> IO ()
plainOutputResult v t (TestPassed _) = when v $ do
	putStr "[ PASS  ] "
	putStrLn (testName t)
	putStrLn ""
plainOutputResult v t TestSkipped = when v $ do
	putStr "[ SKIP  ] "
	putStrLn (testName t)
	putStrLn ""
plainOutputResult _ t (TestFailed notes fs) = do
	putStr "[ FAIL  ] "
	putStrLn (testName t)
	printNotes notes
	printFailures fs
plainOutputResult _ t (TestAborted notes msg) = do
	putStr "[ ABORT ] "
	putStrLn (testName t)
	printNotes notes
	putStr "  "
	putStr msg
	putStrLn "\n"
plainOutputResult _ _ _ = return ()

data ColorMode
	= ColorModeAuto
	| ColorModeAlways
	| ColorModeNever
	deriving (Enum)

colorOutput :: Bool -> Output
#ifndef MIN_VERSION_ansi_terminal
colorOutput = plainOutput
#else
colorOutput v = Output
	{ outputStart = colorOutputStart v
	, outputResult = colorOutputResult v
	}

colorOutputStart :: Bool -> Test -> IO ()
colorOutputStart v t = when v $ do
	putStr "[ RUN   ] "
	putStrLn (testName t)

colorOutputResult :: Bool -> Test -> TestResult -> IO ()
colorOutputResult v t (TestPassed _) = when v $ do
	putStr "[ "
	AnsiTerminal.setSGR
		[ AnsiTerminal.SetColor AnsiTerminal.Foreground AnsiTerminal.Vivid AnsiTerminal.Green
		]
	putStr "PASS"
	AnsiTerminal.setSGR
		[ AnsiTerminal.Reset
		]
	putStr "  ] "
	putStrLn (testName t)
	putStrLn ""
colorOutputResult v t TestSkipped = when v $ do
	putStr "[ "
	AnsiTerminal.setSGR
		[ AnsiTerminal.SetColor AnsiTerminal.Foreground AnsiTerminal.Vivid AnsiTerminal.Yellow
		]
	putStr "SKIP"
	AnsiTerminal.setSGR
		[ AnsiTerminal.Reset
		]
	putStr "  ] "
	putStrLn (testName t)
	putStrLn ""
colorOutputResult _ t (TestFailed notes fs) = do
	putStr "[ "
	AnsiTerminal.setSGR
		[ AnsiTerminal.SetColor AnsiTerminal.Foreground AnsiTerminal.Vivid AnsiTerminal.Red
		]
	putStr "FAIL"
	AnsiTerminal.setSGR
		[ AnsiTerminal.Reset
		]
	putStr "  ] "
	putStrLn (testName t)
	printNotes notes
	printFailures fs
colorOutputResult _ t (TestAborted notes msg) = do
	putStr "[ "
	AnsiTerminal.setSGR
		[ AnsiTerminal.SetColor AnsiTerminal.Foreground AnsiTerminal.Vivid AnsiTerminal.Red
		]
	putStr "ABORT"
	AnsiTerminal.setSGR
		[ AnsiTerminal.Reset
		]
	putStr " ] "
	putStrLn (testName t)
	printNotes notes
	putStr "  "
	putStr msg
	putStrLn "\n"
colorOutputResult _ _ _ = return ()
#endif

printNotes :: [(String, String)] -> IO ()
printNotes notes = unless (null notes) $ do
	forM_ notes $ \(key, value) -> do
		putStr "  note: "
		putStr key
		putStr "="
		putStrLn value
	putStrLn ""

printFailures :: [Failure] -> IO ()
printFailures fs = forM_ fs $ \f -> do
	putStr "  "
	case failureLocation f of
		Just loc -> do
			putStr (locationFile loc)
			putStr ":"
			case locationLine loc of
				Just line -> putStrLn (show line)
				Nothing -> putStrLn ""
		Nothing -> return ()
	putStr "  "
	putStr (failureMessage f)
	putStrLn "\n"