{-# 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 {
        	NineFile m -> Word64 -> Word32 -> m ByteString
read :: Word64	-- Offset
			-> Word32 -- Length
			-> m (B.ByteString),	-- Resulting data
        	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.
		NineFile m -> m ()
remove :: m (),
		NineFile m -> m Stat
stat :: m Stat,
		NineFile m -> Stat -> m ()
wstat :: Stat -> 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.
		NineFile m -> m [NineFile m]
getFiles :: m [NineFile 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.
		NineFile m -> String -> m (NineFile m)
descend :: String -> m (NineFile m),
		-- |Create a file under this directory.
		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 :: String -> NineFile m
boringFile String
name = (Word64 -> Word32 -> m ByteString)
-> (Word64 -> ByteString -> m Word32)
-> m ()
-> m Stat
-> (Stat -> m ())
-> m Word32
-> NineFile m
forall (m :: * -> *).
(Word64 -> Word32 -> m ByteString)
-> (Word64 -> ByteString -> m Word32)
-> m ()
-> m Stat
-> (Stat -> m ())
-> m Word32
-> NineFile m
RegularFile
        (\Word64
_ Word32
_ -> ByteString -> m ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
"")
        (\Word64
_ ByteString
_ -> Word32 -> m Word32
forall (m :: * -> *) a. Monad m => a -> m a
return Word32
0)
        (() -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ())
        (Stat -> m Stat
forall (m :: * -> *) a. Monad m => a -> m a
return (Stat -> m Stat) -> Stat -> m Stat
forall a b. (a -> b) -> a -> b
$ Stat
boringStat {st_name :: String
st_name = String
name})
        (m () -> Stat -> m ()
forall a b. a -> b -> a
const (m () -> Stat -> m ()) -> m () -> Stat -> m ()
forall a b. (a -> b) -> a -> b
$ () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ())
	(Word32 -> m Word32
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 :: String -> [(String, NineFile m)] -> NineFile m
boringDir String
name [(String, NineFile m)]
contents = let m :: Map String (NineFile m)
m = [(String, NineFile m)] -> Map String (NineFile m)
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [(String, NineFile m)]
contents in Directory :: forall (m :: * -> *).
m [NineFile m]
-> m (Maybe (NineFile m))
-> (String -> m (NineFile m))
-> (String -> Word32 -> m (NineFile m))
-> m ()
-> m Stat
-> (Stat -> m ())
-> m Word32
-> NineFile m
Directory {
	getFiles :: m [NineFile m]
getFiles = ([NineFile m] -> m [NineFile m]
forall (m :: * -> *) a. Monad m => a -> m a
return ([NineFile m] -> m [NineFile m]) -> [NineFile m] -> m [NineFile m]
forall a b. (a -> b) -> a -> b
$ ((String, NineFile m) -> NineFile m)
-> [(String, NineFile m)] -> [NineFile m]
forall a b. (a -> b) -> [a] -> [b]
map (String, NineFile m) -> NineFile m
forall a b. (a, b) -> b
snd ([(String, NineFile m)] -> [NineFile m])
-> [(String, NineFile m)] -> [NineFile m]
forall a b. (a -> b) -> a -> b
$ [(String, NineFile m)]
contents),
	descend :: String -> m (NineFile m)
descend = (\String
x -> case String -> Map String (NineFile m) -> Maybe (NineFile m)
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 -> NineError -> m (NineFile m)
forall a e. Exception e => e -> a
throw (NineError -> m (NineFile m)) -> NineError -> m (NineFile m)
forall a b. (a -> b) -> a -> b
$ String -> NineError
ENoFile String
x
		Just NineFile m
f -> NineFile m -> m (NineFile m)
forall (m :: * -> *) a. Monad m => a -> m a
return NineFile m
f),
	create :: String -> Word32 -> m (NineFile m)
create = (\String
name Word32
perms -> NineFile m -> m (NineFile m)
forall (m :: * -> *) a. Monad m => a -> m a
return (NineFile m -> m (NineFile m)) -> NineFile m -> m (NineFile m)
forall a b. (a -> b) -> a -> b
$ String -> NineFile m
forall (m :: * -> *). (Monad m, EmbedIO m) => String -> NineFile m
boringFile String
name),
	remove :: m ()
remove = (() -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()),
	stat :: m Stat
stat = (Stat -> m Stat
forall (m :: * -> *) a. Monad m => a -> m a
return (Stat -> m Stat) -> Stat -> m Stat
forall a b. (a -> b) -> a -> b
$ Stat
boringStat {st_name :: String
st_name = String
name}),
	wstat :: Stat -> m ()
wstat = (m () -> Stat -> m ()
forall a b. a -> b -> a
const (m () -> Stat -> m ()) -> m () -> Stat -> m ()
forall a b. (a -> b) -> a -> b
$ () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()),
	version :: m Word32
version = (Word32 -> m Word32
forall (m :: * -> *) a. Monad m => a -> m a
return Word32
0),
	parent :: m (Maybe (NineFile m))
parent = Maybe (NineFile m) -> m (Maybe (NineFile m))
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (NineFile m)
forall a. Maybe a
Nothing }