use std::sync::{Arc, Mutex};
use wayland_client::protocol::{
    wl_compositor, wl_output, wl_seat, wl_shm, wl_subcompositor, wl_surface,
};
use wayland_client::Proxy;
use wayland_protocols::xdg_shell::client::xdg_toplevel::ResizeEdge;
pub use wayland_protocols::xdg_shell::client::xdg_toplevel::State;
use self::zxdg_decoration_manager_v1::RequestsTrait as DecorationMgrRequests;
use self::zxdg_toplevel_decoration_v1::RequestsTrait as DecorationRequests;
use wayland_protocols::unstable::xdg_decoration::v1::client::{
    zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
};
use {Environment, Shell};
mod basic_frame;
mod concept_frame;
use shell;
pub use self::basic_frame::BasicFrame;
pub use self::concept_frame::ConceptFrame;
const MIN_WINDOW_SIZE: (u32, u32) = (2, 1);
pub enum ButtonState {
    
    Hovered,
    
    Idle,
    
    Disabled,
}
pub trait Theme: Send + 'static {
    
    fn get_primary_color(&self, active: bool) -> [u8; 4];
    
    fn get_secondary_color(&self, active: bool) -> [u8; 4];
    
    fn get_close_button_color(&self, status: ButtonState) -> [u8; 4];
    
    fn get_maximize_button_color(&self, status: ButtonState) -> [u8; 4];
    
    fn get_minimize_button_color(&self, status: ButtonState) -> [u8; 4];
}
#[derive(Clone, Debug)]
pub enum Event {
    
    Configure {
        
        
        
        
        
        
        
        
        
        
        new_size: Option<(u32, u32)>,
        
        
        
        
        states: Vec<State>,
    },
    
    
    
    
    Close,
    
    Refresh,
}
struct WindowInner<F> {
    frame: Arc<Mutex<F>>,
    shell_surface: Arc<Box<shell::ShellSurface>>,
    user_impl: Box<FnMut(Event) + Send>,
    min_size: (u32, u32),
    max_size: Option<(u32, u32)>,
    current_size: (u32, u32),
    old_size: Option<(u32, u32)>,
    decorated: bool,
}
pub struct Window<F: Frame> {
    frame: Arc<Mutex<F>>,
    surface: Proxy<wl_surface::WlSurface>,
    decoration: Mutex<Option<Proxy<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>>>,
    decoration_mgr: Option<Proxy<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>>,
    shell_surface: Arc<Box<shell::ShellSurface>>,
    inner: Arc<Mutex<Option<WindowInner<F>>>>,
}
impl<F: Frame + 'static> Window<F> {
    
    
    pub fn init_from_env<Impl>(
        env: &Environment,
        surface: Proxy<wl_surface::WlSurface>,
        initial_dims: (u32, u32),
        implementation: Impl,
    ) -> Result<Window<F>, F::Error>
    where
        Impl: FnMut(Event) + Send + 'static,
    {
        Self::init_with_decorations(
            surface,
            initial_dims,
            &env.compositor,
            &env.subcompositor,
            &env.shm,
            &env.shell,
            env.decorations_mgr.as_ref(),
            implementation,
        )
    }
    
    
    
    
    pub fn init<Impl>(
        surface: Proxy<wl_surface::WlSurface>,
        initial_dims: (u32, u32),
        compositor: &Proxy<wl_compositor::WlCompositor>,
        subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>,
        shm: &Proxy<wl_shm::WlShm>,
        shell: &Shell,
        implementation: Impl,
    ) -> Result<Window<F>, F::Error>
    where
        Impl: FnMut(Event) + Send + 'static,
    {
        Self::init_with_decorations(
            surface,
            initial_dims,
            compositor,
            subcompositor,
            shm,
            shell,
            None,
            implementation,
        )
    }
    
    
    
    
    
    pub fn init_with_decorations<Impl>(
        surface: Proxy<wl_surface::WlSurface>,
        initial_dims: (u32, u32),
        compositor: &Proxy<wl_compositor::WlCompositor>,
        subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>,
        shm: &Proxy<wl_shm::WlShm>,
        shell: &Shell,
        decoration_mgr: Option<&Proxy<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>>,
        implementation: Impl,
    ) -> Result<Window<F>, F::Error>
    where
        Impl: FnMut(Event) + Send + 'static,
    {
        let inner = Arc::new(Mutex::new(None::<WindowInner<F>>));
        let frame_inner = inner.clone();
        let shell_inner = inner.clone();
        let mut frame = F::init(
            &surface,
            compositor,
            subcompositor,
            shm,
            Box::new(move |req, serial| {
                if let Some(ref mut inner) = *shell_inner.lock().unwrap() {
                    match req {
                        FrameRequest::Minimize => inner.shell_surface.set_minimized(),
                        FrameRequest::Maximize => inner.shell_surface.set_maximized(),
                        FrameRequest::UnMaximize => inner.shell_surface.unset_maximized(),
                        FrameRequest::Move(seat) => inner.shell_surface.move_(&seat, serial),
                        FrameRequest::Resize(seat, edges) => {
                            inner.shell_surface.resize(&seat, serial, edges)
                        }
                        FrameRequest::Close => (inner.user_impl)(Event::Close),
                        FrameRequest::Refresh => (inner.user_impl)(Event::Refresh),
                    }
                }
            }) as Box<_>,
        )?;
        frame.resize(initial_dims);
        let frame = Arc::new(Mutex::new(frame));
        let shell_surface = Arc::new(shell::create_shell_surface(shell, &surface, move |event| {
            if let Some(ref mut inner) = *frame_inner.lock().unwrap() {
                match event {
                    shell::Event::Configure {
                        states,
                        mut new_size,
                    } => {
                        let mut frame = inner.frame.lock().unwrap();
                        
                        new_size = new_size.map(|(w, h)| {
                            use std::cmp::{max, min};
                            let (mut w, mut h) = frame.subtract_borders(w as i32, h as i32);
                            let (minw, minh) = inner.min_size;
                            w = max(w, minw as i32);
                            h = max(h, minh as i32);
                            if let Some((maxw, maxh)) = inner.max_size {
                                w = min(w, maxw as i32);
                                h = min(h, maxh as i32);
                            }
                            (max(w, 1) as u32, max(h, 1) as u32)
                        });
                        
                        let mut need_refresh = false;
                        need_refresh |= frame.set_maximized(states.contains(&State::Maximized));
                        if need_refresh {
                            
                            if states.contains(&State::Maximized) {
                                
                                inner.old_size = Some(inner.current_size);
                            } else if new_size.is_none() {
                                
                                new_size = inner.old_size.take();
                            }
                        }
                        need_refresh |= frame.set_active(states.contains(&State::Activated));
                        if need_refresh {
                            (inner.user_impl)(Event::Refresh);
                        }
                        (inner.user_impl)(Event::Configure { states, new_size });
                    }
                    shell::Event::Close => {
                        (inner.user_impl)(Event::Close);
                    }
                }
            }
        }));
        
        {
            let frame = frame.lock().unwrap();
            let (minw, minh) =
                frame.add_borders(MIN_WINDOW_SIZE.0 as i32, MIN_WINDOW_SIZE.1 as i32);
            shell_surface.set_min_size(Some((minw, minh)));
            let (w, h) = frame.add_borders(initial_dims.0 as i32, initial_dims.1 as i32);
            let (x, y) = frame.location();
            shell_surface.set_geometry(x, y, w, h);
        }
        *(inner.lock().unwrap()) = Some(WindowInner {
            frame: frame.clone(),
            shell_surface: shell_surface.clone(),
            user_impl: Box::new(implementation) as Box<_>,
            min_size: (MIN_WINDOW_SIZE.0, MIN_WINDOW_SIZE.1),
            max_size: None,
            current_size: initial_dims,
            old_size: None,
            decorated: true,
        });
        let window = Window {
            frame,
            shell_surface,
            decoration: Mutex::new(None),
            decoration_mgr: decoration_mgr.cloned(),
            surface,
            inner,
        };
        
        {
            let mut decoration = window.decoration.lock().unwrap();
            window.ensure_decoration(&mut decoration);
        }
        Ok(window)
    }
    fn ensure_decoration(
        &self,
        decoration: &mut Option<Proxy<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>>,
    ) {
        if self.decoration_mgr.is_none() {
            return;
        }
        if let Some(ref decoration) = *decoration {
            if decoration.is_alive() {
                return;
            }
        }
        let decoration_frame = self.frame.clone();
        let decoration_inner = self.inner.clone();
        *decoration = match (self.shell_surface.get_xdg(), &self.decoration_mgr) {
            (Some(toplevel), &Some(ref mgr)) => {
                use self::zxdg_toplevel_decoration_v1::{Event, Mode};
                mgr.get_toplevel_decoration(toplevel, |newdec| {
                    newdec.implement(
                        move |event, _| {
                            let Event::Configure { mode } = event;
                            match mode {
                                Mode::ServerSide => {
                                    decoration_frame.lock().unwrap().set_hidden(true);
                                }
                                Mode::ClientSide => {
                                    let want_decorate = decoration_inner
                                        .lock()
                                        .unwrap()
                                        .as_ref()
                                        .map(|inner| inner.decorated)
                                        .unwrap_or(false);
                                    decoration_frame.lock().unwrap().set_hidden(!want_decorate);
                                }
                            }
                        },
                        (),
                    )
                })
                .ok()
                .map(|decoration| {
                    decoration.set_mode(Mode::ServerSide);
                    decoration
                })
            }
            _ => None,
        };
    }
    
    
    
    
    pub fn new_seat(&mut self, seat: &Proxy<wl_seat::WlSeat>) {
        self.frame.lock().unwrap().new_seat(seat);
    }
    
    pub fn surface(&self) -> &Proxy<wl_surface::WlSurface> {
        &self.surface
    }
    
    
    
    
    
    
    
    
    
    
    
    pub fn refresh(&mut self) {
        self.frame.lock().unwrap().redraw();
    }
    
    
    
    
    
    
    
    pub fn set_title(&self, title: String) {
        self.frame.lock().unwrap().set_title(title.clone());
        self.shell_surface.set_title(title);
    }
    
    
    
    
    
    
    
    pub fn set_app_id(&self, app_id: String) {
        self.shell_surface.set_app_id(app_id);
    }
    
    
    
    
    pub fn set_decorate(&self, decorate: bool) {
        self.frame.lock().unwrap().set_hidden(!decorate);
        let mut decoration_guard = self.decoration.lock().unwrap();
        self.ensure_decoration(&mut decoration_guard);
        if let Some(ref dec) = *decoration_guard {
            if decorate {
                
                dec.unset_mode();
            } else {
                
                
                dec.destroy();
            }
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn set_resizable(&self, resizable: bool) {
        let mut frame = self.frame.lock().unwrap();
        frame.set_resizable(resizable);
        let mut inner = self.inner.lock().unwrap();
        if let Some(ref mut inner) = *inner {
            if resizable {
                
                self.shell_surface.set_min_size(
                    Some(inner.min_size).map(|(w, h)| frame.add_borders(w as i32, h as i32)),
                );
                self.shell_surface.set_max_size(
                    inner
                        .max_size
                        .map(|(w, h)| frame.add_borders(w as i32, h as i32)),
                );
            } else {
                
                let (w, h) = inner.current_size;
                self.shell_surface
                    .set_min_size(Some(frame.add_borders(w as i32, h as i32)));
                self.shell_surface
                    .set_max_size(Some(frame.add_borders(w as i32, h as i32)));
            }
        }
    }
    
    
    
    
    
    
    
    pub fn resize(&mut self, w: u32, h: u32) {
        use std::cmp::max;
        let w = max(w, 1);
        let h = max(h, 1);
        if let Some(ref mut inner) = *self.inner.lock().unwrap() {
            inner.current_size = (w, h);
        }
        let mut frame = self.frame.lock().unwrap();
        frame.resize((w, h));
        let (w, h) = frame.add_borders(w as i32, h as i32);
        let (x, y) = frame.location();
        self.shell_surface.set_geometry(x, y, w, h);
    }
    
    pub fn set_maximized(&self) {
        self.shell_surface.set_maximized();
    }
    
    pub fn unset_maximized(&self) {
        self.shell_surface.unset_maximized();
    }
    
    pub fn set_minimized(&self) {
        self.shell_surface.set_minimized();
    }
    
    
    
    
    pub fn set_fullscreen(&self, output: Option<&Proxy<wl_output::WlOutput>>) {
        self.shell_surface.set_fullscreen(output);
    }
    
    pub fn unset_fullscreen(&self) {
        self.shell_surface.unset_fullscreen();
    }
    
    
    
    
    
    
    pub fn set_min_size(&mut self, size: Option<(u32, u32)>) {
        let (w, h) = size.unwrap_or(MIN_WINDOW_SIZE);
        let (w, h) = self.frame.lock().unwrap().add_borders(w as i32, h as i32);
        self.shell_surface.set_min_size(Some((w, h)));
        if let Some(ref mut inner) = *(self.inner.lock().unwrap()) {
            inner.min_size = size.unwrap_or(MIN_WINDOW_SIZE)
        }
    }
    
    
    
    
    
    
    pub fn set_max_size(&mut self, size: Option<(u32, u32)>) {
        let max_size =
            size.map(|(w, h)| self.frame.lock().unwrap().add_borders(w as i32, h as i32));
        self.shell_surface.set_max_size(max_size);
        if let Some(ref mut inner) = *(self.inner.lock().unwrap()) {
            inner.max_size = size.map(|(w, h)| (w as u32, h as u32));
        }
    }
    
    
    
    
    
    
    pub fn set_theme<T: Theme>(&mut self, theme: T) {
        self.frame.lock().unwrap().set_theme(theme)
    }
}
impl<F: Frame> Drop for Window<F> {
    fn drop(&mut self) {
        self.inner.lock().unwrap().take();
    }
}
pub enum FrameRequest {
    
    Minimize,
    
    Maximize,
    
    UnMaximize,
    
    Close,
    
    Move(Proxy<wl_seat::WlSeat>),
    
    Resize(Proxy<wl_seat::WlSeat>, ResizeEdge),
    
    Refresh,
}
pub trait Frame: Sized + Send {
    
    type Error;
    
    fn init(
        base_surface: &Proxy<wl_surface::WlSurface>,
        compositor: &Proxy<wl_compositor::WlCompositor>,
        subcompositor: &Proxy<wl_subcompositor::WlSubcompositor>,
        shm: &Proxy<wl_shm::WlShm>,
        implementation: Box<FnMut(FrameRequest, u32) + Send>,
    ) -> Result<Self, Self::Error>;
    
    
    
    
    fn set_active(&mut self, active: bool) -> bool;
    
    
    
    
    fn set_maximized(&mut self, maximized: bool) -> bool;
    
    
    
    fn set_hidden(&mut self, hidden: bool);
    
    
    fn set_resizable(&mut self, resizable: bool);
    
    fn new_seat(&mut self, seat: &Proxy<wl_seat::WlSeat>);
    
    
    
    fn resize(&mut self, newsize: (u32, u32));
    
    fn redraw(&mut self);
    
    fn subtract_borders(&self, width: i32, height: i32) -> (i32, i32);
    
    fn add_borders(&self, width: i32, height: i32) -> (i32, i32);
    
    
    
    fn location(&self) -> (i32, i32) {
        (0, 0)
    }
    
    fn set_theme<T: Theme>(&mut self, theme: T);
    
    fn set_title(&mut self, title: String);
}