#pragma once
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include "torlinkc/engine/history.hpp"
#include "torlinkc/engine/persist.hpp"
#include "torlinkc/engine/torrent_engine.hpp"
#include "torlinkc/engine/types.hpp"
namespace torlinkc {
struct AddInput {
std::string id;
std::string name;
std::string magnet;
std::string source; // empty = none
std::optional<std::int64_t> sizeBytes;
};
struct RestoreOptions {
// Safe mode: the previous boot died while restoring (see bootguard.hpp), so
// bring every item back paused and start no engines. The list stays intact
// and visible; the user resumes each item on their own terms.
bool safe = false;
};
// A real seed never pulls data off the network: verifying on-disk files reads
// the disk (network speed stays 0), only fetching *missing* data raises it.
// So sustained network download on a "seed" means its files are gone or
// partial. Ported from queue.ts's strayDownload().
bool strayDownload(std::int64_t total, double progress, int speed);
// Ported from download/queue.ts. Where the original relied on webtorrent's
// async events firing into the queue via Node's event loop, this Phase 1
// port is driven by an explicit tick(): it drains libtorrent alerts and
// refreshes every item/seed's live stats. Call tick() periodically (the
// console harness does so once a second) -- Phase 2 replaces this with a
// background alert thread posting closures onto the UI thread instead.
class DownloadQueue {
public:
explicit DownloadQueue(std::optional<int> maxDownloads = std::nullopt);
// Extra announce URLs appended to every torrent added from now on. Existing
// running torrents aren't retro-updated -- the change takes effect for the
// next add / resume / re-seed.
void setTrackers(std::vector<std::string> trackers);
std::vector<QueueItem> getItems() const;
int activeCount() const;
bool has(const std::string& id) const;
void add(const AddInput& input, const std::string& dir);
// Advances the engine: drains alerts (metadata/done/error) and refreshes
// progress for every downloading item and seeding seed. Call periodically.
void tick();
void pause(const std::string& id);
void resume(const std::string& id);
void togglePause(const std::string& id);
std::optional<std::string> exportTorrentFile(const std::string& id);
// Fetches the .torrent metadata for a magnet-only result and exports it to
// exportDir. Blocking (bounded by a timeout), driven by repeatedly polling
// the engine -- Phase 1 has no background thread to await this on.
std::optional<std::string> fetchAndExportTorrent(const AddInput& input, const std::string& exportDir);
void cancel(const std::string& id);
bool remove(const std::string& id, bool deleteFiles = false);
void retry(const std::string& id);
void retryFailed();
std::optional<SeedItem> getSeed(const std::string& id) const;
std::vector<SeedItem> getSeeds() const;
int seedingCount() const;
void startSeeding(const HistoryItem& h);
void stopSeeding(const std::string& id);
void toggleSeeding(const HistoryItem& h);
void restoreSeeds(const std::vector<SeedRecord>& records, RestoreOptions opts = {});
void restore(std::vector<QueueItem> items, RestoreOptions opts = {});
void restoreHistory(std::vector<HistoryItem> items);
std::vector<HistoryItem> getHistory() const;
void removeHistory(const std::string& id);
void clearHistory();
// Synchronously flushes every state file from current memory. Touches no
// engine state, so it can never block shutdown.
void persistSync();
void suspend();
// Fired synchronously wherever the TS original called `this.emit(...)`.
// Unset by default (Phase 1's console harness polls getItems() directly);
// Phase 2 wires these into the FTXUI redraw pipeline.
std::function<void()> onUpdate;
std::function<void(const std::string&)> onCompleted;
private:
std::unordered_map<std::string, QueueItem> items_;
TorrentEngine engine_;
std::vector<HistoryItem> history_;
std::unordered_map<std::string, SeedItem> seeds_;
std::unordered_map<std::string, int> strayHits_;
std::unordered_map<std::string, std::int64_t> seedStartedAt_;
std::vector<std::string> trackers_;
int maxDownloads_ = 0; // 0 = unlimited
void startEngine(QueueItem& item);
void promote();
AddHandlers engineHandlers(std::string id);
void completeItem(QueueItem it);
void beginSeed(const QueueItem& it);
void restorePaused(const HistoryItem& h);
std::vector<SeedRecord> seedRecords() const;
void persistSeeds() const;
void persistQueue() const;
void recordHistory(const QueueItem& it);
void changed();
};
} // namespace torlinkc