module Main ( main ) where

import           Codec.Lzip                   (compress, compressBest,
                                               decompress)
import           Control.Applicative
import           Control.Concurrent           (forkIO, newEmptyMVar, putMVar,
                                               readMVar)
import           Control.Monad                (filterM)
import qualified Data.ByteString              as BS
import qualified Data.ByteString.Lazy         as BSL
import           Data.ByteString.Pathological (nonstandardRead)
import           Data.Foldable                (traverse_)
import           System.Directory             (doesFileExist)
import           Test.Hspec

forceHead :: BSL.ByteString -> IO ()
forceHead bsl = BS.length (head $ BSL.toChunks bsl) `seq` pure ()

forceBSL :: BSL.ByteString -> IO ()
forceBSL bsl = BS.length (last $ BSL.toChunks bsl) `seq` pure ()

decompressMultithreaded :: FilePath -> IO ()
decompressMultithreaded fp = do
    bsl <- decompress <$> BSL.readFile fp
    forceHead bsl
    done <- newEmptyMVar
    _ <- forkIO (forceBSL bsl *> putMVar done ())
    _ <- forkIO (forceBSL bsl *> putMVar done ())
    readMVar done

compressFileGeneral :: (FilePath -> IO BSL.ByteString) -> FilePath -> Spec
compressFileGeneral f fp = parallel $
    it ("decompress . compress should be identity (" ++ fp ++ ")") $ do
        str <- f fp
        decompress (compress str) `shouldBe` str

compressFileFreaky :: FilePath -> Spec
compressFileFreaky = compressFileGeneral nonstandardRead

compressFile :: FilePath -> Spec
compressFile = compressFileGeneral BSL.readFile

decompressFileGeneral :: (FilePath -> IO BSL.ByteString) -> FilePath -> Spec
decompressFileGeneral f fp = parallel $
    it ("compress . decompress should be identity (" ++ fp ++ ")") $ do
        str <- f fp
        compressBest (decompress str) `shouldBe` str

decompressFileFreaky :: FilePath -> Spec
decompressFileFreaky = decompressFileGeneral nonstandardRead

decompressFile :: FilePath -> Spec
decompressFile = decompressFileGeneral BSL.readFile

main :: IO ()
main = do
    ex' <- filterM doesFileExist ["gmp-6.1.2.tar.lz", "lzlib-1.10.tar.lz"]
    ex <- ("cbits/encoder.c" :)
            <$> filterM doesFileExist ["gmp-6.1.2.tar", "lzlib-1.10.tar"]
    hspec $ do
        describe "roundtrip" $
            traverse_ compressFile ex
        describe "roundtrip" $
            traverse_ decompressFile ex'
        describe "roundtrip (sketchy)" $
            traverse_ compressFileFreaky ex
        describe "roundtrip (sketchy)" $
            traverse_ decompressFileFreaky ex'
        describe "decompress (multithreaded)" $
            it "should not fail" $
                traverse_ decompressMultithreaded ex'
