{-# LANGUAGE OverloadedStrings #-}

module Std.IO.FileSystemTSpec where

import           Control.Concurrent.MVar (readMVar)
import           Control.Monad
import           Data.Bits
import           Std.Data.Vector         as V
import           Std.Data.Vector.Base    as V
import           Data.List               as List
import           Foreign.Marshal.Array
import           Foreign.Ptr
import           Std.IO.Buffered
import           Std.IO.Exception
import           Std.IO.FileSystemT
import           Std.IO.Resource
import           Std.IO.UV.Manager
import           Test.Hspec
import           Test.HUnit

spec :: Spec
spec = describe "filesystem (threadpool version) operations" $ do

        let content = "Hello world!"
            content2 = V.cycleN 1024 "quick fox jumps over the lazy dog, 世界你好!\n"
            size = V.length content
            size2 = V.length content2

        it "create a temp dir" $ do
            tempdir <- mkdtemp "stdio-filesystem-unit"
            dirs <- scandir "./"
            rmdir tempdir
            List.lookup tempdir dirs @?= Just DirEntDir


        let flags = O_RDWR .|. O_CREAT
            mode = DEFAULT_MODE
            filename = "test-file"

        it "Opens and writes a file" $ do
            withResource (initUVFile filename flags mode) $ \ file -> do
                o <- newBufferedOutput file 4096
                writeBuffer o content
                flushBuffer o

            withResource (initUVFile filename flags mode) $ \ file -> do
                i <- newBufferedInput file 4096
                written <- readExactly size i
                written @=? content

                fr <- newUVFileReader file 0
                i <- newBufferedInput fr 4096
                written <- readExactly size i
                written @=? content

            unlink filename

        it "Opens and writes a file II" $ do
            withResource (initUVFile filename flags mode) $ \ file -> do
                o <- newBufferedOutput file 4096
                writeBuffer o content2
                flushBuffer o

            withResource (initUVFile filename flags mode) $ \ file -> do
                i <- newBufferedInput file 4096
                written <- readExactly size2 i
                written @=? content2

            withResource (initUVFile filename flags mode) $ \ file -> do
                i <- newBufferedInput file 4096
                firstLine <- readLine i
                firstLine  @=? fst (V.break (== V.c2w '\n') content2)

                fr <- newUVFileReader file 0
                i <- newBufferedInput fr 4096
                replicateM_ 1024 $ do
                    firstLine <- readLine i
                    firstLine  @=? fst (V.break (== V.c2w '\n') content2)
            unlink filename


        it "create and remove dir" $ do
            tempdir <- mkdtemp "stdio-filesystem-unit"
            let dirname  = tempdir <> "/test-dir"
            mkdir dirname mode
            dirs <- scandir tempdir
            print dirs
            rmdir dirname
            rmdir tempdir
            List.lookup "test-dir" dirs @?= Just DirEntDir

        let linkname  = "test-link"
            symlinkname  = "test-symlink"
            symlinkname2  = "test-symlink2"

        it "link stat should be equal to target file" $ do

            withResource (initUVFile filename flags mode) $ \ file -> return ()

            s0 <- stat filename

            link filename linkname
            symlink "test-link" symlinkname SYMLINK_DEFAULT

            absfp <- realpath filename
            symlink absfp symlinkname2 SYMLINK_DEFAULT  -- the second way to create a proper symlink

            s1 <- stat linkname
            s2 <- stat symlinkname
            s2' <- stat symlinkname2

            s0 @?= s1 {stNlink = 1} -- update hard link number
            s0 @?= s2 {stNlink = 1}
            s0 @?= s2' {stNlink = 1}

            withResource (initUVFile filename flags mode) $ \ file -> do
                s4 <- fstat file
                s0 @?= s4 {stNlink = 1}

            unlink filename
            unlink linkname
            unlink symlinkname
            unlink symlinkname2

        it "utime result in stat change" $ do
            withResource (initUVFile filename flags mode) $ \ file -> return ()
            utime filename 1000.2000 3000.4000
            s <- stat filename
            print s
            uvtSecond (stAtim s) @?= 1000
            uvtNanoSecond (stAtim s) @?= 200000000
            uvtSecond (stMtim s) @?= 3000
            uvtNanoSecond (stMtim s) @?= 400000000
            unlink filename

        it "futime result in fstat change" $ do
            withResource (initUVFile filename flags mode) $ \ file -> do
                futime file 5000.6000 7000.8000
                s <- fstat file
                print s
                uvtSecond (stAtim s) @?= 5000
                uvtNanoSecond (stAtim s) @?= 600000000
                uvtSecond (stMtim s) @?= 7000
                uvtNanoSecond (stMtim s) @?= 800000000
            unlink filename

