use std::sync::{Arc, Mutex};
use wayland_client::protocol::wl_output::{self, Event, RequestsTrait, WlOutput};
use wayland_client::protocol::wl_registry::{self, RequestsTrait as RegistryRequest};
use wayland_client::Proxy;
pub use wayland_client::protocol::wl_output::{Subpixel, Transform};
#[derive(Copy, Clone, Debug)]
pub struct Mode {
    
    
    
    pub dimensions: (i32, i32),
    
    pub refresh_rate: i32,
    
    pub is_current: bool,
    
    pub is_preferred: bool,
}
#[derive(Clone, Debug)]
pub struct OutputInfo {
    
    pub model: String,
    
    pub make: String,
    
    
    
    
    
    pub location: (i32, i32),
    
    pub physical_size: (i32, i32),
    
    pub subpixel: Subpixel,
    
    
    
    
    
    pub transform: Transform,
    
    
    
    
    
    
    
    pub scale_factor: i32,
    
    pub modes: Vec<Mode>,
}
impl OutputInfo {
    fn new() -> OutputInfo {
        OutputInfo {
            model: String::new(),
            make: String::new(),
            location: (0, 0),
            physical_size: (0, 0),
            subpixel: Subpixel::Unknown,
            transform: Transform::Normal,
            scale_factor: 1,
            modes: Vec::new(),
        }
    }
}
struct Inner {
    outputs: Vec<(u32, Proxy<WlOutput>, OutputInfo)>,
    pending: Vec<(Proxy<WlOutput>, Event)>,
}
impl Inner {
    fn merge(&mut self, output: &Proxy<WlOutput>) {
        let info = match self
            .outputs
            .iter_mut()
            .find(|&&mut (_, ref o, _)| o.equals(output))
        {
            Some(&mut (_, _, ref mut info)) => info,
            
            
            
            None => {
                
                self.pending.retain(|&(ref o, _)| o.is_alive());
                return;
            }
        };
        
        
        
        while let Some(idx) = self.pending.iter().position(|&(ref o, _)| o.equals(output)) {
            let (_, event) = self.pending.swap_remove(idx);
            match event {
                Event::Geometry {
                    x,
                    y,
                    physical_width,
                    physical_height,
                    subpixel,
                    model,
                    make,
                    transform,
                } => {
                    info.location = (x, y);
                    info.physical_size = (physical_width, physical_height);
                    info.subpixel = subpixel;
                    info.transform = transform;
                    info.model = model;
                    info.make = make;
                }
                Event::Scale { factor } => {
                    info.scale_factor = factor;
                }
                Event::Done => {
                    
                    unreachable!();
                }
                Event::Mode {
                    width,
                    height,
                    refresh,
                    flags,
                } => {
                    let mut found = false;
                    if let Some(mode) = info
                        .modes
                        .iter_mut()
                        .find(|m| m.dimensions == (width, height) && m.refresh_rate == refresh)
                    {
                        
                        mode.is_preferred = flags.contains(wl_output::Mode::Preferred);
                        mode.is_current = flags.contains(wl_output::Mode::Current);
                        found = true;
                    }
                    if !found {
                        
                        info.modes.push(Mode {
                            dimensions: (width, height),
                            refresh_rate: refresh,
                            is_preferred: flags.contains(wl_output::Mode::Preferred),
                            is_current: flags.contains(wl_output::Mode::Current),
                        })
                    }
                }
            }
        }
    }
}
#[derive(Clone)]
pub struct OutputMgr {
    inner: Arc<Mutex<Inner>>,
}
impl OutputMgr {
    pub(crate) fn new() -> OutputMgr {
        OutputMgr {
            inner: Arc::new(Mutex::new(Inner {
                outputs: Vec::new(),
                pending: Vec::new(),
            })),
        }
    }
    pub(crate) fn new_output(
        &self,
        id: u32,
        version: u32,
        registry: &Proxy<wl_registry::WlRegistry>,
    ) {
        let inner = self.inner.clone();
        let output = registry
            .bind(version, id, |output| {
                output.implement(
                    move |event, output| {
                        let mut inner = inner.lock().unwrap();
                        if let Event::Done = event {
                            inner.merge(&output);
                        } else {
                            inner.pending.push((output.clone(), event));
                            if output.version() < 2 {
                                
                                
                                inner.merge(&output);
                            }
                        }
                    },
                    (),
                )
            })
            .unwrap();
        self.inner
            .lock()
            .unwrap()
            .outputs
            .push((id, output, OutputInfo::new()));
    }
    pub(crate) fn output_removed(&self, id: u32) {
        let mut inner = self.inner.lock().unwrap();
        if let Some(idx) = inner.outputs.iter().position(|&(i, _, _)| i == id) {
            let (_, output, _) = inner.outputs.swap_remove(idx);
            
            inner.pending.retain(|&(ref o, _)| !o.equals(&output));
            if output.version() >= 3 {
                output.release();
            }
        }
    }
    
    
    
    
    pub fn find_id<F, T>(&self, id: u32, f: F) -> Option<T>
    where
        F: FnOnce(&Proxy<wl_output::WlOutput>, &OutputInfo) -> T,
    {
        let inner = self.inner.lock().unwrap();
        if let Some(&(_, ref proxy, ref info)) = inner.outputs.iter().find(|&&(i, _, _)| i == id) {
            Some(f(proxy, info))
        } else {
            None
        }
    }
    
    
    
    
    pub fn with_info<F, T>(&self, output: &Proxy<WlOutput>, f: F) -> Option<T>
    where
        F: FnOnce(u32, &OutputInfo) -> T,
    {
        let inner = self.inner.lock().unwrap();
        if let Some(&(id, _, ref info)) = inner
            .outputs
            .iter()
            .find(|&&(_, ref o, _)| o.equals(output))
        {
            Some(f(id, info))
        } else {
            None
        }
    }
    
    pub fn with_all<F, T>(&self, f: F) -> T
    where
        F: FnOnce(&[(u32, Proxy<WlOutput>, OutputInfo)]) -> T,
    {
        let inner = self.inner.lock().unwrap();
        f(&inner.outputs)
    }
}