use epaint::ColorImage;

use crate::{
    Key,
    emath::{Pos2, Vec2},
};

use super::{
    ImeEvent, Modifiers, MouseWheelUnit, PointerButton, TouchDeviceId, TouchId, TouchPhase,
};

/// An input event generated by the integration.
///
/// This only covers events that egui cares about.
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum Event {
    /// The integration detected a "copy" event (e.g. Cmd+C).
    Copy,

    /// The integration detected a "cut" event (e.g. Cmd+X).
    Cut,

    /// The integration detected a "paste" event (e.g. Cmd+V).
    Paste(String),

    /// Text input, e.g. via keyboard.
    ///
    /// When the user presses enter/return, do not send a [`Text`](Event::Text) (just [`Key::Enter`]).
    Text(String),

    /// A key was pressed or released.
    ///
    /// ## Note for integration authors
    ///
    /// Key events that has been processed by IMEs should not be sent to `egui`.
    Key {
        /// Most of the time, it's the logical key, heeding the active keymap -- for instance, if the user has Dvorak
        /// keyboard layout, it will be taken into account.
        ///
        /// If it's impossible to determine the logical key on desktop platforms (say, in case of non-Latin letters),
        /// `key` falls back to the value of the corresponding physical key. This is necessary for proper work of
        /// standard shortcuts that only respond to Latin-based bindings (such as `Ctrl` + `V`).
        key: Key,

        /// The physical key, corresponding to the actual position on the keyboard.
        ///
        /// This ignores keymaps, so it is not recommended to use this.
        /// The only thing it makes sense for is things like games,
        /// where e.g. the physical location of WSAD on QWERTY should always map to movement,
        /// even if the user is using Dvorak or AZERTY.
        ///
        /// `eframe` does not (yet) implement this on web.
        physical_key: Option<Key>,

        /// Was it pressed or released?
        pressed: bool,

        /// If this is a `pressed` event, is it a key-repeat?
        ///
        /// On many platforms, holding down a key produces many repeated "pressed" events for it, so called key-repeats.
        /// Sometimes you will want to ignore such events, and this lets you do that.
        ///
        /// egui will automatically detect such repeat events and mark them as such here.
        /// Therefore, if you are writing an egui integration, you do not need to set this (just set it to `false`).
        repeat: bool,

        /// The state of the modifier keys at the time of the event.
        modifiers: Modifiers,
    },

    /// The set of held modifier keys changed.
    ModifiersChanged(Modifiers),

    /// The mouse or touch moved to a new place.
    PointerMoved(Pos2),

    /// The mouse moved, the units are unspecified.
    /// Represents the actual movement of the mouse, without acceleration or clamped by screen edges.
    /// `PointerMoved` and `MouseMoved` can be sent at the same time.
    /// This event is optional. If the integration can not determine unfiltered motion it should not send this event.
    MouseMoved(Vec2),

    /// A mouse button was pressed or released (or a touch started or stopped).
    PointerButton {
        /// Where is the pointer?
        pos: Pos2,

        /// What mouse button? For touches, use [`PointerButton::Primary`].
        button: PointerButton,

        /// Was it the button/touch pressed this frame, or released?
        pressed: bool,

        /// The state of the modifier keys at the time of the event.
        modifiers: Modifiers,
    },

    /// The mouse left the screen, or the last/primary touch input disappeared.
    ///
    /// This means there is no longer a cursor on the screen for hovering etc.
    ///
    /// On touch-up first send `PointerButton{pressed: false, …}` followed by `PointerLeft`.
    PointerGone,

    /// Zoom scale factor this frame (e.g. from a pinch gesture).
    ///
    /// * `zoom = 1`: no change.
    /// * `zoom < 1`: pinch together
    /// * `zoom > 1`: pinch spread
    ///
    /// Note that egui also implement zooming by holding `Ctrl` and scrolling the mouse wheel,
    /// so integration need NOT emit this `Zoom` event in those cases, just [`Self::MouseWheel`].
    ///
    /// As a user, check [`crate::InputState::smooth_scroll_delta`] to see if the user did any zooming this frame.
    Zoom(f32),

    /// Rotation in radians this frame, measuring clockwise (e.g. from a rotation gesture).
    Rotate(f32),

    /// IME Event
    Ime(ImeEvent),

    /// On touch screens, report this *in addition to*
    /// [`Self::PointerMoved`], [`Self::PointerButton`], [`Self::PointerGone`]
    Touch {
        /// Hashed device identifier (if available; may be zero).
        /// Can be used to separate touches from different devices.
        device_id: TouchDeviceId,

        /// Unique identifier of a finger/pen. Value is stable from touch down
        /// to lift-up
        id: TouchId,

        /// One of: start move end cancel.
        phase: TouchPhase,

        /// Position of the touch (or where the touch was last detected)
        pos: Pos2,

        /// Describes how hard the touch device was pressed. May always be `None` if the platform does
        /// not support pressure sensitivity.
        /// The value is in the range from 0.0 (no pressure) to 1.0 (maximum pressure).
        force: Option<f32>,
    },

    /// A raw mouse wheel event as sent by the backend.
    ///
    /// Used for scrolling.
    MouseWheel {
        /// The unit of `delta`: points, lines, or pages.
        unit: MouseWheelUnit,

        /// The direction of the vector indicates how to move the _content_ that is being viewed.
        /// So if you get positive values, the content being viewed should move to the right and down,
        /// revealing new things to the left and up.
        ///
        /// A positive X-value indicates the content is being moved right,
        /// as when swiping right on a touch-screen or track-pad with natural scrolling.
        ///
        /// A positive Y-value indicates the content is being moved down,
        /// as when swiping down on a touch-screen or track-pad with natural scrolling.
        delta: Vec2,

        /// The phase of the scroll, useful for trackpads.
        ///
        /// If unknown set this to [`TouchPhase::Move`].
        phase: TouchPhase,

        /// The state of the modifier keys at the time of the event.
        modifiers: Modifiers,
    },

    /// The native window gained or lost focused (e.g. the user clicked alt-tab).
    WindowFocused(bool),

    /// An assistive technology (e.g. screen reader) requested an action.
    AccessKitActionRequest(accesskit::ActionRequest),

    /// The reply of a screenshot requested with [`crate::ViewportCommand::Screenshot`].
    Screenshot {
        viewport_id: crate::ViewportId,

        /// Whatever was passed to [`crate::ViewportCommand::Screenshot`].
        user_data: crate::UserData,

        image: std::sync::Arc<ColorImage>,
    },
}
