content: module Main where
  
  import qualified Data.ByteString as B
  import qualified Codec.FEC as FEC
  import System.IO (withFile, IOMode(..))
  import System.Random
  import Data.List (sortBy)
  
  import Test.QuickCheck
  
  -- | Return true if the given @k@ and @n@ values are valid
  isValidConfig :: Int -> Int -> Bool
  isValidConfig k n
    | k >= n = False
    | k < 1 = False
    | n < 1 = False
    | otherwise = True
  
  randomTake :: Int -> Int -> [a] -> [a]
  randomTake seed n values = map snd $ take n sortedValues where
    sortedValues = sortBy (\a b -> compare (fst a) (fst b)) taggedValues
    taggedValues = zip rnds values
    rnds :: [Float]
    rnds = randoms gen
    gen = mkStdGen seed
  
  testFEC k n len seed = FEC.decode fec someTaggedBlocks == origBlocks where
    origBlocks = map (\i -> B.replicate len $ fromIntegral i) [0..(k - 1)]
    fec = FEC.fec k n
    secondaryBlocks = FEC.encode fec origBlocks
    taggedBlocks = zip [0..] (origBlocks ++ secondaryBlocks)
    someTaggedBlocks = randomTake seed k taggedBlocks
  
  prop_FEC :: Int -> Int -> Int -> Int -> Property
  prop_FEC k n len seed =
    isValidConfig k n && n < 256 && len < 1024 ==> testFEC k n len seed
  
  checkDivide :: Int -> IO ()
  checkDivide n = do
    let input = B.replicate 1024 65
    parts <- FEC.secureDivide n input
    if FEC.secureCombine parts == input
       then return ()
       else fail "checkDivide failed"
  
  checkEnFEC :: Int -> IO ()
  checkEnFEC len = do
    testdata <- withFile "/dev/urandom" ReadMode (\handle -> B.hGet handle len)
    let [a, b, c, d, e] = FEC.enFEC 3 5 testdata
    if FEC.deFEC 3 5 [b, e, d] == testdata
       then return ()
       else fail "deFEC failure"
  
  main = do
    mapM_ (check (defaultConfig { configMaxTest = 1000, configMaxFail = 10000 })) [prop_FEC]
    mapM_ checkDivide [1, 2, 3, 4, 10]
    mapM_ checkEnFEC [1, 2, 3, 4, 5, 1024 * 1024]
  