#include "protocols.hpp"
#include <QString>
#include <ranges>

ProtocolStore::ProtocolStore() noexcept {
    // The format for this table is as follows:
    // add({ProtocolSource, ProtocolStatus, "Protocol common name", "protocol-xml-file-name"}, {"server_global_name_1", ...});
    // Any and all globals that would be exposed by the Wayland SERVER must be added.
    // Client-side globals must be excluded.

    // clang-format off
    add({UPSTREAM, STABLE, "Linux DMA-BUF", "linux-dmabuf-v1"}, {"zwp_linux_dmabuf_v1"});
    add({UPSTREAM, STABLE, "Presentation time", "presentation-time"}, {"wp_presentation"});
    add({UPSTREAM, STABLE, "Tablet", "tablet-v2"}, {"zwp_tablet_manager_v2"});
    add({UPSTREAM, STABLE, "Viewporter", "viewporter"}, {"wp_viewporter"});
    add({UPSTREAM, STABLE, "XDG shell", "xdg-shell"}, {"xdg_wm_base"});

    add({UPSTREAM, STAGING, "Color management", "color-management-v1"}, {"wp_color_manager_v1"});
    add({UPSTREAM, STAGING, "Alpha modifier", "alpha-modifier-v1"}, {"wp_alpha_modifier_v1"});
    add({UPSTREAM, STAGING, "Color representation", "color-representation-v1"}, {"wp_color_representation_manager_v1"});
    add({UPSTREAM, STAGING, "Commit timing", "commit-timing-v1"}, {"wp_commit_timing_manager_v1"});
    add({UPSTREAM, STAGING, "Content type hint", "content-type-v1"}, {"wp_content_type_manager_v1"});
    add({UPSTREAM, STAGING, "Cursor shape", "cursor-shape-v1"}, {"wp_cursor_shape_manager_v1"});
    add({UPSTREAM, STAGING, "Data control", "ext-data-control-v1"}, {"ext_data_control_manager_v1"});
    add({UPSTREAM, STAGING, "DRM lease", "drm-lease-v1"}, {"wp_drm_lease_device_v1"});
    add({UPSTREAM, STAGING, "Background effect", "ext-background-effect-v1"}, {"ext_background_effect_manager_v1"});
    add({UPSTREAM, STAGING, "FIFO", "fifo-v1"}, {"wp_fifo_manager_v1"});
    add({UPSTREAM, STAGING, "Image capture source", "ext-image-capture-source-v1"}, {"ext_output_image_capture_source_manager_v1", "ext_foreign_toplevel_image_capture_source_manager_v1"});
    add({UPSTREAM, STAGING, "Image copy capture", "ext-image-copy-capture-v1"}, {"ext_image_copy_capture_manager_v1"});
    add({UPSTREAM, STAGING, "Foreign toplevel list", "ext-foreign-toplevel-list-v1"}, {"ext_foreign_toplevel_list_v1"});
    add({UPSTREAM, STAGING, "Fractional scale", "fractional-scale-v1"}, {"wp_fractional_scale_manager_v1"});
    add({UPSTREAM, STAGING, "Idle notify", "ext-idle-notify-v1"}, {"ext_idle_notifier_v1"});
    add({UPSTREAM, STAGING, "Linux DRM syncobj", "linux-drm-syncobj-v1"}, {"wp_linux_drm_syncobj_manager_v1"});
    add({UPSTREAM, STAGING, "Pointer warp", "pointer-warp-v1"}, {"wp_pointer_warp_v1"});
    add({UPSTREAM, STAGING, "Security context", "security-context-v1"}, {"wp_security_context_manager_v1"});
    add({UPSTREAM, STAGING, "Session lock", "ext-session-lock-v1"}, {"ext_session_lock_manager_v1"});
    add({UPSTREAM, STAGING, "Single-pixel buffer", "single-pixel-buffer-v1"}, {"wp_single_pixel_buffer_manager_v1"});
    add({UPSTREAM, STAGING, "Tearing control", "tearing-control-v1"}, {"wp_tearing_control_manager_v1"});
    add({UPSTREAM, STAGING, "Transient seat", "ext-transient-seat-v1"}, {"ext_transient_seat_manager_v1"});
    add({UPSTREAM, STAGING, "Workspace", "ext-workspace-v1"}, {"ext_workspace_manager_v1"});
    add({UPSTREAM, STAGING, "XDG activation", "xdg-activation-v1"}, {"xdg_activation_v1"});
    add({UPSTREAM, STAGING, "XDG dialog", "xdg-dialog-v1"}, {"xdg_wm_dialog_v1"});
    add({UPSTREAM, STAGING, "XDG system bell", "xdg-system-bell-v1"}, {"xdg_system_bell_v1"});
    add({UPSTREAM, STAGING, "XDG toplevel drag", "xdg-toplevel-drag-v1"}, {"xdg_toplevel_drag_manager_v1"});
    add({UPSTREAM, STAGING, "XDG toplevel icon", "xdg-toplevel-icon-v1"}, {"xdg_toplevel_icon_manager_v1"});
    add({UPSTREAM, STAGING, "XDG toplevel tag", "xdg-toplevel-tag-v1"}, {"xdg_toplevel_tag_manager_v1"});
    add({UPSTREAM, STAGING, "Xwayland shell", "xwayland-shell-v1"}, {"xwayland_shell_v1"});

    add({UPSTREAM, UNSTABLE, "Fullscreen shell", "fullscreen-shell-unstable-v1"}, {"zwp_fullscreen_shell_v1"});
    add({UPSTREAM, UNSTABLE, "Idle inhibit", "idle-inhibit-unstable-v1"}, {"zwp_idle_inhibit_manager_v1"});
    add({UPSTREAM, UNSTABLE, "Input method", "input-method-unstable-v1"}, {"zwp_input_method_context_v1"});
    add({UPSTREAM, UNSTABLE, "Input timestamps", "input-timestamps-unstable-v1"}, {"zwp_input_timestamps_manager_v1"});
    add({UPSTREAM, UNSTABLE, "Keyboard shortcuts inhibit", "keyboard-shortcuts-inhibit-unstable-v1"}, {"zwp_keyboard_shortcuts_inhibit_manager_v1"});
    add({UPSTREAM, UNSTABLE, "Linux explicit synchronization", "linux-explicit-synchronization-unstable-v1"}, {"zwp_linux_explicit_synchronization_v1"});
    add({UPSTREAM, UNSTABLE, "Pointer constraints", "pointer-constraints-unstable-v1"}, {"zwp_pointer_constraints_v1"});
    add({UPSTREAM, UNSTABLE, "Pointer gestures", "pointer-gestures-unstable-v1"}, {"zwp_pointer_gestures_v1"});
    add({UPSTREAM, UNSTABLE, "Primary selection", "primary-selection-unstable-v1"}, {"zwp_primary_selection_device_manager_v1"});
    add({UPSTREAM, UNSTABLE, "Relative pointer", "relative-pointer-unstable-v1"}, {"zwp_relative_pointer_manager_v1"});
    add({UPSTREAM, UNSTABLE, "Text input", "text-input-unstable-v3"}, {"zwp_text_input_manager_v3"});
    add({UPSTREAM, UNSTABLE, "XDG decoration", "xdg-decoration-unstable-v1"}, {"zxdg_decoration_manager_v1"});
    add({UPSTREAM, UNSTABLE, "XDG foreign v1", "xdg-foreign-unstable-v1"}, {"zxdg_importer_v1", "zxdg_exporter_v1"});
    add({UPSTREAM, UNSTABLE, "XDG foreign v2", "xdg-foreign-unstable-v2"}, {"zxdg_importer_v2", "zxdg_exporter_v2"});
    add({UPSTREAM, UNSTABLE, "XDG output", "xdg-output-unstable-v1"}, {"zxdg_output_manager_v1"});
    add({UPSTREAM, UNSTABLE, "Xwayland keyboard grabbing", "xwayland-keyboard-grab-unstable-v1"}, {"zwp_xwayland_keyboard_grab_manager_v1"});

    add({WLROOTS, NONE, "Data control", "wlr-data-control-unstable-v1"}, {"zwlr_data_control_manager_v1"});
    add({WLROOTS, NONE, "Export DMA-BUF", "wlr-export-dmabuf-unstable-v1"}, {"zwlr_export_dmabuf_manager_v1"});
    add({WLROOTS, NONE, "Foreign toplevel management", "wlr-foreign-toplevel-management-unstable-v1"}, {"zwlr_foreign_toplevel_manager_v1"});
    add({WLROOTS, NONE, "Gamma control", "wlr-gamma-control-unstable-v1"}, {"zwlr_gamma_control_manager_v1"});
    add({WLROOTS, NONE, "Input inhibitor", "wlr-input-inhibitor-unstable-v1"}, {"zwlr_input_inhibit_manager_v1"});
    add({WLROOTS, NONE, "Layer shell", "wlr-layer-shell-unstable-v1"}, {"zwlr_layer_shell_v1"});
    add({WLROOTS, NONE, "Output management", "wlr-output-management-unstable-v1"}, {"zwlr_output_manager_v1"});
    add({WLROOTS, NONE, "Output power management", "wlr-output-power-management-unstable-v1"}, {"zwlr_output_power_manager_v1"});
    add({WLROOTS, NONE, "Screencopy", "wlr-screencopy-unstable-v1"}, {"zwlr_screencopy_manager_v1"});
    add({WLROOTS, NONE, "Virtual pointer", "wlr-virtual-pointer-unstable-v1"}, {"zwlr_virtual_pointer_manager_v1"});

    add({KDE, NONE, "AppMenu", "appmenu"}, {"org_kde_kwin_appmenu_manager"});
    add({KDE, NONE, "Blur", "blur"}, {"org_kde_kwin_blur_manager"});
    add({KDE, NONE, "Contrast", "contrast"}, {"org_kde_kwin_contrast_manager"});
    add({KDE, NONE, "DPMS", "dpms"}, {"org_kde_kwin_dpms_manager"});
    add({KDE, NONE, "External brightness", "kde-external-brightness-v1"}, {"kde_external_brightness_v1"});
    add({KDE, NONE, "Fake input", "fake-input"}, {"org_kde_kwin_fake_input"});
    add({KDE, NONE, "Idle", "idle"}, {"org_kde_kwin_idle"});
    add({KDE, NONE, "Key state", "keystate"}, {"org_kde_kwin_keystate"});
    add({KDE, NONE, "Lockscreen overlay", "kde-lockscreen-overlay-v1"}, {"kde_lockscreen_overlay_v1"});
    add({KDE, NONE, "Output management", "output-management"}, {"org_kde_kwin_outputmanagement"});
    add({KDE, NONE, "Output management v2", "kde-output-management-v2"}, {"kde_output_management_v2"});
    add({KDE, NONE, "Output device", "outputdevice"}, {"org_kde_kwin_outputdevice"});
    add({KDE, NONE, "Output device v2", "kde-output-device-v2"}, {"kde_output_device_v2"});
    add({KDE, NONE, "Output order", "kde-output-order-v1"}, {"kde_output_order_v1"});
    add({KDE, NONE, "Plasma shell", "plasma-shell"}, {"org_kde_plasma_shell"});
    add({KDE, NONE, "Plasma virtual desktop", "org-kde-plasma-virtual-desktop"}, {"org_kde_plasma_virtual_desktop_management"});
    add({KDE, NONE, "Plasma window management", "plasma-window-management"}, {"org_kde_plasma_window_management"});
    add({KDE, NONE, "Primary output", "kde-primary-output-v1"}, {"kde_primary_output_v1"});
    add({KDE, NONE, "Screencast", "zkde-screencast-unstable-v1"}, {"zkde_screencast_unstable_v1"});
    add({KDE, NONE, "Screen edge", "kde-screen-edge-v1"}, {"kde_screen_edge_manager_v1"});
    add({KDE, NONE, "Server decoration", "server-decoration"}, {"org_kde_kwin_server_decoration_manager"});
    add({KDE, NONE, "Server decoration palette", "server-decoration-palette"}, {"org_kde_kwin_server_decoration_palette_manager"});
    add({KDE, NONE, "Shadow", "shadow"}, {"org_kde_kwin_shadow_manager"});
    add({KDE, NONE, "Slide", "slide"}, {"org_kde_kwin_slide_manager"});

    add({COSMIC, NONE, "A11y", "cosmic-a11y-unstable-v1"}, {"cosmic_a11y_manager_v1"});
    add({COSMIC, NONE, "AT-SPI", "cosmic-atspi-unstable-v1"}, {"cosmic_atspi_manager_v1"});
    add({COSMIC, NONE, "Image source", "cosmic-image-source-unstable-v1"}, {"zcosmic_output_image_source_manager_v1", "zcosmic_workspace_image_source_manager_v1"});
    add({COSMIC, NONE, "Output management", "cosmic-output-management-unstable-v1"}, {"zcosmic_output_manager_v1"});
    add({COSMIC, NONE, "Overlap notify", "cosmic-overlap-notify-unstable-v1"}, {"zcosmic_overlap_notify_v1"});
    add({COSMIC, NONE, "Screencopy", "cosmic-screencopy-unstable-v1"}, {"zcosmic_screencopy_manager_v1"});
    add({COSMIC, NONE, "Screencopy v2", "cosmic-screencopy-unstable-v2"}, {"zcosmic_screencopy_manager_v2"});
    add({COSMIC, NONE, "Toplevel info", "cosmic-toplevel-info-unstable-v1"}, {"zcosmic_toplevel_info_v1"});
    add({COSMIC, NONE, "Toplevel management", "cosmic-toplevel-management-unstable-v1"}, {"zcosmic_toplevel_manager_v1"});
    add({COSMIC, NONE, "Workspace", "cosmic-workspace-unstable-v1"}, {"zcosmic_workspace_manager_v1"});

    add({WESTON, NONE, "Content protection", "weston-content-protection"}, {"weston_content_protection"});
    add({WESTON, NONE, "Desktop shell", "weston-desktop-shell"}, {"weston_desktop_shell"});
    add({WESTON, NONE, "Direct display", "weston-direct-display"}, {"weston_direct_display_v1"});
    add({WESTON, NONE, "In-vehicle infotainment application", "ivi-application"}, {"ivi_application"});
    add({WESTON, NONE, "In-vehicle infotainment HMI controller", "ivi-hmi-controller"}, {"ivi_hmi_controller"});
    add({WESTON, NONE, "Output capture", "weston-output-capture"}, {"weston_capture_v1"});
    add({WESTON, NONE, "Screenshooter", "weston-screenshooter"}, {"weston_screenshooter"});
    add({WESTON, NONE, "Text cursor position", "text-cursor-position"}, {"text_cursor_position"});
    add({WESTON, NONE, "Touch calibration", "weston-touch-calibration"}, {"weston_touch_calibration"});

    add({CHROMEOS, NONE, "Aura output management", "aura-output-management"}, {"zaura_output_manager_v2"});
    add({CHROMEOS, NONE, "Aura shell", "aura-shell"}, {"zaura_shell"});
    add({CHROMEOS, NONE, "Color management", "chrome-color-management"}, {"zcr_color_manager_v1"});
    add({CHROMEOS, NONE, "Overlay prioritizer", "overlay-prioritizer"}, {"overlay_prioritizer"});
    add({CHROMEOS, NONE, "Surface augmenter", "surface-augmenter"}, {"surface_augmenter"});

    add({DEEPIN, NONE, "Capture", "treeland-capture-unstable-v1"}, {"treeland_capture_session_v1"});
    add({DEEPIN, NONE, "DDE shell", "treeland-dde-shell-v1"}, {"treeland_dde_shell_manager_v1"});
    add({DEEPIN, NONE, "Foreign toplevel manager", "treeland-foreign-toplevel-manager-v1"}, {"treeland_foreign_toplevel_manager_v1"});
    add({DEEPIN, NONE, "Output manager", "treeland-output-manager-v1"}, {"treeland_output_manager_v1"});
    add({DEEPIN, NONE, "Personalization manager", "treeland-personalization-manager-v1"}, {"treeland_personalization_manager_v1"});
    add({DEEPIN, NONE, "Shortcut manager", "treeland-shortcut-manager-v1"}, {"treeland_shortcut_manager_v1"});
    add({DEEPIN, NONE, "Virtual output manager", "treeland-virtual-output-manager-v1"}, {"treeland_virtual_output_manager_v1"});
    add({DEEPIN, NONE, "Wallpaper color", "treeland-wallpaper-color-v1"}, {"treeland_wallpaper_color_manager_v1"});
    add({DEEPIN, NONE, "Window management", "treeland-window-management-v1"}, {"treeland_window_management_v1"});

    add({FROG, NONE, "Frog color management", "frog-color-management-v1"}, {"frog_color_management_factory_v1"});
    add({FROG, NONE, "Frog FIFO constraints", "frog-fifo-v1"}, {"frog_fifo_manager_v1"});

    add({GAMESCOPE, NONE, "Gamescope control", "gamescope-control"}, {"gamescope_control"});
    add({GAMESCOPE, NONE, "Gamescope input method", "gamescope-input-method"}, {"gamescope_input_method_manager"});
    add({GAMESCOPE, NONE, "Gamescope PipeWire", "gamescope-pipewire"}, {"gamescope_pipewire"});
    add({GAMESCOPE, NONE, "Gamescope reshade", "gamescope-reshade"}, {"gamescope_reshade"});
    add({GAMESCOPE, NONE, "Gamescope swapchain", "gamescope-swapchain"}, {"gamescope_swapchain_factory_v2"});

    add({PHOSH, NONE, "Phoc device state", "phoc-device-state-v1"}, {"zphoc_device_state_v1"});
    add({PHOSH, NONE, "Phoc layer shell effects", "phoc-layer-shell-effects-v1"}, {"zphoc_layer_shell_effects_v1"});
    add({PHOSH, NONE, "Phosh private", "phosh-private"}, {"phosh_private"});
    // clang-format on
}

void ProtocolStore::add(const Protocol& protocol, const std::set<std::string>& interfaces) noexcept {
    const auto name = QString::fromStdString(protocol.name);
    auto id = QStringLiteral("%1-%2-%3").arg(protocol.source).arg(protocol.status).arg(name).toStdString();
    if (known_protocols.contains(id)) {
        std::printf("Known protocols already contains id %s\n", id.c_str());
        return;
    }

    known_protocols.emplace(id, protocol);
    for (auto& interface : interfaces) {
        known_interfaces.emplace(interface, id);
    }
}

std::optional<Protocol> ProtocolStore::protocolForInterface(const std::string& interface) const noexcept {
    if (!known_interfaces.contains(interface)) {
        return {};
    }

    return known_protocols.at(known_interfaces.at(interface));
}

std::set<const Protocol*> ProtocolStore::getProtocols() const noexcept {
    std::set<const Protocol*> ret;
    for (auto& it : std::ranges::views::values(known_protocols)) {
        ret.emplace(&it);
    }
    return ret;
}

std::string source_to_string(const ProtocolSource source) {
    switch (source) {
        case UPSTREAM:
            return "Upstream";
        case WLROOTS:
            return "Wlroots";
        case KDE:
            return "KDE";
        case COSMIC:
            return "COSMIC";
        case WESTON:
            return "Weston";
        case CHROMEOS:
            return "ChromeOS";
        case DEEPIN:
            return "Deepin";
        case FROG:
            return "Frog";
        case GAMESCOPE:
            return "Gamescope";
        case PHOSH:
            return "Phosh";
        default:
            return "Unknown";
    }
}

std::string status_to_string(const ProtocolStatus status) {
    switch (status) {
        case STABLE:
            return "Stable";
        case STAGING:
            return "Staging";
        case UNSTABLE:
            return "Unstable";
        default:
            return "None";
    }
}
