{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}

module Network.NineP.Internal.File
	( NineFile(..)
	, boringFile
	, boringDir
	) where

import Control.Exception
import Control.Monad.EmbedIO
import qualified Data.ByteString.Lazy.Char8 as B
import qualified Data.Map as M
import Data.Word

import Data.NineP
import Network.NineP.Error

data NineFile m =
	RegularFile {
        	forall (m :: * -> *).
NineFile m -> Word64 -> Word32 -> m ByteString
read :: Word64	-- Offset
			-> Word32 -- Length
			-> m (B.ByteString),	-- Resulting data
        	forall (m :: * -> *).
NineFile m -> Word64 -> ByteString -> m Word32
write :: Word64	-- Offset
			-> B.ByteString	-- The written data
			-> m (Word32),	-- Number of bytes written successfully. Should return @0@ in case of an error.
		forall (m :: * -> *). NineFile m -> m ()
remove :: m (),
		forall (m :: * -> *). NineFile m -> m Stat
stat :: m Stat,
		forall (m :: * -> *). NineFile m -> Stat -> m ()
wstat :: Stat -> m (),
		forall (m :: * -> *). NineFile m -> m Word32
version :: m Word32
	} | Directory {
		-- |A callback to get the list of the files this directory contains. Must not contain @.@ and @..@ entries.
		forall (m :: * -> *). NineFile m -> m [NineFile m]
getFiles :: m [NineFile m],
		forall (m :: * -> *). NineFile m -> m (Maybe (NineFile m))
parent :: m (Maybe (NineFile m)),
		-- |A callback to address a specific file by its name. @.@ and @..@ are handled in the library.
		forall (m :: * -> *). NineFile m -> String -> m (NineFile m)
descend :: String -> m (NineFile m),
		-- |Create a file under this directory.
		forall (m :: * -> *).
NineFile m -> String -> Word32 -> m (NineFile m)
create :: String -> Word32 -> m (NineFile m),
		remove :: m (),
		-- |The directory stat must return only stat for @.@.
		stat :: m Stat,
		wstat :: Stat -> m (),
		version :: m Word32
	}

boringStat :: Stat
boringStat :: Stat
boringStat = Word16
-> Word32
-> Qid
-> Word32
-> Word32
-> Word32
-> Word64
-> String
-> String
-> String
-> String
-> Stat
Stat Word16
0 Word32
0 (Word8 -> Word32 -> Word64 -> Qid
Qid Word8
0 Word32
0 Word64
0) Word32
0o0777 Word32
0 Word32
0 Word64
0 String
"boring" String
"root" String
"root" String
"root"

-- |A dumb file that can't do anything.
boringFile :: (Monad m, EmbedIO m) => String -> NineFile m
boringFile :: forall (m :: * -> *). (Monad m, EmbedIO m) => String -> NineFile m
boringFile String
name = forall (m :: * -> *).
(Word64 -> Word32 -> m ByteString)
-> (Word64 -> ByteString -> m Word32)
-> m ()
-> m Stat
-> (Stat -> m ())
-> m Word32
-> NineFile m
RegularFile
        (\Word64
_ Word32
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
"")
        (\Word64
_ ByteString
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return Word32
0)
        (forall (m :: * -> *) a. Monad m => a -> m a
return ())
        (forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Stat
boringStat {st_name :: String
st_name = String
name})
        (forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return ())
	(forall (m :: * -> *) a. Monad m => a -> m a
return Word32
0)

-- |A dumb directory that can't do anything but provide the files it contains. An user can create files, but they won't show up in listing and will essentially be 'boringFile's.
boringDir :: (Monad m, EmbedIO m) => String -> [(String, NineFile m)] -> NineFile m
boringDir :: forall (m :: * -> *).
(Monad m, EmbedIO m) =>
String -> [(String, NineFile m)] -> NineFile m
boringDir String
name [(String, NineFile m)]
contents = let m :: Map String (NineFile m)
m = forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [(String, NineFile m)]
contents in Directory {
	getFiles :: m [NineFile m]
getFiles = (forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd forall a b. (a -> b) -> a -> b
$ [(String, NineFile m)]
contents),
	descend :: String -> m (NineFile m)
descend = (\String
x -> case forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup String
x Map String (NineFile m)
m of
		Maybe (NineFile m)
Nothing -> forall a e. Exception e => e -> a
throw forall a b. (a -> b) -> a -> b
$ String -> NineError
ENoFile String
x
		Just NineFile m
f -> forall (m :: * -> *) a. Monad m => a -> m a
return NineFile m
f),
	create :: String -> Word32 -> m (NineFile m)
create = (\String
name Word32
perms -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *). (Monad m, EmbedIO m) => String -> NineFile m
boringFile String
name),
	remove :: m ()
remove = (forall (m :: * -> *) a. Monad m => a -> m a
return ()),
	stat :: m Stat
stat = (forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Stat
boringStat {st_name :: String
st_name = String
name}),
	wstat :: Stat -> m ()
wstat = (forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return ()),
	version :: m Word32
version = (forall (m :: * -> *) a. Monad m => a -> m a
return Word32
0),
	parent :: m (Maybe (NineFile m))
parent = forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing }