//! Safe native clipboard adapter selection.
//!
//! Desktop adapters are supplied by `arboard`, which owns the operating-system
//! integration. IronStorage performs no direct FFI and contains no unsafe code.

use super::{ClipboardBackend, ClipboardContent, ClipboardError};
use crate::repository::SecretBytes;

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub struct NativeClipboardBackend {
    clipboard: arboard::Clipboard,
}

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
impl NativeClipboardBackend {
    pub fn new() -> Result<Self, ClipboardError> {
        arboard::Clipboard::new()
            .map(|clipboard| Self { clipboard })
            .map_err(|_| ClipboardError::Unavailable)
    }
}

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
impl ClipboardBackend for NativeClipboardBackend {
    fn read(&mut self) -> Result<ClipboardContent, ClipboardError> {
        match self.clipboard.get_text() {
            Ok(value) => Ok(ClipboardContent::text(value.into_bytes())),
            Err(arboard::Error::ContentNotAvailable) => Ok(ClipboardContent::EmptyOrNonText),
            Err(arboard::Error::ClipboardNotSupported) => Err(ClipboardError::Unavailable),
            Err(_) => Err(ClipboardError::ReadFailed),
        }
    }

    fn write(&mut self, value: &SecretBytes) -> Result<(), ClipboardError> {
        let text = std::str::from_utf8(value.expose()).map_err(|_| ClipboardError::NonUtf8)?;
        set_text(&mut self.clipboard, text).map_err(|error| match error {
            arboard::Error::ClipboardNotSupported => ClipboardError::Unavailable,
            _ => ClipboardError::WriteFailed,
        })
    }

    fn clear(&mut self) -> Result<(), ClipboardError> {
        self.clipboard.clear().map_err(|error| match error {
            arboard::Error::ClipboardNotSupported => ClipboardError::Unavailable,
            _ => ClipboardError::WriteFailed,
        })
    }
}

#[cfg(target_os = "linux")]
fn set_text(clipboard: &mut arboard::Clipboard, text: &str) -> Result<(), arboard::Error> {
    use arboard::SetExtLinux as _;

    clipboard.set().exclude_from_history().text(text)
}

#[cfg(any(target_os = "macos", target_os = "windows"))]
fn set_text(clipboard: &mut arboard::Clipboard, text: &str) -> Result<(), arboard::Error> {
    clipboard.set_text(text)
}

#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
pub struct NativeClipboardBackend;

#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
impl NativeClipboardBackend {
    pub fn new() -> Result<Self, ClipboardError> {
        Err(ClipboardError::Unavailable)
    }
}

#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
impl ClipboardBackend for NativeClipboardBackend {
    fn read(&mut self) -> Result<ClipboardContent, ClipboardError> {
        Err(ClipboardError::Unavailable)
    }

    fn write(&mut self, _value: &SecretBytes) -> Result<(), ClipboardError> {
        Err(ClipboardError::Unavailable)
    }

    fn clear(&mut self) -> Result<(), ClipboardError> {
        Err(ClipboardError::Unavailable)
    }
}
