{-# LANGUAGE BangPatterns #-}

module TestHelpers where

import Data.List
import Prelude hiding (catch)
import Control.Exception 
import System.IO.Unsafe (unsafePerformIO)
import Data.IORef
import Data.Time.Clock

import Control.Monad.Par.Class

------------------------------------------------------------
-- Helpers

-- _unsafeio :: IO a -> Par a
_unsafeio :: ParFuture iv p => IO a -> p a
_unsafeio io = let x = unsafePerformIO io in
	        x `seq` return x

_waste_time :: Int -> Double
_waste_time n = loop n 1.00111
  where 
    loop 0  !x             = x
    loop !n !x | x > 100.0 = loop (n-1) (x / 2)
    loop !n !x             = loop (n-1) (x + x * 0.5011)

-- This version watches the clock so it uses a constant amount of time
-- regadless of compile/interpret mode an opt lvl.
waste_time :: Double -> IO Double
waste_time seconds = 
    do strt <- getCurrentTime
       let loop !x | x > 100.0 = chk (x / 2)
	   loop !x             = chk (x + x * 0.5011)
	   chk  !x = do t <- getCurrentTime
			if diffUTCTime t strt >= realToFrac seconds
			 then return x
			 else loop x
       loop  1.00111

-- Obviously this takes a lot longer if it's interpreted:
--awhile = 300000000
awhile :: Integer
awhile = 3 * 1000 * 1000
-- awhile = 300000

atomicModifyIORef_ :: IORef a -> (a -> a) -> IO ()
atomicModifyIORef_ rf fn = atomicModifyIORef rf (\x -> (fn x, ()))


-- | Haskell doesn't offer a way to create a Handle for in-memory output.
--   So here we use IORefs instead...
collectOutput :: (IORef [String] -> IO ()) -> IO String
collectOutput fn = 
  do c <- newIORef []
     fn c
     ls <- readIORef c
     return (unlines (reverse ls))

prnt :: IORef [String] -> String -> IO ()
prnt ref str = atomicModifyIORef_ ref (str:)

-- _prnt :: IORef [String] -> String -> Par ()
_prnt :: ParFuture iv p => IORef [String] -> String -> p ()
_prnt ref = _unsafeio . prnt ref
     


-- -----------------------------------------------------------------------------

-- assertException :: (Exception e, Eq e) => e -> IO a -> IO ()
-- assertException ex action =
--     handleJust isWanted (const $ return ()) $ do
--         action
--         assertFailure $ "Expected exception: " ++ show ex
--   where isWanted = guard . (== ex)

-- | Ensure that evaluating an expression returns an exception
--   containing one of the expected messages.
assertException  :: [String] -> a -> IO ()
assertException msgs val = do
 x <- catch (do evaluate val; return Nothing) 
            (\e -> do putStrLn$ "Good.  Caught exception: " ++ show (e :: SomeException)
                      return (Just$ show e))
 case x of 
  Nothing -> error "Failed to get an exception!"
  Just s -> 
   if  any (`isInfixOf` s) msgs
   then return () 
   else error$ "Got the wrong exception, expected to one of the strings: "++ show msgs
	       ++ "\nInstead got this exception:\n  " ++ show s
     
