{-# LANGUAGE BangPatterns      #-}
{-# LANGUAGE OverloadedStrings #-}

module System.IO.Streams.Tests.Internal (tests) where

------------------------------------------------------------------------------
import           Control.Applicative
import           Control.Monad                  hiding (mapM)
import           Control.Monad.IO.Class         (liftIO)
import           Data.IORef
import           Prelude                        hiding (mapM, read)
import           Test.Framework
import           Test.Framework.Providers.HUnit
import           Test.HUnit                     hiding (Test)
------------------------------------------------------------------------------
import           System.IO.Streams.Internal
import           System.IO.Streams.List
import           System.IO.Streams.Tests.Common

tests :: [Test]
tests = [ testAppendInput
        , testConst
        , testCoverLockingStream
        , testPeek
        , testNullInput
        , testGenerator
        , testGeneratorInstances
        , testConsumer
        , testTrivials
        ]


------------------------------------------------------------------------------
testAppendInput :: Test
testAppendInput = testCase "internal/appendInputStream" $ do
    s1 <- fromList [1::Int, 2, 3]
    s2 <- fromList [5, 6, 7]

    is <- appendInputStream s1 s2
    l  <- toList is

    assertEqual "appendInputStream" [1,2,3,5,6,7] l


------------------------------------------------------------------------------
testConst :: Test
testConst = testCase "internal/const" $ do
    is <- makeInputStream (return (Just (1::Int)))
    read is >>= assertEqual "const" (Just 1)

    unRead 7 is
    read is >>= assertEqual "unRead" (Just 7)
    read is >>= assertEqual "const2" (Just 1)


------------------------------------------------------------------------------
testNullInput :: Test
testNullInput = testCase "internal/nullInput" $ do
    is <- nullInput
    xs <- replicateM 10 $ read (is :: InputStream Int)
    assertEqual "nullInput" (replicate 10 Nothing) xs


------------------------------------------------------------------------------
testCoverLockingStream :: Test
testCoverLockingStream = testCase "internal/coverLockingStreams" $ do
    is <- fromList [1::Int, 2, 3] >>= lockingInputStream
    (os0, grab) <- listOutputStream
    os <- lockingOutputStream os0

    connect is os
    xs <- grab

    assertEqual "lockingStreams" [1,2,3] xs

    write Nothing os
    write Nothing os

    unRead 7 is
    y <- read is
    assertEqual "unRead" (Just 7) y


------------------------------------------------------------------------------
testPeek :: Test
testPeek = testCase "internal/peek" $ do
    is <- fromList [1::Int, 2, 3]
    b   <- atEOF is
    assertEqual "eof1" False b

    x0  <- peek is
    x1  <- peek is

    unRead 7 is
    x2  <- peek is

    assertEqual "peek" (map Just [1, 1, 7]) [x0, x1, x2]

    l   <- toList is
    assertEqual "toList" [7, 1, 2, 3] l

    z   <- peek is
    assertEqual "peekEOF" Nothing z

    b'  <- atEOF is
    assertEqual "eof2" True b'


------------------------------------------------------------------------------
testGenerator :: Test
testGenerator = testCase "internal/generator" $ do
    is <- fromGenerator $ sequence $
          Prelude.map ((>>= yield) . (liftIO . return)) [1..5::Int]
    toList is >>= assertEqual "generator" [1..5]
    read is >>= assertEqual "read after EOF" Nothing


------------------------------------------------------------------------------
testGeneratorInstances :: Test
testGeneratorInstances = testCase "internal/generatorInstances" $ do
    fromGenerator g1 >>= toList
                     >>= assertEqual "generator" [2,4..10]

    fromGenerator g2 >>= toList
                     >>= assertEqual "generator" [2,4..10]

  where
    g1 = do
        l <- fmap (map (*2)) $ return [1..5::Int]
        fmap id $ sequence_ $ Prelude.map yield l

    g2 = pure id <*> g1


------------------------------------------------------------------------------
testConsumer :: Test
testConsumer = testCase "internal/consumer" $ do
    is  <- fromList [1..10::Int]
    ref <- newIORef 0
    os  <- fromConsumer (fmap id (pure id <*> c ref))
    connect is os
    readIORef ref >>= assertEqual "sum" (sum [1..10])

    -- should be nullsink after receiving Nothing
    write (Just 2) os
    readIORef ref >>= assertEqual "sum" (sum [1..10])

    is2 <- fromList [1..10::Int]
    os2 <- fromConsumer (return ())
    connect is2 os2

  where
    c ref = await >>= maybe (return ())
                            (\x -> do
                                 !t <- liftIO $ readIORef ref
                                 liftIO $ writeIORef ref $! t + x
                                 c ref)


------------------------------------------------------------------------------
testTrivials :: Test
testTrivials = testCase "internal/trivials" $ do
    coverTypeableInstance (undefined :: InputStream Int)
    coverTypeableInstance (undefined :: OutputStream Int)
    coverTypeableInstance (undefined :: Generator Int ())
    coverTypeableInstance (undefined :: Consumer Int ())
    coverTypeableInstance (undefined :: SP Int Int)
