// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.

#include <algorithm>
#include <array>
#include <cstddef>
#include <iterator> // for std::distance()
#include <optional>
#include <string_view>
#include <vector>

#include "libtransmission/quark.h"

using namespace std::literals;

namespace
{

auto constexpr MyStatic = std::array<std::string_view, TR_N_KEYS>{
    ""sv,
    "activeTorrentCount"sv,
    "activity-date"sv,
    "activityDate"sv,
    "added"sv,
    "added-date"sv,
    "added.f"sv,
    "added6"sv,
    "added6.f"sv,
    "addedDate"sv,
    "address"sv,
    "alt-speed-down"sv,
    "alt-speed-enabled"sv,
    "alt-speed-time-begin"sv,
    "alt-speed-time-day"sv,
    "alt-speed-time-enabled"sv,
    "alt-speed-time-end"sv,
    "alt-speed-up"sv,
    "announce"sv,
    "announce-ip"sv,
    "announce-ip-enabled"sv,
    "announce-list"sv,
    "announceState"sv,
    "anti-brute-force-enabled"sv,
    "anti-brute-force-threshold"sv,
    "arguments"sv,
    "availability"sv,
    "bandwidth-priority"sv,
    "bandwidthPriority"sv,
    "begin_piece"sv,
    "bind-address-ipv4"sv,
    "bind-address-ipv6"sv,
    "bitfield"sv,
    "blocklist-date"sv,
    "blocklist-enabled"sv,
    "blocklist-size"sv,
    "blocklist-updates-enabled"sv,
    "blocklist-url"sv,
    "blocks"sv,
    "bytesCompleted"sv,
    "cache-size-mb"sv,
    "clientIsChoked"sv,
    "clientIsInterested"sv,
    "clientName"sv,
    "comment"sv,
    "comment_utf_8"sv,
    "compact-view"sv,
    "complete"sv,
    "config-dir"sv,
    "cookies"sv,
    "corrupt"sv,
    "corruptEver"sv,
    "created by"sv,
    "created by.utf-8"sv,
    "creation date"sv,
    "creator"sv,
    "cumulative-stats"sv,
    "current-stats"sv,
    "date"sv,
    "dateCreated"sv,
    "default-trackers"sv,
    "delete-local-data"sv,
    "desiredAvailable"sv,
    "destination"sv,
    "details-window-height"sv,
    "details-window-width"sv,
    "dht-enabled"sv,
    "dnd"sv,
    "done-date"sv,
    "doneDate"sv,
    "download-dir"sv,
    "download-dir-free-space"sv,
    "download-queue-enabled"sv,
    "download-queue-size"sv,
    "downloadCount"sv,
    "downloadDir"sv,
    "downloadLimit"sv,
    "downloadLimited"sv,
    "downloadSpeed"sv,
    "downloaded"sv,
    "downloaded-bytes"sv,
    "downloadedBytes"sv,
    "downloadedEver"sv,
    "downloaders"sv,
    "downloading-time-seconds"sv,
    "dropped"sv,
    "dropped6"sv,
    "e"sv,
    "editDate"sv,
    "encoding"sv,
    "encryption"sv,
    "end_piece"sv,
    "error"sv,
    "errorString"sv,
    "eta"sv,
    "etaIdle"sv,
    "fields"sv,
    "file-count"sv,
    "fileStats"sv,
    "filename"sv,
    "files"sv,
    "files-added"sv,
    "files-unwanted"sv,
    "files-wanted"sv,
    "filesAdded"sv,
    "filter-mode"sv,
    "filter-text"sv,
    "filter-trackers"sv,
    "flagStr"sv,
    "flags"sv,
    "format"sv,
    "fromCache"sv,
    "fromDht"sv,
    "fromIncoming"sv,
    "fromLpd"sv,
    "fromLtep"sv,
    "fromPex"sv,
    "fromTracker"sv,
    "group"sv,
    "hasAnnounced"sv,
    "hasScraped"sv,
    "hashString"sv,
    "have"sv,
    "haveUnchecked"sv,
    "haveValid"sv,
    "honorsSessionLimits"sv,
    "host"sv,
    "id"sv,
    "id_timestamp"sv,
    "idle-limit"sv,
    "idle-mode"sv,
    "idle-seeding-limit"sv,
    "idle-seeding-limit-enabled"sv,
    "ids"sv,
    "incomplete"sv,
    "incomplete-dir"sv,
    "incomplete-dir-enabled"sv,
    "info"sv,
    "inhibit-desktop-hibernation"sv,
    "ip_protocol"sv,
    "ipv4"sv,
    "ipv6"sv,
    "isBackup"sv,
    "isDownloadingFrom"sv,
    "isEncrypted"sv,
    "isFinished"sv,
    "isIncoming"sv,
    "isPrivate"sv,
    "isStalled"sv,
    "isUTP"sv,
    "isUploadingTo"sv,
    "labels"sv,
    "lastAnnouncePeerCount"sv,
    "lastAnnounceResult"sv,
    "lastAnnounceStartTime"sv,
    "lastAnnounceSucceeded"sv,
    "lastAnnounceTime"sv,
    "lastAnnounceTimedOut"sv,
    "lastScrapeResult"sv,
    "lastScrapeStartTime"sv,
    "lastScrapeSucceeded"sv,
    "lastScrapeTime"sv,
    "lastScrapeTimedOut"sv,
    "leecherCount"sv,
    "leftUntilDone"sv,
    "length"sv,
    "location"sv,
    "lpd-enabled"sv,
    "m"sv,
    "magnetLink"sv,
    "main-window-height"sv,
    "main-window-is-maximized"sv,
    "main-window-layout-order"sv,
    "main-window-width"sv,
    "main-window-x"sv,
    "main-window-y"sv,
    "manualAnnounceTime"sv,
    "max-peers"sv,
    "maxConnectedPeers"sv,
    "memory-bytes"sv,
    "memory-units"sv,
    "message-level"sv,
    "metadataPercentComplete"sv,
    "metadata_size"sv,
    "metainfo"sv,
    "method"sv,
    "min_request_interval"sv,
    "move"sv,
    "msg_type"sv,
    "mtimes"sv,
    "name"sv,
    "name.utf-8"sv,
    "nextAnnounceTime"sv,
    "nextScrapeTime"sv,
    "nodes"sv,
    "nodes6"sv,
    "open-dialog-dir"sv,
    "p"sv,
    "path"sv,
    "path.utf-8"sv,
    "paused"sv,
    "pausedTorrentCount"sv,
    "peer-congestion-algorithm"sv,
    "peer-limit"sv,
    "peer-limit-global"sv,
    "peer-limit-per-torrent"sv,
    "peer-port"sv,
    "peer-port-random-high"sv,
    "peer-port-random-low"sv,
    "peer-port-random-on-start"sv,
    "peer-socket-tos"sv,
    "peerIsChoked"sv,
    "peerIsInterested"sv,
    "peers"sv,
    "peers2"sv,
    "peers2-6"sv,
    "peersConnected"sv,
    "peersFrom"sv,
    "peersGettingFromUs"sv,
    "peersSendingToUs"sv,
    "percentComplete"sv,
    "percentDone"sv,
    "pex-enabled"sv,
    "pidfile"sv,
    "piece"sv,
    "piece length"sv,
    "pieceCount"sv,
    "pieceSize"sv,
    "pieces"sv,
    "play-download-complete-sound"sv,
    "port"sv,
    "port-forwarding-enabled"sv,
    "port-is-open"sv,
    "preallocation"sv,
    "preferred_transport"sv,
    "primary-mime-type"sv,
    "priorities"sv,
    "priority"sv,
    "priority-high"sv,
    "priority-low"sv,
    "priority-normal"sv,
    "private"sv,
    "progress"sv,
    "prompt-before-exit"sv,
    "proxy_url"sv,
    "queue-move-bottom"sv,
    "queue-move-down"sv,
    "queue-move-top"sv,
    "queue-move-up"sv,
    "queue-stalled-enabled"sv,
    "queue-stalled-minutes"sv,
    "queuePosition"sv,
    "rateDownload"sv,
    "rateToClient"sv,
    "rateToPeer"sv,
    "rateUpload"sv,
    "ratio-limit"sv,
    "ratio-limit-enabled"sv,
    "ratio-mode"sv,
    "read-clipboard"sv,
    "recent-download-dir-1"sv,
    "recent-download-dir-2"sv,
    "recent-download-dir-3"sv,
    "recent-download-dir-4"sv,
    "recent-relocate-dir-1"sv,
    "recent-relocate-dir-2"sv,
    "recent-relocate-dir-3"sv,
    "recent-relocate-dir-4"sv,
    "recheckProgress"sv,
    "remote-session-enabled"sv,
    "remote-session-host"sv,
    "remote-session-https"sv,
    "remote-session-password"sv,
    "remote-session-port"sv,
    "remote-session-requres-authentication"sv,
    "remote-session-username"sv,
    "removed"sv,
    "rename-partial-files"sv,
    "reqq"sv,
    "result"sv,
    "rpc-authentication-required"sv,
    "rpc-bind-address"sv,
    "rpc-enabled"sv,
    "rpc-host-whitelist"sv,
    "rpc-host-whitelist-enabled"sv,
    "rpc-password"sv,
    "rpc-port"sv,
    "rpc-socket-mode"sv,
    "rpc-url"sv,
    "rpc-username"sv,
    "rpc-version"sv,
    "rpc-version-minimum"sv,
    "rpc-version-semver"sv,
    "rpc-whitelist"sv,
    "rpc-whitelist-enabled"sv,
    "scrape"sv,
    "scrape-paused-torrents-enabled"sv,
    "scrapeState"sv,
    "script-torrent-added-enabled"sv,
    "script-torrent-added-filename"sv,
    "script-torrent-done-enabled"sv,
    "script-torrent-done-filename"sv,
    "script-torrent-done-seeding-enabled"sv,
    "script-torrent-done-seeding-filename"sv,
    "seconds-active"sv,
    "secondsActive"sv,
    "secondsDownloading"sv,
    "secondsSeeding"sv,
    "seed-queue-enabled"sv,
    "seed-queue-size"sv,
    "seedIdleLimit"sv,
    "seedIdleMode"sv,
    "seedRatioLimit"sv,
    "seedRatioLimited"sv,
    "seedRatioMode"sv,
    "seederCount"sv,
    "seeding-time-seconds"sv,
    "sequential_download"sv,
    "session-count"sv,
    "session-id"sv,
    "sessionCount"sv,
    "show-backup-trackers"sv,
    "show-extra-peer-details"sv,
    "show-filterbar"sv,
    "show-notification-area-icon"sv,
    "show-options-window"sv,
    "show-statusbar"sv,
    "show-toolbar"sv,
    "show-tracker-scrapes"sv,
    "sitename"sv,
    "size-bytes"sv,
    "size-units"sv,
    "sizeWhenDone"sv,
    "sleep-per-seconds-during-verify"sv,
    "socket_address"sv,
    "sort-mode"sv,
    "sort-reversed"sv,
    "source"sv,
    "speed"sv,
    "speed-Bps"sv,
    "speed-bytes"sv,
    "speed-limit-down"sv,
    "speed-limit-down-enabled"sv,
    "speed-limit-up"sv,
    "speed-limit-up-enabled"sv,
    "speed-units"sv,
    "start-added-torrents"sv,
    "start-minimized"sv,
    "startDate"sv,
    "start_paused"sv,
    "status"sv,
    "statusbar-stats"sv,
    "tag"sv,
    "tcp-enabled"sv,
    "tier"sv,
    "time-checked"sv,
    "torrent-added"sv,
    "torrent-added-notification-command"sv,
    "torrent-added-notification-enabled"sv,
    "torrent-added-verify-mode"sv,
    "torrent-complete-notification-command"sv,
    "torrent-complete-notification-enabled"sv,
    "torrent-complete-sound-command"sv,
    "torrent-complete-sound-enabled"sv,
    "torrent-duplicate"sv,
    "torrent-get"sv,
    "torrent-set"sv,
    "torrent-set-location"sv,
    "torrentCount"sv,
    "torrentFile"sv,
    "torrents"sv,
    "totalSize"sv,
    "total_size"sv,
    "trackerAdd"sv,
    "trackerList"sv,
    "trackerRemove"sv,
    "trackerReplace"sv,
    "trackerStats"sv,
    "trackers"sv,
    "trash-can-enabled"sv,
    "trash-original-torrent-files"sv,
    "umask"sv,
    "units"sv,
    "upload-slots-per-torrent"sv,
    "uploadLimit"sv,
    "uploadLimited"sv,
    "uploadRatio"sv,
    "uploadSpeed"sv,
    "upload_only"sv,
    "uploaded"sv,
    "uploaded-bytes"sv,
    "uploadedBytes"sv,
    "uploadedEver"sv,
    "url-list"sv,
    "use-global-speed-limit"sv,
    "use-speed-limit"sv,
    "user-has-given-informed-consent"sv,
    "ut_comment"sv,
    "ut_holepunch"sv,
    "ut_metadata"sv,
    "ut_pex"sv,
    "ut_recommend"sv,
    "utp-enabled"sv,
    "v"sv,
    "version"sv,
    "wanted"sv,
    "watch-dir"sv,
    "watch-dir-enabled"sv,
    "watch-dir-force-generic"sv,
    "webseeds"sv,
    "webseedsSendingToUs"sv,
    "yourip"sv,
};

bool constexpr quarks_are_sorted()
{
    for (size_t i = 1; i < std::size(MyStatic); ++i)
    {
        if (MyStatic[i - 1] >= MyStatic[i])
        {
            return false;
        }
    }

    return true;
}

static_assert(quarks_are_sorted(), "Predefined quarks must be sorted by their string value");
static_assert(std::size(MyStatic) == TR_N_KEYS);

auto& my_runtime{ *new std::vector<std::string_view>{} };

} // namespace

std::optional<tr_quark> tr_quark_lookup(std::string_view key)
{
    // is it in our static array?
    auto constexpr Sbegin = std::begin(MyStatic);
    auto constexpr Send = std::end(MyStatic);

    if (auto const sit = std::lower_bound(Sbegin, Send, key); sit != Send && *sit == key)
    {
        return std::distance(Sbegin, sit);
    }

    /* was it added during runtime? */
    auto const rbegin = std::begin(my_runtime);
    auto const rend = std::end(my_runtime);
    if (auto const rit = std::find(rbegin, rend, key); rit != rend)
    {
        return TR_N_KEYS + std::distance(rbegin, rit);
    }

    return {};
}

tr_quark tr_quark_new(std::string_view str)
{
    if (auto const prior = tr_quark_lookup(str); prior)
    {
        return *prior;
    }

    auto const ret = TR_N_KEYS + std::size(my_runtime);
    auto const len = std::size(str);
    auto* perma = new char[len + 1];
    std::copy_n(std::begin(str), len, perma);
    perma[len] = '\0';
    my_runtime.emplace_back(perma);
    return ret;
}

std::string_view tr_quark_get_string_view(tr_quark q)
{
    return q < TR_N_KEYS ? MyStatic[q] : my_runtime[q - TR_N_KEYS];
}
