    {-# LANGUAGE DeriveDataTypeable #-}

    -- |  A module to deal with verbosity, how \'chatty\' a program should be.
    --    This module defines the 'Verbosity' data type, along with functions
    --    for manipulating a global verbosity value.
    module System.Console.CmdArgs.Verbosity(
        Verbosity(..), setVerbosity, getVerbosity,
        isNormal, isLoud,
        whenNormal, whenLoud
        ) where

    import Control.Monad
    import Data.Data
    import Data.IORef
    import System.IO.Unsafe

    -- | The verbosity data type
    data Verbosity
        = Quiet  -- ^ Only output essential messages (typically errors)
        | Normal -- ^ Output normal messages (typically errors and warnings)
        | Loud   -- ^ Output lots of messages (typically errors, warnings and status updates)
          deriving (Eq,Ord,Bounded,Enum,Show,Read,Data,Typeable)

    {-# NOINLINE ref #-}
    ref :: IORef Verbosity
    ref = unsafePerformIO $ newIORef Normal

    -- | Set the global verbosity.
    setVerbosity :: Verbosity -> IO ()
    setVerbosity = writeIORef ref

    -- | Get the global verbosity. Initially @Normal@ before any calls to 'setVerbosity'.
    getVerbosity :: IO Verbosity
    getVerbosity = readIORef ref

    -- | Used to test if warnings should be output to the user.
    --   @True@ if the verbosity is set to 'Normal' or 'Loud' (when @--quiet@ is /not/ specified).
    isNormal :: IO Bool
    isNormal = fmap (>=Normal) getVerbosity

    -- | Used to test if status updates should be output to the user.
    --   @True@ if the verbosity is set to 'Loud' (when @--verbose@ is specified).
    isLoud :: IO Bool
    isLoud = fmap (>=Loud) getVerbosity

    -- | An action to perform if the verbosity is normal or higher, based on 'isNormal'.
    whenNormal :: IO () -> IO ()
    whenNormal act = do
        b <- isNormal
        when b act

    -- | An action to perform if the verbosity is loud, based on 'isLoud'.
    whenLoud :: IO () -> IO ()
    whenLoud act = do
        b <- isLoud
        when b act