Documentation
use crate::frame::context::{Render, UpdateType};
use crate::frame::App;
use crate::{Pos, Size};
use glyphon::Viewport;
use std::collections::HashMap;
use std::sync::Arc;
use winit::application::ApplicationHandler;
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::event::{ElementState, Ime, MouseButton, MouseScrollDelta, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
use winit::keyboard::{Key, NamedKey};
use winit::window::{ImePurpose, WindowId};
use crate::window::{UserEvent, WindowKind, WindowType};
use crate::window::ime::{IMEData, IME};
use crate::window::wnit::handle::WInitWindowHandle;
use crate::window::wnit::Window;

pub struct WInitApplication<A> {
    windows: HashMap<super::WindowId, Window>,
    app: Option<A>,
    proxy_event: Option<EventLoopProxy<(super::WindowId, UserEvent)>>,
    rebuilding: bool,
}

impl<A> WInitApplication<A> {
    pub fn new() -> Self {
        WInitApplication {
            windows: HashMap::new(),
            app: None,
            proxy_event: None,
            rebuilding: false,
        }
    }

    pub fn set_app(&mut self, app: Option<A>) {
        self.app = app;
    }

    pub fn set_proxy_event(&mut self, proxy_event: Option<EventLoopProxy<(super::WindowId, UserEvent)>>) {
        self.proxy_event = proxy_event;
    }
}

impl<A: App + 'static> ApplicationHandler<(super::WindowId, UserEvent)> for WInitApplication<A> {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        println!("111111111111111111111111111111");
        let app = self.app.take().unwrap();
        let event = self.proxy_event.take().unwrap();
        let attr = app.window_attributes();
        let winit_window = event_loop.create_window(attr.as_winit_attributes()).unwrap();
        println!("{}", format!("winit id: {:?}", winit_window.id()));
        winit_window.set_ime_allowed(true);
        winit_window.set_ime_cursor_area(PhysicalPosition::new(400, 300), PhysicalSize::new(100, 100));
        winit_window.set_ime_purpose(ImePurpose::Normal);
        let id = super::WindowId::from_winit_id(winit_window.id());
        let handle = WInitWindowHandle::new(winit_window, event);
        let window_type = WindowType {
            kind: WindowKind::Winit(handle),
            id,
            type_: WindowType::ROOT,
            ime: Arc::new(IME::new_winit()),
        };
        let loop_window = Arc::new(window_type);
        let window = pollster::block_on(Window::new_winit(loop_window.clone(), Box::new(app), attr)).unwrap();
        self.windows.insert(loop_window.id(), window);
        loop_window.request_redraw();
    }

    fn user_event(&mut self, _: &ActiveEventLoop, (wid, t): (super::WindowId, UserEvent)) {
        if self.rebuilding {
            println!("Rebuilding");
            return;
        }
        match t {
            UserEvent::ReqUpdate => {
                println!("recv event");
                let window = self.windows.get_mut(&wid).unwrap();
                window.app_ctx.update(window.app_ctx.context.user_update.1.clone(), &mut window.app)
            }
            UserEvent::CreateChild => {}
            UserEvent::UserUpdate => {}
            UserEvent::ReInit => {
                self.rebuilding = true;
                println!("sleep start");
                println!("sleep end");
                let window = self.windows.get_mut(&wid).unwrap();
                let w = window.app_ctx.context.window.clone();
                window.app_ctx.device = pollster::block_on(async {
                    Window::rebuild_device(&window.app_ctx.context.window, |device| {
                        device.on_uncaptured_error(Box::new(move |err| {
                            println!("Error: {:#?}", err);
                            w.request_update_event(UserEvent::ReInit);
                        }))
                    }).await
                }).unwrap();
                println!("1");
                window.app_ctx.context.viewport = Viewport::new(&window.app_ctx.device.device, &window.app_ctx.device.cache);
                println!("2");
                window.app_ctx.context.render = Render::new(&window.app_ctx.device);
                println!("3");
                window.configure_surface();
                println!("4");
                window.app_ctx.update(UpdateType::ReInit, &mut window.app);
                println!("5");
                self.rebuilding = false;
                println!("re init finished");
            }
        }
    }

    fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
        let wid = super::WindowId::from_winit_id(id);
        let window = self.windows.get_mut(&wid);
        if window.is_none() { return; }
        let window = window.unwrap();
        match event {
            WindowEvent::CloseRequested => {
                println!("The close button was pressed; stopping");
                if window.app_ctx.context.window.type_ == WindowType::ROOT {
                    event_loop.exit();
                } else {
                    self.windows.remove(&wid);
                }
            }
            WindowEvent::RedrawRequested => {
                if self.rebuilding {
                    println!("rebuilding window");
                    return;
                }
                println!("11");
                window.render();
            }
            WindowEvent::Resized(size) => {
                window.resize(Size { width: size.width, height: size.height });
            }
            WindowEvent::MouseInput { state, button, .. } => {
                match (state, button) {
                    (ElementState::Pressed, MouseButton::Left) => {
                        window.app_ctx.device.device_input.mouse.mouse_press();
                        window.app_ctx.update(UpdateType::MousePress, &mut window.app);
                    }
                    (ElementState::Released, MouseButton::Left) => {
                        window.app_ctx.device.device_input.mouse.mouse_release();
                        window.app_ctx.update(UpdateType::MouseRelease, &mut window.app);
                        window.app_ctx.device.device_input.mouse.a = 0.0;
                    }
                    (_, _) => {}
                }
            }
            WindowEvent::MouseWheel { delta, .. } => {
                match delta {
                    MouseScrollDelta::LineDelta(x, y) => {
                        window.app_ctx.device.device_input.mouse.delta = (x, y);
                        window.app_ctx.update(UpdateType::MouseWheel, &mut window.app);
                        window.app_ctx.device.device_input.mouse.delta = (0.0, 0.0);
                    }
                    _ => {}
                }
            }
            WindowEvent::CursorMoved { position, .. } => {
                window.app_ctx.device.device_input.mouse.update(Pos { x: position.x as f32, y: position.y as f32 });
                window.app_ctx.update(UpdateType::MouseMove, &mut window.app);
            }
            WindowEvent::KeyboardInput { device_id: _device_id, event, .. } => {
                if !event.state.is_pressed() { return; }
                let key = match event.logical_key {
                    Key::Named(name) => {
                        match name {
                            NamedKey::Enter => crate::key::Key::Enter,
                            NamedKey::Space => crate::key::Key::Space,
                            NamedKey::ArrowDown => crate::key::Key::DownArrow,
                            NamedKey::ArrowLeft => crate::key::Key::LeftArrow,
                            NamedKey::ArrowRight => crate::key::Key::RightArrow,
                            NamedKey::ArrowUp => crate::key::Key::UpArrow,
                            NamedKey::End => crate::key::Key::End,
                            NamedKey::Home => crate::key::Key::Home,
                            NamedKey::Backspace => crate::key::Key::Backspace,
                            NamedKey::Delete => crate::key::Key::Delete,
                            _ => return,
                        }
                    }
                    Key::Character(c) => crate::key::Key::Char(c.as_str().chars().next().unwrap()),
                    Key::Unidentified(_) => return,
                    Key::Dead(_) => return,
                };
                window.app_ctx.update(UpdateType::KeyRelease(key), &mut window.app);
                window.app_ctx.context.window.request_redraw();
            }
            WindowEvent::Ime(ime) => {
                match ime {
                    Ime::Preedit(ps, _) => window.app_ctx.update(UpdateType::IME(IMEData::Preedit(ps.chars().collect())), &mut window.app),
                    Ime::Commit(cs) => window.app_ctx.update(UpdateType::IME(IMEData::Commit(cs.chars().collect())), &mut window.app),
                    _ => {}
                }
            }

            _ => (),
        }
    }
}