public void DownloadTorrent(string torrentPath, string saveDir, int port)
        {
            var    torrent     = _torrentFactory.GetTorrentFromFile(torrentPath);
            var    fileName    = Path.GetFileNameWithoutExtension(torrent.Info.Name);
            string fullSaveDir = Path.Combine(saveDir, fileName);

            lock (_pieceLock)
            {
                foreach (var pieceIndex in Enumerable.Range(0, torrent.Info.PieceHashes.Count))
                {
                    var pieceLength = torrent.Info.PieceLength;
                    if (pieceIndex == torrent.Info.PieceHashes.Count - 1) //Last piece is odd length
                    {
                        var tempPieceLength = torrent.Info.Length % pieceLength;
                        if (tempPieceLength != 0)
                        {
                            pieceLength = tempPieceLength;
                        }
                    }

                    var piece = new TorrentPiece(pieceIndex, pieceLength, fullSaveDir, torrent.Info.PieceHashes[pieceIndex]);
                    _pieces.Add(piece);
                }
            }

            var tracker       = new Tracker(_httpClientHelper, _bencodeParser, _trackerResponseFactory, _peerId, port); //Technically needs a factory for testing
            var peerConnector = new PeerConnector(_peerEventDataFactory, _tcpSocketHelper, _peerId, torrent.Info);      //Technically needs a factory for testing

            while (GetNonCompletedPieces().Any())
            {
                try
                {
                    ConnectNewPeersIfNeeded(peerConnector, tracker, torrent);

                    var neededPieces = GetAvailableForDownloadPieces();
                    foreach (var peer in peerConnector.Peers)
                    {
                        var neededPiecesIds = neededPieces
                                              .Select(p => p.PieceIndex)
                                              .ToList();

                        var nullablePieceIndex = peer.RetrievePieceIndexIfAny(neededPiecesIds);
                        if (nullablePieceIndex == null)
                        {
                            if (peer.AmInterested)
                            {
                                peer.SendInterest(false);
                            }

                            continue;
                        }

                        if (!peer.AmInterested)
                        {
                            peer.SendInterest(true);
                            continue;
                        }

                        if (!peer.PeerChocking && !peer.AmWaitingForPiece)
                        {
                            var pieceIndex  = nullablePieceIndex.Value;
                            var neededPiece = neededPieces.Find(p => p.PieceIndex == pieceIndex);
                            neededPieces.Remove(neededPiece);

                            lock (_pieceLock)
                            {
                                _pendingPieces.Add(neededPiece, peer);
                                var block = neededPiece.GetNextBlock();
                                peer.RequestPiece((uint)pieceIndex, block.Offset, block.Length);
                            }
                            continue;
                        }

                        //peer.SendKeepAlive();
                    }

                    FreeBlockedPieces(peerConnector);
                }
                //TODO: Bad practice
                catch (Exception ex)
                { }
            }

            var fullFilePath = Path.Combine(fullSaveDir, torrent.Info.Name);

            CombinePiecesIntoFile(fullFilePath);

            CleanupResources(peerConnector);

            IsDownloadCompleted = true;
        }
 private bool IsCompleteOrPending(TorrentPiece piece)
 {
     return(piece.Complete || PendingPieces.TryGetValue(piece, out var tempPeer));
 }