module Channel ( stdChan , newChan , newDummyChan , serialisable , getChannelData ) where import Control.Applicative ((<$>)) import Control.Arrow (second) import Control.Concurrent (forkIO,threadDelay) import Control.Concurrent.MVar import qualified Control.Concurrent.Chan as Ch import Network.BSD (getHostName) import qualified Network as N import System.IO (BufferMode(..), Handle, hFlush, hGetLine, hPutStr, hPutStrLn, hReady, hSetBuffering) import System.IO.Error (catchIOError) import TypDefs (Channel (..), BuildType (..)) stdChan :: Handle -> Channel stdChan h = Channel write rd [] where write = hPutStrLn h rd = hGetLine h newChan :: BuildType -> String -> Integer -> IO Channel newChan t host port = case t of Init -> newChanServer port Connect -> newChanClient host port newDummyChan :: IO Channel newDummyChan = do chan <- Ch.newChan return $ Channel (Ch.writeChan chan) (Ch.readChan chan) [] newChanServer :: Integer -> IO Channel newChanServer cp = N.withSocketsDo $ do hanVar <- newEmptyMVar _ <- forkIO $ do sock <- N.listenOn $ N.PortNumber $ fromIntegral cp (clientHandle,_,_) <- N.accept sock putMVar hanVar clientHandle currentHost <- getHostName let ex = makeExtra [hostSig,portSig] [currentHost, show cp] return $ Channel (send' hanVar) (receive' hanVar) ex newChanClient :: String -> Integer -> IO Channel newChanClient hostName hostPort = N.withSocketsDo $ do hanVar <- newEmptyMVar _ <- forkIO $ do serverHandle <- waitForConnect hostName $ N.PortNumber $ fromIntegral hostPort putMVar hanVar serverHandle let ex = makeExtra [hostSig, portSig] [hostName, show hostPort] return $ Channel (send' hanVar) (receive' hanVar) ex waitForConnect :: N.HostName -> N.PortID -> IO Handle waitForConnect h p = N.connectTo h p `catchIOError` (\_ -> do threadDelay 1000000 putStrLn "waiting for connection" waitForConnect h p) send' :: MVar Handle -> String -> IO () send' hanVar msg = do han <- takeMVar hanVar hPutStrLn han msg putMVar hanVar han receive' :: MVar Handle -> IO String receive' hanVar = do han <- readMVar hanVar unlines <$> emptyHandle han emptyHandle :: Handle -> IO [String] emptyHandle h = do line <- hGetLine h more <- hReady h `catchIOError` (\_ -> return False) if more then (line:) <$> emptyHandle h else return [line] --lineBuffer :: Handle -> IO () --lineBuffer h = hSetBuffering h LineBuffering dataBreak :: Char dataBreak = '#' hostSig :: String hostSig = "host" portSig :: String portSig = "port" makeExtra :: [String] -> [String] -> [String] makeExtra = zipWith (\a b -> (a ++ dataBreak : b)) serialisable :: Channel -> Bool serialisable = not . null . extra getChannelData :: [String] -> Maybe (String, Integer) getChannelData strs = do let ex = map (second tail . break (==dataBreak)) strs host <- lookup hostSig ex port <- lookup portSig ex return (host,read port)