{-# LANGUAGE TemplateHaskell #-}

module Network.NineP.Internal.State
	( Nine
	, NineVersion(..)
	, readVersion
	, Config(..)
	, emptyState
	, msize
	, protoVersion
	, lookup
	, insert
	, delete
	, iounit
	) where

import Control.Concurrent.MState
import Control.Monad.Reader
import Control.Monad.State.Class
import Data.List (isPrefixOf)
import Data.Map (Map)
import qualified Data.Map as M
import Data.Word
import Prelude hiding (lookup)

import Network.NineP.Error
import Network.NineP.Internal.File

data NineVersion = VerUnknown | Ver9P2000

instance Show NineVersion where
	show VerUnknown = "unknown"
	show Ver9P2000 = "9P2000"

readVersion :: String -> NineVersion
readVersion s = if isPrefixOf "9P2000" s then Ver9P2000 else VerUnknown

-- |Server configuration.
data Config = Config {
		-- |The @/@ directory of the hosted filesystem
		root :: NineFile,
		-- |The listening address. The syntax is taken from @Plan 9@ operating system and has the form @unix!/path/to/socket@ for unix socket files, and @tcp!hostname!port@ for tcp sockets.
		addr :: String
	}

data NineState = NineState {
		fidMap :: Map Word32 NineFile,
		msize :: Word32,
		protoVersion :: NineVersion
	}

emptyState = NineState
	(M.empty :: Map Word32 NineFile)
	0
	VerUnknown

type Nine x = ErrorT NineError (MState NineState (ReaderT Config IO)) x

lookup :: Word32 -> Nine NineFile
lookup fid = do
	m <- (return . fidMap) =<< get 
	case M.lookup fid m of
		Nothing -> throwError $ ENoFid fid
		Just f -> return f

insert :: Word32 -> NineFile -> Nine ()
insert fid f = do
	m <- (return . fidMap) =<< get 
	lift $ modifyM_ (\s -> s { fidMap = M.insert fid f $ fidMap s })

delete :: Word32 -> Nine ()
delete fid = do
	lift $ modifyM_ (\s -> s { fidMap = M.delete fid $ fidMap s })

iounit :: Nine Word32
iounit = do
	ms <- (return . msize) =<< get
	-- 23 is the biggest size of a message (write), but libixp uses 24, so we do too to stay safe
	return $ ms - 24