{-# LANGUAGE CPP, ScopedTypeVariables #-}

-- |
-- MODULE        : Debug
-- AUTHOR        : George Russell
-- University of Bremen
-- DATE          : 2000
-- DESCRIPTION   : This module provides a uniform interface for debugging
--              purposes.  In final versions of this module it would
--              be best to make the debug function do nothing and
--              force it to be inlined.
--
-- #########################################################################

module Util.Debug(
  debug, -- show something to log file if debugging is turned on.

  debugAct,
  -- If an action fails print out a message before
  -- propagating message.
  (@:),
  -- inline version of debugAct

  -- The following functions work whether debugging is turned on or
  -- not, and are intended to be used when the debugging facility
  -- itself is causing strange effects . . .
  alwaysDebug,
  alwaysDebugAct,

  debugString, -- Send a string to the debug file.  This differs from
     -- debug, in that debug will Haskell-escape the string and add
     -- a newline, while just writes to the file with no interpretation.
  (@@:),


  wrapError, -- :: String -> a -> a
     -- If debugging is on, transforms value so that when evaluated, if
     -- the evaluation calls an error call, the given String is prepended
     -- to the evaluation.
  ) where
import System.IO as IO

import System.IO.Unsafe
import Control.Exception as Exception

import Util.WBFiles

openDebugFile :: IO (Maybe Handle)
openDebugFile =
   do
      debugFileName <- getDebugFileName
      Exception.catch (
         do
             handle <- openFile debugFileName WriteMode
             hSetBuffering handle NoBuffering
             return (Just handle)
         )
         (\ (_ :: IOException) -> return Nothing)

debugFile = unsafePerformIO openDebugFile
debugFile :: Maybe Handle
{-# NOINLINE debugFile #-}

#ifdef DEBUG
debugString s =
               case debugFile of
                  Just f -> IO.hPutStr f s
                  Nothing -> return ()

debug s =
               case debugFile of
                  Just f  -> IO.hPutStrLn f (show s)
                  Nothing -> return ()

debugAct mess act =
               do
                  res <- Exception.try act
                  case res of
                     Left error ->
                        do
                           debug ("Debug.debug caught "++mess)
                           throw (error :: SomeException)
                     Right success -> return success

#else
debugString _ = return ()

debug _ = return ()
{-# inline debug #-}

debugAct _ act = act
{-# inline debugAct #-}
#endif

-- | show something to log file if debugging is turned on.
debug :: Show a => a -> IO()

-- | Send a string to the debug file.  This differs from
-- debug, in that debug will Haskell-escape the string and add
-- a newline, while just writes to the file with no interpretation.
debugString :: String -> IO ()

-- | If an action fails print out a message before
-- propagating message.
debugAct :: String -> IO a -> IO a

(@:) :: String -> IO a -> IO a
(@:) = debugAct


-- | always show something to the log file
alwaysDebug :: Show a => a -> IO()
alwaysDebug s =
   case debugFile of
      Just f  -> IO.hPutStrLn f (show s)
      Nothing -> return ()

-- | always print out a message if action fails.
alwaysDebugAct :: String -> IO a -> IO a
alwaysDebugAct mess act =
   do
      res <- Exception.try act
      case res of
         Left error ->
            do
               alwaysDebug ("AlwaysDebug.debug caught "++mess)
               throw (error :: SomeException)
         Right success -> return success

(@@:) :: String -> IO a -> IO a
(@@:) = alwaysDebugAct

wrapError :: String -> a -> a
#ifdef DEBUG
wrapError str value = unsafePerformIO (wrapErrorIO str value)
#else
wrapError str value = value
#endif

wrapErrorIO :: String -> a -> IO a
wrapErrorIO str value =
   Exception.catch (value `seq` return value)
      (\ mess -> error (str ++ ":"++ show (mess :: ErrorCall)))