use std::{
    sync::Arc,
    task::{Context, Poll},
};

use futures::{Future, FutureExt};
use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSelectScreenResult};

use crate::{
    backend::{
        common::OverlaySelector,
        overlay::{
            ui_transform, OverlayBackend, OverlayRenderer, OverlayState, SplitOverlayBackend,
        },
        task::TaskType,
    },
    state::{AppSession, AppState},
};

use super::screen::ScreenRenderer;
type PinnedSelectorFuture = core::pin::Pin<
    Box<dyn Future<Output = Result<PipewireSelectScreenResult, wlx_capture::pipewire::AshpdError>>>,
>;

pub struct MirrorRenderer {
    name: Arc<str>,
    renderer: Option<ScreenRenderer>,
    selector: Option<PinnedSelectorFuture>,
    last_extent: [u32; 3],
}
impl MirrorRenderer {
    pub fn new(name: Arc<str>) -> Self {
        let selector = Box::pin(pipewire_select_screen(None, false, false, false, false));
        Self {
            name,
            renderer: None,
            selector: Some(selector),
            last_extent: [0; 3],
        }
    }
}

impl OverlayRenderer for MirrorRenderer {
    fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
        Ok(())
    }
    fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
        if let Some(mut selector) = self.selector.take() {
            let maybe_pw_result = match selector
                .poll_unpin(&mut Context::from_waker(futures::task::noop_waker_ref()))
            {
                Poll::Ready(result) => result,
                Poll::Pending => {
                    self.selector = Some(selector);
                    return Ok(());
                }
            };

            match maybe_pw_result {
                Ok(pw_result) => {
                    let node_id = pw_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element
                    log::info!("{}: PipeWire node selected: {}", self.name.clone(), node_id);
                    let capture = PipewireCapture::new(self.name.clone(), node_id);
                    self.renderer = Some(ScreenRenderer::new_raw(
                        self.name.clone(),
                        Box::new(capture),
                    ));
                    app.tasks.enqueue(TaskType::Overlay(
                        OverlaySelector::Name(self.name.clone()),
                        Box::new(|app, o| {
                            o.grabbable = true;
                            o.interactable = true;
                            o.reset(app, false);
                        }),
                    ));
                }
                Err(e) => {
                    log::warn!("Failed to create mirror due to PipeWire error: {:?}", e);
                    self.renderer = None;
                    // drop self
                    app.tasks
                        .enqueue(TaskType::DropOverlay(OverlaySelector::Name(
                            self.name.clone(),
                        )));
                }
            }
        }

        if let Some(renderer) = self.renderer.as_mut() {
            renderer.render(app)?;
            if let Some(view) = renderer.view() {
                let extent = view.image().extent();
                if self.last_extent != extent {
                    self.last_extent = extent;
                    // resized
                    app.tasks.enqueue(TaskType::Overlay(
                        OverlaySelector::Name(self.name.clone()),
                        Box::new(move |_app, o| {
                            o.interaction_transform = ui_transform(&[extent[0], extent[1]]);
                        }),
                    ));
                }
            }
        }

        Ok(())
    }
    fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> {
        if let Some(renderer) = self.renderer.as_mut() {
            renderer.pause(app)?;
        }
        Ok(())
    }
    fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> {
        if let Some(renderer) = self.renderer.as_mut() {
            renderer.resume(app)?;
        }
        Ok(())
    }
    fn view(&mut self) -> Option<std::sync::Arc<vulkano::image::view::ImageView>> {
        self.renderer.as_mut().and_then(|r| r.view())
    }

    fn extent(&mut self) -> Option<[u32; 3]> {
        Some(self.last_extent)
    }
}

pub fn new_mirror(
    name: Arc<str>,
    show_hide: bool,
    session: &AppSession,
) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
    let state = OverlayState {
        name: name.clone(),
        show_hide,
        want_visible: true,
        spawn_scale: 0.5 * session.config.desktop_view_scale,
        ..Default::default()
    };
    let backend = Box::new(SplitOverlayBackend {
        renderer: Box::new(MirrorRenderer::new(name)),
        ..Default::default()
    });

    Some((state, backend))
}
