]> git.rkrishnan.org Git - functorrent.git/commitdiff
new modules FileSystem and PieceManager
authorRamakrishnan Muthukrishnan <ram@rkrishnan.org>
Wed, 9 Dec 2015 06:21:53 +0000 (11:51 +0530)
committerRamakrishnan Muthukrishnan <ram@rkrishnan.org>
Wed, 9 Dec 2015 06:21:53 +0000 (11:51 +0530)
FileSystem module abstracts the filesystem operations for the download
and upload via a message channel that peers can send messages to.

functorrent.cabal
src/FuncTorrent/FileSystem.hs [new file with mode: 0644]
src/FuncTorrent/Peer.hs
src/FuncTorrent/PieceManager.hs [new file with mode: 0644]
src/FuncTorrent/Server.hs
src/FuncTorrent/Tracker.hs
src/FuncTorrent/Utils.hs
src/main/Main.hs

index abd77a69ec43a25dabcea8dd8d470caf369a111a..c4da29dc1adce981bf64705131d98cfa3a10275d 100644 (file)
@@ -17,11 +17,13 @@ cabal-version:       >=1.18
 
 library
   exposed-modules:     FuncTorrent.Bencode,
+                       FuncTorrent.FileSystem,
                        FuncTorrent.Logger,
                        FuncTorrent.Metainfo,
                        FuncTorrent.Network
                        FuncTorrent.Peer,
                        FuncTorrent.PeerMsgs,
+                       FuncTorrent.PieceManager,
                        FuncTorrent.Server,
                        FuncTorrent.Tracker,
                        FuncTorrent.Utils
diff --git a/src/FuncTorrent/FileSystem.hs b/src/FuncTorrent/FileSystem.hs
new file mode 100644 (file)
index 0000000..1b190ca
--- /dev/null
@@ -0,0 +1,77 @@
+module FuncTorrent.FileSystem
+       (startThread,
+        MsgChannel,
+        initFS,
+        Msg(..),
+        Piece(..),
+        pieceMapFromFile
+       )
+       where
+
+import           Control.Concurrent (ThreadId, forkIO)
+import           Control.Concurrent.Chan (Chan, newChan, readChan)
+import           Control.Concurrent.MVar (MVar, putMVar)
+import           Control.Monad (forever)
+import           Data.Map (traverseWithKey)
+
+import qualified Data.ByteString as BS
+import           Data.Map ((!))
+import           System.IO (Handle, openFile, IOMode (ReadWriteMode))
+
+import           FuncTorrent.PieceManager (PieceDlState(..), PieceData(..), PieceMap, pieceNumToOffset)
+import           FuncTorrent.Utils (readFileAtOffset, writeFileAtOffset, verifyHash)
+
+type PieceNum = Integer
+data Piece = Piece PieceNum BS.ByteString
+
+data Msg = ReadPiece PieceNum Integer (MVar Piece)
+         | WritePiece Piece
+         | VerifyPiece PieceNum (MVar Bool)
+
+type MsgChannel = Chan Msg
+
+-- init :: FileName -> IO (Handle, MsgChannel)
+initFS :: FilePath -> IO (Handle, MsgChannel)
+initFS filepath = do
+  c <- newChan
+  h <- openFile filepath ReadWriteMode
+  return (h, c)
+
+startThread :: Handle -> MsgChannel -> PieceMap -> IO ThreadId
+startThread handle c pieceMap = do
+  forkIO $ forever $ recvMsg >>= sendResponse
+  where
+    recvMsg = readChan c
+    sendResponse msg =
+      case msg of
+        ReadPiece n len' var -> do
+          bs <- readPiece n len'
+          putMVar var (Piece n bs)
+        WritePiece (Piece n bs) -> do
+          writePiece n bs
+        VerifyPiece n var -> do
+          isHashValid <- verifyPiece n
+          putMVar var isHashValid
+    readPiece n len' = do
+      let offset = pieceNumToOffset pieceMap n
+      readFileAtOffset handle offset len'
+    writePiece n piece = do
+      let offset = pieceNumToOffset pieceMap n
+      writeFileAtOffset handle offset piece
+    verifyPiece n = do
+      let offset = pieceNumToOffset pieceMap n
+          hash'  = hash (pieceMap ! n)
+          len'   = len (pieceMap ! n)
+      bs' <- readFileAtOffset handle offset len'
+      return $ verifyHash bs' hash'
+
+pieceMapFromFile :: Handle -> PieceMap -> IO PieceMap
+pieceMapFromFile handle pieceMap = do
+  traverseWithKey f pieceMap
+  where
+    f k v = do
+      let offset = pieceNumToOffset pieceMap k
+      isHashValid <- flip verifyHash (hash v) <$> readFileAtOffset handle offset (len v)
+      if isHashValid
+        then return $ v { dlstate = Have }
+        else return v
index c6629a4c980146a734f3e003d2ef7751506356ad..834c8086032c700f921e7034975156f2b1653e6e 100644 (file)
@@ -2,27 +2,25 @@
 module FuncTorrent.Peer
     (Peer(..),
      PieceMap,
-     handlePeerMsgs,
-     bytesDownloaded,
-     initPieceMap,
-     pieceMapFromFile
+     handlePeerMsgs
     ) where
 
-import Prelude hiding (lookup, concat, replicate, splitAt, take, drop, filter)
+import Prelude hiding (lookup, concat, replicate, splitAt, take, drop)
 
-import System.IO (Handle, BufferMode(..), hSetBuffering, hClose)
-import Data.ByteString (ByteString, unpack, concat, hGet, hPut, take, drop, empty)
-import qualified Data.ByteString.Char8 as BC (length)
-import Network (connectTo, PortID(..))
+import Control.Concurrent.Chan (writeChan)
 import Control.Monad.State
+import Data.ByteString (ByteString, unpack, concat, hGet, hPut, take, drop, empty)
 import Data.Bits
 import Data.Word (Word8)
-import Data.Map (Map, fromList, toList, (!), mapWithKey, traverseWithKey, adjust, filter)
-import Safe (headMay)
+import Data.Map ((!), adjust)
+import Network (connectTo, PortID(..))
+import System.IO (Handle, BufferMode(..), hSetBuffering, hClose)
 
-import FuncTorrent.Metainfo (Info(..), Metainfo(..))
-import FuncTorrent.Utils (splitN, splitNum, writeFileAtOffset, readFileAtOffset, verifyHash)
+import FuncTorrent.Metainfo (Metainfo(..))
 import FuncTorrent.PeerMsgs (Peer(..), PeerMsg(..), sendMsg, getMsg, genHandshakeMsg)
+import FuncTorrent.Utils (splitNum, verifyHash)
+import FuncTorrent.PieceManager (PieceDlState(..), PieceData(..), PieceMap, pickPiece, updatePieceAvailability)
+import qualified FuncTorrent.FileSystem as FS (MsgChannel, Msg(..), Piece(..))
 
 data PState = PState { handle :: Handle
                      , peer :: Peer
@@ -31,46 +29,6 @@ data PState = PState { handle :: Handle
                      , heChoking :: Bool
                      , heInterested :: Bool}
 
-data PieceDlState = Pending
-                  | Downloading
-                  | Have
-                  deriving (Show, Eq)
-
--- todo - map with index to a new data structure (peers who have that piece and state)
-data PieceData = PieceData { peers :: [Peer]        -- ^ list of peers who have this piece
-                           , dlstate :: PieceDlState  -- ^ state of the piece from download perspective.
-                           , hash  :: ByteString    -- ^ piece hash
-                           , len :: Integer }       -- ^ piece length
-
--- which piece is with which peers
-type PieceMap = Map Integer PieceData
-
-
--- Make the initial Piece map, with the assumption that no peer has the
--- piece and that every piece is pending download.
-initPieceMap :: ByteString  -> Integer -> Integer -> PieceMap
-initPieceMap pieceHash fileLen pieceLen = fromList kvs
-  where
-    numPieces = (toInteger . (`quot` 20) . BC.length) pieceHash
-    kvs = [(i, PieceData { peers = []
-                         , dlstate = Pending
-                         , hash = h
-                         , len = pLen })
-          | (i, h, pLen) <- zip3 [0..numPieces] hashes pLengths]
-    hashes = splitN 20 pieceHash
-    pLengths = splitNum fileLen pieceLen
-
-pieceMapFromFile :: FilePath -> PieceMap -> IO PieceMap
-pieceMapFromFile filePath pieceMap =
-  traverseWithKey f pieceMap
-  where
-    f k v = do
-      let offset = if k == 0 then 0 else k * len (pieceMap ! (k - 1))
-      isHashValid <- flip verifyHash (hash v) <$> readFileAtOffset filePath offset (len v)
-      if isHashValid
-        then return $ v { dlstate = Have }
-        else return v
-
 havePiece :: PieceMap -> Integer -> Bool
 havePiece pm index =
   dlstate (pm ! index) == Have
@@ -129,33 +87,16 @@ toPeerState h p meCh meIn heCh heIn =
          , meChoking = meCh
          , meInterested = meIn }
 
--- simple algorithm to pick piece.
--- pick the first piece from 0 that is not downloaded yet.
-pickPiece :: PieceMap -> Maybe Integer
-pickPiece =
-  (fst `liftM`) . headMay . toList . filter (\v -> dlstate v == Pending)
-
-bytesDownloaded :: PieceMap -> Integer
-bytesDownloaded =
-  sum . map (len . snd) . toList . filter (\v -> dlstate v == Have)
-
-updatePieceAvailability :: PieceMap -> Peer -> [Integer] -> PieceMap
-updatePieceAvailability pieceStatus p pieceList =
-  mapWithKey (\k pd -> if k `elem` pieceList
-                       then (pd { peers = p : peers pd })
-                       else pd) pieceStatus
-
-handlePeerMsgs :: Peer -> String -> Metainfo -> PieceMap -> Bool -> IO ()
-handlePeerMsgs p peerId m pieceMap isClient = do
+handlePeerMsgs :: Peer -> String -> Metainfo -> PieceMap -> Bool -> FS.MsgChannel -> IO ()
+handlePeerMsgs p peerId m pieceMap isClient c = do
   h <- connectToPeer p
   doHandshake isClient h p (infoHash m) peerId
   let pstate = toPeerState h p False False True True
-      filePath = name (info m)
-  _ <- runStateT (msgLoop pieceMap filePath) pstate
+  _ <- runStateT (msgLoop pieceMap c) pstate
   return ()
 
-msgLoop :: PieceMap -> FilePath -> StateT PState IO ()
-msgLoop pieceStatus file = do
+msgLoop :: PieceMap -> FS.MsgChannel -> StateT PState IO ()
+msgLoop pieceStatus msgchannel = do
   h <- gets handle
   st <- get
   case st of
@@ -163,7 +104,7 @@ msgLoop pieceStatus file = do
       liftIO $ sendMsg h InterestedMsg
       gets peer >>= (\p -> liftIO $ putStrLn $ "--> InterestedMsg to peer: " ++ show p)
       modify (\st' -> st' { meInterested = True })
-      msgLoop pieceStatus file
+      msgLoop pieceStatus msgchannel
     PState { meInterested = True, heChoking = False } ->
       case pickPiece pieceStatus of
         Nothing -> liftIO $ putStrLn "Nothing to download"
@@ -175,10 +116,9 @@ msgLoop pieceStatus file = do
             then
             liftIO $ putStrLn "Hash mismatch"
             else do
-            let fileOffset = if workPiece == 0 then 0 else workPiece * len (pieceStatus ! (workPiece - 1))
-            liftIO $ putStrLn $ "Write into file at offset: " ++ show fileOffset
-            liftIO $ writeFileAtOffset file fileOffset pBS
-            msgLoop (adjust (\pieceData -> pieceData { dlstate = Have }) workPiece pieceStatus) file
+            liftIO $ putStrLn $ "Write piece: " ++ show workPiece
+            liftIO $ writeChan msgchannel $ FS.WritePiece (FS.Piece workPiece pBS)
+            msgLoop (adjust (\pieceData -> pieceData { dlstate = Have }) workPiece pieceStatus) msgchannel
     _ -> do
       msg <- liftIO $ getMsg h
       gets peer >>= (\p -> liftIO $ putStrLn $ "<-- " ++ show msg ++ "from peer: " ++ show p)
@@ -186,7 +126,7 @@ msgLoop pieceStatus file = do
         KeepAliveMsg -> do
           liftIO $ sendMsg h KeepAliveMsg
           gets peer >>= (\p -> liftIO $ putStrLn $ "--> " ++ "KeepAliveMsg to peer: " ++ show p)
-          msgLoop pieceStatus file
+          msgLoop pieceStatus msgchannel
         BitFieldMsg bss -> do
           p <- gets peer
           let pieceList = bitfieldToList (unpack bss)
@@ -195,23 +135,23 @@ msgLoop pieceStatus file = do
           -- for each pieceIndex in pieceList, make an entry in the pieceStatus
           -- map with pieceIndex as the key and modify the value to add the peer.
           -- download each of the piece in order
-          msgLoop pieceStatus' file
+          msgLoop pieceStatus' msgchannel
         UnChokeMsg -> do
           modify (\st' -> st' {heChoking = False })
-          msgLoop pieceStatus file
+          msgLoop pieceStatus msgchannel
         ChokeMsg -> do
           modify (\st' -> st' {heChoking = True })
-          msgLoop pieceStatus file
+          msgLoop pieceStatus msgchannel
         InterestedMsg -> do
           modify (\st' -> st' {heInterested = True})
-          msgLoop pieceStatus file
+          msgLoop pieceStatus msgchannel
         NotInterestedMsg -> do
           modify (\st' -> st' {heInterested = False})
-          msgLoop pieceStatus file
+          msgLoop pieceStatus msgchannel
         CancelMsg _ _ _ -> -- check if valid index, begin, length
-          msgLoop pieceStatus file
+          msgLoop pieceStatus msgchannel
         PortMsg _ ->
-          msgLoop pieceStatus file
+          msgLoop pieceStatus msgchannel
         -- handle RequestMsg, HaveMsg. No need to handle PieceMsg here.
         -- also BitFieldMsg
 
@@ -233,6 +173,6 @@ downloadPiece h index pieceLength = do
                                                     ++ show begin
                                                   return block
                                                 _ -> do
-                                                  putStrLn "ignoring irrelevant msg"
+                                                  putStrLn $ "ignoring irrelevant msg: " ++ show msg
                                                   return empty)
 
diff --git a/src/FuncTorrent/PieceManager.hs b/src/FuncTorrent/PieceManager.hs
new file mode 100644 (file)
index 0000000..e65b3fd
--- /dev/null
@@ -0,0 +1,69 @@
+module FuncTorrent.PieceManager
+       (PieceDlState(..),
+        PieceData(..),
+        PieceMap,
+        pieceNumToOffset,
+        updatePieceAvailability,
+        pickPiece,
+        bytesDownloaded,
+        initPieceMap,
+       ) where
+
+import           Prelude hiding (filter)
+
+import qualified Data.ByteString.Char8 as BC (length)
+import           Control.Monad (liftM)
+import           Data.ByteString (ByteString)
+import           Data.Map (Map, (!), fromList, toList, mapWithKey, filter)
+import           Safe (headMay)
+
+import           FuncTorrent.PeerMsgs (Peer)
+import           FuncTorrent.Utils (splitN, splitNum)
+
+data PieceDlState = Pending
+                  | Downloading
+                  | Have
+                  deriving (Show, Eq)
+
+-- todo - map with index to a new data structure (peers who have that piece and state)
+data PieceData = PieceData { peers :: [Peer]        -- ^ list of peers who have this piece
+                           , dlstate :: PieceDlState  -- ^ state of the piece from download perspective.
+                           , hash  :: ByteString    -- ^ piece hash
+                           , len :: Integer }       -- ^ piece length
+
+-- which piece is with which peers
+type PieceMap = Map Integer PieceData
+
+pieceNumToOffset :: PieceMap -> Integer -> Integer
+pieceNumToOffset _        0 = 0
+pieceNumToOffset pieceMap k = k * len (pieceMap ! (k - 1))
+
+-- simple algorithm to pick piece.
+-- pick the first piece from 0 that is not downloaded yet.
+pickPiece :: PieceMap -> Maybe Integer
+pickPiece =
+  (fst `liftM`) . headMay . toList . filter (\v -> dlstate v == Pending)
+
+bytesDownloaded :: PieceMap -> Integer
+bytesDownloaded =
+  sum . map (len . snd) . toList . filter (\v -> dlstate v == Have)
+
+updatePieceAvailability :: PieceMap -> Peer -> [Integer] -> PieceMap
+updatePieceAvailability pieceStatus p pieceList =
+  mapWithKey (\k pd -> if k `elem` pieceList
+                       then (pd { peers = p : peers pd })
+                       else pd) pieceStatus
+
+-- Make the initial Piece map, with the assumption that no peer has the
+-- piece and that every piece is pending download.
+initPieceMap :: ByteString  -> Integer -> Integer -> PieceMap
+initPieceMap pieceHash fileLen pieceLen = fromList kvs
+  where
+    numPieces = (toInteger . (`quot` 20) . BC.length) pieceHash
+    kvs = [(i, PieceData { peers = []
+                         , dlstate = Pending
+                         , hash = h
+                         , len = pLen })
+          | (i, h, pLen) <- zip3 [0..numPieces] hashes pLengths]
+    hashes = splitN 20 pieceHash
+    pLengths = splitNum fileLen pieceLen
index 766ac4e77aea22510569ace8598767dc77e294f3..465ef045ac4e45b3f516a2ff8e3bd384aebb2bde 100644 (file)
@@ -8,6 +8,7 @@ import System.IO (hSetBuffering, BufferMode ( NoBuffering ))
 
 import FuncTorrent.Metainfo (Metainfo)
 import FuncTorrent.Peer (handlePeerMsgs, Peer(..), PieceMap)
+import qualified FuncTorrent.FileSystem as FS (MsgChannel)
 
 -- server is listening on any port from 6881 - 6889
 -- return the port number used
@@ -17,9 +18,9 @@ start = withSocketsDo $ do
   sock <- listenOn $ PortNumber $ fromIntegral (head portnums)
   return (sock, PortNumber $ head portnums)
 
-run :: Socket -> String -> Metainfo -> PieceMap -> IO ()
-run listenSock peerid m pieceMap = forever $ do
+run :: Socket -> String -> Metainfo -> PieceMap -> FS.MsgChannel -> IO ()
+run listenSock peerid m pieceMap = forever $ do
   (handle, ip, port) <- accept listenSock
   let peer = Peer "" ip (fromIntegral port)
   hSetBuffering handle NoBuffering
-  forkIO $ handlePeerMsgs peer peerid m pieceMap False
+  forkIO $ handlePeerMsgs peer peerid m pieceMap False c
index 125a9269f9b9137fc7e2b25273a3ae4de44d1dd9..b07fd2546f20912c6f7a07c9f9621a6dd5df9d83 100644 (file)
@@ -3,14 +3,16 @@ module FuncTorrent.Tracker
     (TState(..),
      initialTrackerState,
      trackerLoop,
+     udpTrackerLoop
     ) where
 
 import Prelude hiding (lookup, splitAt)
 
-import System.IO (Handle)
+
 import Control.Applicative (liftA2)
 import Control.Concurrent (threadDelay)
 import Control.Concurrent.MVar (MVar, newEmptyMVar, newMVar, readMVar, putMVar)
+import Control.Exception (try)
 import Data.Binary (Binary(..), encode, decode)
 import Data.Binary.Put (putWord16be, putWord32be, putWord64be, putByteString)
 import Data.Binary.Get (Get, isEmpty, getByteString, getWord16be, getWord32be)
@@ -20,7 +22,9 @@ import Data.ByteString.Lazy (fromStrict, toStrict)
 import Data.Char (chr)
 import Data.List (intercalate, isPrefixOf)
 import Data.Map as M (lookup)
-import Network (PortNumber)
+import Network (connectTo, PortID(..), PortNumber, Socket)
+import Network.Socket (defaultProtocol, SocketType( Datagram ), Family( AF_INET ), SockAddr( SockAddrInet ), inet_addr, socket)
+import Network.Socket.ByteString (sendTo, recv)
 import Network.HTTP.Base (urlEncode)
 import qualified Data.ByteString.Base16 as B16 (encode)
 
@@ -31,7 +35,6 @@ import FuncTorrent.Network (sendGetRequest)
 import FuncTorrent.Peer (Peer(..))
 import FuncTorrent.Utils (splitN)
 
-
 data TrackerProtocol = Http
                      | Udp
                      | UnknownProtocol
@@ -239,18 +242,33 @@ trackerLoop port peerId m st = do
           trackerLoop port peerId m st
 
 -- udp tracker
-getResponse :: Handle -> IO UDPResponse
-getResponse h = do
+getResponse :: Socket -> IO UDPResponse
+getResponse s = do
   -- connect packet is 16 bytes long
   -- announce packet is atleast 20 bytes long
-  bs <- hGet h (16*1024)
+  bs <- recv s (16*1024)
   return $ decode $ fromStrict bs
 
-sendRequest :: Handle -> UDPRequest -> IO ()
-sendRequest h req = hPut h bsReq
-  where bsReq = toStrict $ encode req
+sendRequest :: Socket -> String -> PortNumber -> UDPRequest -> IO ()
+sendRequest s ip port req = do
+  hostaddr <- inet_addr ip
+  _ <- sendTo s bsReq $ SockAddrInet (fromIntegral port) hostaddr
+  return ()
+    where bsReq = toStrict $ encode req
 
 getTrackerType :: String -> TrackerProtocol
 getTrackerType url | isPrefixOf "http://" url = Http
                    | isPrefixOf "udp://" url  = Udp
                    | otherwise                = UnknownProtocol
+
+udpTrackerLoop :: PortNumber -> String -> Metainfo -> TState -> IO String
+udpTrackerLoop port peerId m st = do
+  -- h <- connectTo "exodus.desync.com" (PortNumber 6969)
+  s <- socket AF_INET Datagram defaultProtocol
+  hostAddr <- inet_addr "185.37.101.229"
+  putStrLn "connected to tracker"
+  _ <- sendTo s (toStrict $ encode (ConnectReq 42)) (SockAddrInet 2710 hostAddr)
+  putStrLn "--> sent ConnectReq to tracker"
+  resp <- recv s 16
+  putStrLn "<-- recv ConnectResp from tracker"
+  return $ show resp
index 2a2797626527c8bdc7f3f983361bed31f127e129..50478323664a58ca2f6574479c04225201ef1770 100644 (file)
@@ -14,7 +14,7 @@ import qualified Crypto.Hash.SHA1 as SHA1 (hash)
 import Control.Exception.Base (IOException, try)
 import Data.ByteString (ByteString, writeFile, hPut, hGet, take)
 import qualified Data.ByteString.Char8 as BC
-import System.IO (withFile, hSeek, IOMode(..), SeekMode(..))
+import System.IO (Handle, hSeek, SeekMode(..))
 import System.Directory (doesFileExist)
 
 splitN :: Int -> BC.ByteString -> [BC.ByteString]
@@ -36,16 +36,15 @@ createDummyFile path size = do
     return $ Right ()
 
 -- write into a file at a specific offet
-writeFileAtOffset :: FilePath -> Integer -> ByteString -> IO ()
-writeFileAtOffset path offset block =
-  withFile path ReadWriteMode (\h -> do
-                                  hSeek h AbsoluteSeek offset
-                                  hPut h block)
-readFileAtOffset :: FilePath -> Integer -> Integer -> IO ByteString
-readFileAtOffset path offset len =
-  withFile path ReadWriteMode (\h -> do
-                                  hSeek h AbsoluteSeek offset
-                                  hGet h (fromInteger len))
+writeFileAtOffset :: Handle -> Integer -> ByteString -> IO ()
+writeFileAtOffset h offset block = do
+  hSeek h AbsoluteSeek offset
+  hPut h block
+
+readFileAtOffset :: Handle -> Integer -> Integer -> IO ByteString
+readFileAtOffset h offset len = do
+  hSeek h AbsoluteSeek offset
+  hGet h (fromInteger len)
 
 verifyHash :: ByteString -> ByteString -> Bool
 verifyHash bs pieceHash =
index 4dd4c6867a92f06e0a9fdb69086aa791cfd507de..a75f01c122df050615e645c4ba9f0fb4f55563da 100644 (file)
@@ -1,22 +1,24 @@
 {-# LANGUAGE OverloadedStrings #-}
 module Main where
 
-import Prelude hiding (log, length, readFile, getContents, replicate, writeFile)
+import           Prelude hiding (log, length, readFile, getContents)
 
-import Control.Concurrent (forkIO)
-import Control.Concurrent.MVar (readMVar)
-import Data.ByteString.Char8 (ByteString, getContents, readFile, writeFile, replicate)
-import Network (PortID (PortNumber))
-import System.Environment (getArgs)
-import System.Exit (exitSuccess)
-import System.Directory (doesFileExist)
-import System.Random (getStdGen, randomRs)
-
-import FuncTorrent.Logger (initLogger, logMessage, logStop)
-import FuncTorrent.Metainfo (Info(..), Metainfo(..), torrentToMetainfo)
-import FuncTorrent.Peer (initPieceMap, handlePeerMsgs, pieceMapFromFile)
+import           Control.Concurrent (forkIO)
+import           Control.Concurrent.MVar (readMVar)
+import           Data.ByteString.Char8 (ByteString, getContents, readFile)
+import qualified FuncTorrent.FileSystem as FS (initFS, pieceMapFromFile, startThread)
+import           FuncTorrent.Logger (initLogger, logMessage, logStop)
+import           FuncTorrent.Metainfo (Info(..), Metainfo(..), torrentToMetainfo)
+import           FuncTorrent.Peer (handlePeerMsgs)
+import           FuncTorrent.PieceManager (initPieceMap)
 import qualified FuncTorrent.Server as Server
-import FuncTorrent.Tracker (connectedPeers, initialTrackerState, trackerLoop)
+import           FuncTorrent.Tracker (connectedPeers, initialTrackerState, trackerLoop, udpTrackerLoop)
+import           FuncTorrent.Utils (createDummyFile)
+import           Network (PortID (PortNumber))
+import           System.Directory (doesFileExist)
+import           System.Environment (getArgs)
+import           System.Exit (exitSuccess)
+import           System.Random (getStdGen, randomRs)
 
 logError :: String -> (String -> IO ()) -> IO ()
 logError e logMsg = logMsg $ "parse error: \n" ++ e
@@ -68,20 +70,24 @@ main = do
            pieceHash = pieces (info m)
            pLen = pieceLength (info m)
            defaultPieceMap = initPieceMap pieceHash fileLen pLen
+       log $ "create FS msg channel"
+       (handle, fsMsgChannel) <- FS.initFS filePath
        log $ "Downloading file : " ++ filePath
        dfe <- doesFileExist filePath
        pieceMap <- if dfe
                    then
-                     pieceMapFromFile filePath defaultPieceMap
+                     FS.pieceMapFromFile handle defaultPieceMap
                    else do
                      -- create a dummy file
-                     _ <- writeFile filePath (replicate (fromIntegral fileLen) '\0')
-                     return defaultPieceMap
+                     createDummyFile filePath (fromIntegral fileLen) >>
+                       return defaultPieceMap
+       log $ "start filesystem manager thread"
+       FS.startThread handle fsMsgChannel pieceMap
        log $ "starting server"
        (serverSock, (PortNumber portnum)) <- Server.start
        log $ "server started on " ++ show portnum
        log "Trying to fetch peers"
-       _ <- forkIO $ Server.run serverSock peerId m pieceMap
+       _ <- forkIO $ Server.run serverSock peerId m pieceMap fsMsgChannel
        log $ "Trackers: " ++ head (announceList m)
        -- (tstate, errstr) <- runTracker portnum peerId m
        tstate <- initialTrackerState $ lengthInBytes $ info m
@@ -89,5 +95,5 @@ main = do
        ps <- readMVar (connectedPeers tstate)
        log $ "Peers List : " ++ (show ps)
        let p1 = head ps
-       handlePeerMsgs p1 peerId m pieceMap True
+       handlePeerMsgs p1 peerId m pieceMap True fsMsgChannel
        logStop logR