public async Task DownloadAsync(CancellationToken token)
        {
            // Torrents will be downloaded to this directory
            var downloadsPath = Path.Combine(Environment.CurrentDirectory, "Downloads");

            // .torrent files will be loaded from this directory (if any exist)
            var torrentsPath = Path.Combine(Environment.CurrentDirectory, "Torrents");

#if DEBUG
            LoggerFactory.Register(className => new TextLogger(Console.Out, className));
#endif

            // If the torrentsPath does not exist, we want to create it
            if (!Directory.Exists(torrentsPath))
            {
                Directory.CreateDirectory(torrentsPath);
            }

            // For each file in the torrents path that is a .torrent file, load it into the engine.
            foreach (string file in Directory.GetFiles(torrentsPath))
            {
                if (file.EndsWith(".torrent", StringComparison.OrdinalIgnoreCase))
                {
                    try {
                        // EngineSettings.AutoSaveLoadFastResume is enabled, so any cached fast resume
                        // data will be implicitly loaded. If fast resume data is found, the 'hash check'
                        // phase of starting a torrent can be skipped.
                        //
                        // TorrentSettingsBuilder can be used to modify the settings for this
                        // torrent.
                        var settingsBuilder = new TorrentSettingsBuilder {
                            MaximumConnections = 60,
                        };
                        var manager = await Engine.AddAsync(file, downloadsPath, settingsBuilder.ToSettings());

                        manager.PeersFound += Manager_PeersFound;
                        Console.WriteLine(manager.InfoHash.ToHex());
                    } catch (Exception e) {
                        Console.Write("Couldn't decode {0}: ", file);
                        Console.WriteLine(e.Message);
                    }
                }
            }

            // If we loaded no torrents, just exist. The user can put files in the torrents directory and start
            // the client again
            if (Engine.Torrents.Count == 0)
            {
                Console.WriteLine($"No torrents found in '{torrentsPath}'");
                Console.WriteLine("Exiting...");
                return;
            }

            // For each torrent manager we loaded and stored in our list, hook into the events
            // in the torrent manager and start the engine.
            foreach (TorrentManager manager in Engine.Torrents)
            {
                manager.PeerConnected += (o, e) => {
                    lock (Listener)
                        Listener.WriteLine($"Connection succeeded: {e.Peer.Uri}");
                };
                manager.ConnectionAttemptFailed += (o, e) => {
                    lock (Listener)
                        Listener.WriteLine(
                            $"Connection failed: {e.Peer.ConnectionUri} - {e.Reason}");
                };
                // Every time a piece is hashed, this is fired.
                manager.PieceHashed += delegate(object o, PieceHashedEventArgs e) {
                    lock (Listener)
                        Listener.WriteLine($"Piece Hashed: {e.PieceIndex} - {(e.HashPassed ? "Pass" : "Fail")}");
                };

                // Every time the state changes (Stopped -> Seeding -> Downloading -> Hashing) this is fired
                manager.TorrentStateChanged += delegate(object o, TorrentStateChangedEventArgs e) {
                    lock (Listener)
                        Listener.WriteLine($"OldState: {e.OldState} NewState: {e.NewState}");
                };

                // Every time the tracker's state changes, this is fired
                manager.TrackerManager.AnnounceComplete += (sender, e) => {
                    Listener.WriteLine($"{e.Successful}: {e.Tracker}");
                };

                // Start the torrentmanager. The file will then hash (if required) and begin downloading/seeding.
                // As EngineSettings.AutoSaveLoadDhtCache is enabled, any cached data will be loaded into the
                // Dht engine when the first torrent is started, enabling it to bootstrap more rapidly.
                await manager.StartAsync();
            }

            // While the torrents are still running, print out some stats to the screen.
            // Details for all the loaded torrent managers are shown.
            StringBuilder sb = new StringBuilder(1024);
            while (Engine.IsRunning)
            {
                sb.Remove(0, sb.Length);

                AppendFormat(sb, $"Transfer Rate:      {Engine.TotalDownloadSpeed / 1024.0:0.00}kB/sec down / {Engine.TotalUploadSpeed / 1024.0:0.00}kB/sec up");
                AppendFormat(sb, $"Memory Cache:       {Engine.DiskManager.CacheBytesUsed / 1024.0:0.00}/{Engine.Settings.DiskCacheBytes / 1024.0:0.00} kB");
                AppendFormat(sb, $"Disk IO Rate:       {Engine.DiskManager.ReadRate / 1024.0:0.00} kB/s read / {Engine.DiskManager.WriteRate / 1024.0:0.00} kB/s write");
                AppendFormat(sb, $"Disk IO Total:      {Engine.DiskManager.TotalBytesRead / 1024.0:0.00} kB read / {Engine.DiskManager.TotalBytesWritten / 1024.0:0.00} kB written");
                AppendFormat(sb, $"Open Connections:   {Engine.ConnectionManager.OpenConnections}");

                // Print out the port mappings
                foreach (var mapping in Engine.PortMappings.Created)
                {
                    AppendFormat(sb, $"Successful Mapping    {mapping.PublicPort}:{mapping.PrivatePort} ({mapping.Protocol})");
                }
                foreach (var mapping in Engine.PortMappings.Failed)
                {
                    AppendFormat(sb, $"Failed mapping:       {mapping.PublicPort}:{mapping.PrivatePort} ({mapping.Protocol})");
                }
                foreach (var mapping in Engine.PortMappings.Pending)
                {
                    AppendFormat(sb, $"Pending mapping:      {mapping.PublicPort}:{mapping.PrivatePort} ({mapping.Protocol})");
                }

                foreach (TorrentManager manager in Engine.Torrents)
                {
                    AppendSeparator(sb);
                    AppendFormat(sb, $"State:              {manager.State}");
                    AppendFormat(sb, $"Name:               {(manager.Torrent == null ? "MetaDataMode" : manager.Torrent.Name)}");
                    AppendFormat(sb, $"Progress:           {manager.Progress:0.00}");
                    AppendFormat(sb, $"Transfer Rate:      {manager.Monitor.DownloadSpeed / 1024.0:0.00}kB/s down / {manager.Monitor.UploadSpeed / 1024.0:0.00} kB/s up");
                    AppendFormat(sb, $"Total transferred:  {manager.Monitor.DataBytesDownloaded / (1024.0 * 1024.0):0.00} MB down / {manager.Monitor.DataBytesUploaded / (1024.0 * 1024.0):0.00} MB up");
                    AppendFormat(sb, $"Tracker Status");
                    foreach (var tier in manager.TrackerManager.Tiers)
                    {
                        AppendFormat(sb, $"\t{tier.ActiveTracker} : Announce Succeeded: {tier.LastAnnounceSucceeded}. Scrape Succeeded: {tier.LastScrapeSucceeded}.");
                    }

                    if (manager.PieceManager != null)
                    {
                        AppendFormat(sb, "Current Requests:   {0}", await manager.PieceManager.CurrentRequestCountAsync());
                    }

                    var peers = await manager.GetPeersAsync();

                    AppendFormat(sb, "Outgoing:");
                    foreach (PeerId p in peers.Where(t => t.ConnectionDirection == Direction.Outgoing))
                    {
                        AppendFormat(sb, "\t{2} - {1:0.00}/{3:0.00}kB/sec - {0} - {4} ({5})", p.Uri,
                                     p.Monitor.DownloadSpeed / 1024.0,
                                     p.AmRequestingPiecesCount,
                                     p.Monitor.UploadSpeed / 1024.0,
                                     p.EncryptionType,
                                     string.Join("|", p.SupportedEncryptionTypes.Select(t => t.ToString()).ToArray()));
                    }
                    AppendFormat(sb, "");
                    AppendFormat(sb, "Incoming:");
                    foreach (PeerId p in peers.Where(t => t.ConnectionDirection == Direction.Incoming))
                    {
                        AppendFormat(sb, "\t{2} - {1:0.00}/{3:0.00}kB/sec - {0} - {4} ({5})", p.Uri,
                                     p.Monitor.DownloadSpeed / 1024.0,
                                     p.AmRequestingPiecesCount,
                                     p.Monitor.UploadSpeed / 1024.0,
                                     p.EncryptionType,
                                     string.Join("|", p.SupportedEncryptionTypes.Select(t => t.ToString()).ToArray()));
                    }

                    AppendFormat(sb, "", null);
                    if (manager.Torrent != null)
                    {
                        foreach (var file in manager.Files)
                        {
                            AppendFormat(sb, "{1:0.00}% - {0}", file.Path, file.BitField.PercentComplete);
                        }
                    }
                }
                Console.Clear();
                Console.WriteLine(sb.ToString());
                Listener.ExportTo(Console.Out);

                await Task.Delay(5000, token);
            }
        }
        protected override async Task HandleStartAsync()
        {
            await ValidateCacheFolderAsync();

            if (manager == null)
            {
                var realTarget = (TorrentTarget)DownloadTask.Target;
                manager = engine.Torrents.FirstOrDefault(m =>
                                                         m.SavePath.Equals(cacheFolder.Path) &&
                                                         m.Torrent.Equals(realTarget.Torrent)
                                                         );

                if (manager == null)
                {
                    TorrentSettings settings = new TorrentSettingsBuilder()
                    {
                        MaximumConnections   = maximumConnections,
                        MaximumDownloadSpeed = maximumDownloadSpeed,
                        MaximumUploadSpeed   = maximumUploadSpeed,
                        UploadSlots          = uploadSlots,
                    }.ToSettings();
                    manager = await engine.AddAsync(
                        realTarget.Torrent, cacheFolder.Path, settings);

                    if (announceUrls != null)
                    {
                        foreach (var url in announceUrls.Take(10))
                        {
                            try { await manager.TrackerManager.AddTrackerAsync(new Uri(url)); }
                            catch (Exception) { }
                        }
                    }
                    foreach (var file in manager.Files)
                    {
                        await manager.SetFilePriorityAsync(file,
                                                           realTarget.IsFileSelected(file)?
                                                           Priority.Normal : Priority.DoNotDownload);
                    }
                }

                manager.TorrentStateChanged += ManagerTorrentStateChanged;
                RegisterDebugMessages();
            }

            await manager.StartAsync();

            cancellationTokenSource = new CancellationTokenSource();
            var token = cancellationTokenSource.Token;

            downloadTask = new Task(async() =>
            {
                Speed.IsEnabled = true;

                try
                {
                    var mprog = Progress as BaseMeasurableProgress;
                    while (!token.IsCancellationRequested)
                    {
                        try
                        {
                            await Task.Delay(ProgressUpdateInterval);
                            long nowVal = (long)(manager.PartialProgress / 100.0 * mprog.TotalSize);
                            long delta  = nowVal - mprog.DownloadedSize;
                            if (delta < 0)
                            {
                                delta = nowVal;
                                mprog.Reset();
                            }
                            mprog.Increase(delta);
                        }
                        catch (Exception) { }
                    }
                }
                finally
                {
                    Speed.IsEnabled = false;
                }
            });

            downloadTask.RunSynchronously();
        }