//! Wrappers for iced tasks to allow testing.

use super::Task;

use iced::widget::{self, scrollable};
use iced::{advanced, window};
use iced_runtime::Action;

use std::convert::Infallible;

/// Write the given contents to the clipboard.
pub fn clipboard_write<T>(contents: String) -> Task<T> {
    #[cfg(not(any(test, feature = "testing")))]
    let task = iced::clipboard::write(contents);

    #[cfg(any(test, feature = "testing"))]
    let task = {
        let _ = contents;
        Task::none()
    };

    task
}

/// Creates a new [`Task`] that executes the given [`Action`] and produces no
/// output.
pub fn effect<T>(action: impl Into<Action<Infallible>>) -> Task<T> {
    #[cfg(not(any(test, feature = "testing")))]
    let task = iced_runtime::task::effect(action);

    #[cfg(any(test, feature = "testing"))]
    let task = {
        let _ = action;
        Task::none()
    };

    task
}

/// Produces a [`Task`] that scrolls the [`scrollable::Scrollable`] with the
/// given [`widget::Id`] by the provided [`scrollable::AbsoluteOffset`].
pub fn scroll_by<T>(
    id: impl Into<widget::Id>,
    offset: scrollable::AbsoluteOffset,
) -> Task<T> {
    #[cfg(not(any(test, feature = "testing")))]
    let task = widget::operation::scroll_by(id, offset);

    #[cfg(any(test, feature = "testing"))]
    let task = {
        let _ = (id, offset);
        Task::none()
    };

    task
}

/// Produces a [`Task`] that scrolls the [`scrollable::Scrollable`] with the
/// given [`widget::Id`] to the provided [`scrollable::AbsoluteOffset`].
pub fn scroll_to<T>(
    id: impl Into<widget::Id>,
    offset: scrollable::AbsoluteOffset,
) -> Task<T> {
    #[cfg(not(any(test, feature = "testing")))]
    let task = widget::operation::scroll_to(id, offset);

    #[cfg(any(test, feature = "testing"))]
    let task = {
        let _ = (id, offset);
        Task::none()
    };

    task
}

/// Focuses the next focusable widget.
pub fn widget_focus_next<T>() -> Task<T> {
    #[cfg(not(any(test, feature = "testing")))]
    let task = widget::operation::focus_next();

    #[cfg(any(test, feature = "testing"))]
    let task = Task::none();

    task
}

/// Focuses the previous focusable widget.
pub fn widget_focus_previous<T>() -> Task<T> {
    #[cfg(not(any(test, feature = "testing")))]
    let task = widget::operation::focus_previous();

    #[cfg(any(test, feature = "testing"))]
    let task = Task::none();

    task
}

/// Creates a new [`Task`] that runs the given [`advanced::widget::Operation`]
/// and produces its output.
pub fn widget_operate<T>(
    operation: impl advanced::widget::Operation<T> + 'static,
) -> Task<T>
where
    T: Send + 'static,
{
    #[cfg(not(any(test, feature = "testing")))]
    let task = advanced::widget::operate(operation);

    #[cfg(any(test, feature = "testing"))]
    let task = {
        let _ = operation;
        Task::none()
    };

    task
}

/// Opens a new window with the given [`window::Settings`]; producing the
/// [`window::Id`] of the new window on completion.
pub fn window_open(
    settings: window::Settings,
) -> (window::Id, Task<window::Id>) {
    #[cfg(not(any(test, feature = "testing")))]
    let (window_id, task) = window::open(settings);

    #[cfg(any(test, feature = "testing"))]
    let (window_id, task) = {
        let _ = settings;
        let id = window::Id::unique();
        (id, Task::done(id))
    };

    (window_id, task)
}

/// Changes the icon of the window using an icon from the content of an image
/// file at the given path.
pub fn window_set_icon<T>(window_id: window::Id, icon_bytes: Vec<u8>) -> Task<T>
where
    T: Send + 'static,
{
    #[cfg(not(any(test, feature = "testing")))]
    let set_icon_task = Task::future(async move {
        use image::ImageFormat;

        window::icon::from_file_data(&icon_bytes, Some(ImageFormat::Png))
            .unwrap_or_else(|err| {
                panic!("unable to decode PNG icon bytes: {err:?}",)
            })
    })
    .then(move |icon| window::set_icon(window_id, icon));

    #[cfg(any(test, feature = "testing"))]
    let set_icon_task = {
        let _ = (window_id, icon_bytes);
        Task::none()
    };

    set_icon_task
}