use crate::PixelFormat;

type ImageData = Vec<u8>;

/// A rect where the image should be updated
#[derive(Debug, Clone, Copy)]
pub struct Rect {
    pub x: u16,
    pub y: u16,
    pub width: u16,
    pub height: u16,
}

/// Resolution format to resize window
#[derive(Debug, Clone)]
pub struct Screen {
    pub width: u16,
    pub height: u16,
}

impl From<(u16, u16)> for Screen {
    fn from(tuple: (u16, u16)) -> Self {
        Self {
            width: tuple.0,
            height: tuple.1,
        }
    }
}

type SrcRect = Rect;
type DstRect = Rect;

/// Events generated by the [crate::VncClient]
///
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum VncEvent {
    /// Tell the client how to display the images
    ///
    /// ```no_compile
    /// if let VncEvent::SetResolution(resolution) = event {
    ///     window.resize(screen.width, screen.height);
    /// }
    /// ```
    ///
    /// Note that this event may be recived multiple times
    ///
    /// If the [crate::VncEncoding::DesktopSizePseudo] is set
    ///
    SetResolution(Screen),
    /// If the connector doesn't call `set_pixel_format` method
    ///
    /// The engine will generate a [VncEvent::SetPixelFormat] to let the window know how to render image
    ///
    SetPixelFormat(PixelFormat),
    /// Raw image data in the order followed by informed PixelFormat
    ///
    RawImage(Rect, ImageData),
    /// Copy image data from the second rect to the first
    ///
    Copy(DstRect, SrcRect),
    /// A jpeg image if using Tight encoding,
    ///
    /// Encoding the bytes with base64 and render it with "<img src=data:image/jpeg;base64,.../>",
    ///
    JpegImage(Rect, ImageData),

    // PngImage(Rect, ImageData),
    /// Will be generated if [crate::VncEncoding::CursorPseudo] is set
    ///
    /// According to [RFC6143, section-7.8.1](https://www.rfc-editor.org/rfc/rfc6143.html#section-7.8.1)
    ///
    SetCursor(Rect, ImageData),
    /// Just ring a bell
    ///
    Bell,
    /// Will be generated everytime the vncserver's clipboarded get updated
    ///
    /// Note that only Latin-1 character set is allowed
    ///
    /// According to [RFC6143](https://www.rfc-editor.org/rfc/rfc6143.html#section-7.6.4)
    ///
    Text(String),
    /// If any unexpected error happens in the async process routines
    /// This event will propagate the error to the current context
    Error(String),
}

/// X11 keyboard event to notify the server
///
/// Referring to [RFC6143, section-7.5.4](https://www.rfc-editor.org/rfc/rfc6143.html#section-7.5.4)
///
#[derive(Debug, Clone)]
pub struct ClientKeyEvent {
    pub keycode: u32,
    pub down: bool,
}

impl From<(u32, bool)> for ClientKeyEvent {
    fn from(tuple: (u32, bool)) -> Self {
        Self {
            keycode: tuple.0,
            down: tuple.1,
        }
    }
}

/// X11 mouse event to notify the server
///
/// Referring to [RFC6143, seciont-7.5.5](https://www.rfc-editor.org/rfc/rfc6143.html#section-7.5.5)
///
#[derive(Debug, Clone)]
pub struct ClientMouseEvent {
    pub position_x: u16,
    pub position_y: u16,
    pub bottons: u8,
}

impl From<(u16, u16, u8)> for ClientMouseEvent {
    fn from(tuple: (u16, u16, u8)) -> Self {
        Self {
            position_x: tuple.0,
            position_y: tuple.1,
            bottons: tuple.2,
        }
    }
}

/// Client-side event which used to ask the engine send some command to the vnc server
///
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum X11Event {
    /// Require an incremental frame update
    ///
    Refresh,
    /// Require a full (non-incremental) frame update.
    /// Forces the server to send the entire framebuffer, useful after
    /// CursorPseudo is negotiated to clear cursor ghosts from the framebuffer.
    FullRefresh,
    /// Key down/up
    ///
    KeyEvent(ClientKeyEvent),
    /// Mouse move/up/down/scroll
    ///
    PointerEvent(ClientMouseEvent),
    /// Send data to the server's clipboard
    ///
    /// Only Latin-1 character set is allowed
    ///
    CopyText(String),
}
