use eframe::egui::{self, Button, Ui, Vec2, pos2, vec2};

#[derive(Clone, Copy, Debug, Default, PartialEq)]
enum Transition {
    #[default]
    None,
    CloseOnNextFrame,
    CloseImmediately,
}

#[derive(Clone, Debug)]
struct State {
    open: bool,
    closable: bool,
    close_on_next_frame: bool,
    start_pos: egui::Pos2,
    focus: Option<egui::Id>,
    events: Option<Vec<egui::Event>>,
}

impl State {
    fn new() -> Self {
        Self {
            open: false,
            closable: false,
            close_on_next_frame: false,
            start_pos: pos2(100.0, 100.0),
            focus: None,
            events: None,
        }
    }

    fn queue_char(&mut self, c: char) {
        let events = self.events.get_or_insert(vec![]);
        if let Some(key) = egui::Key::from_name(&c.to_string()) {
            events.push(egui::Event::Key {
                key,
                physical_key: Some(key),
                pressed: true,
                repeat: false,
                modifiers: Default::default(),
            });
        }
        events.push(egui::Event::Text(c.to_string()));
    }

    fn queue_key(&mut self, key: egui::Key) {
        let events = self.events.get_or_insert(vec![]);
        events.push(egui::Event::Key {
            key,
            physical_key: Some(key),
            pressed: true,
            repeat: false,
            modifiers: Default::default(),
        });
    }
}

impl Default for State {
    fn default() -> Self {
        Self::new()
    }
}

/// A simple keypad widget.
pub struct Keypad {
    id: egui::Id,
}

impl Keypad {
    pub fn new() -> Self {
        Self {
            id: egui::Id::new("keypad"),
        }
    }

    pub fn bump_events(&self, ctx: &egui::Context, raw_input: &mut egui::RawInput) {
        let events = ctx.memory_mut(|m| {
            m.data
                .get_temp_mut_or_default::<State>(self.id)
                .events
                .take()
        });
        if let Some(mut events) = events {
            events.append(&mut raw_input.events);
            raw_input.events = events;
        }
    }

    fn buttons(ui: &mut Ui, state: &mut State) -> Transition {
        let mut trans = Transition::None;
        ui.vertical(|ui| {
            let window_margin = ui.spacing().window_margin;
            let size_1x1 = vec2(32.0, 26.0);
            let _size_1x2 = vec2(32.0, 52.0 + window_margin.topf());
            let _size_2x1 = vec2(64.0 + window_margin.leftf(), 26.0);

            ui.spacing_mut().item_spacing = Vec2::splat(window_margin.leftf());

            ui.horizontal(|ui| {
                if ui.add_sized(size_1x1, Button::new("1")).clicked() {
                    state.queue_char('1');
                }
                if ui.add_sized(size_1x1, Button::new("2")).clicked() {
                    state.queue_char('2');
                }
                if ui.add_sized(size_1x1, Button::new("3")).clicked() {
                    state.queue_char('3');
                }
                if ui.add_sized(size_1x1, Button::new("⏮")).clicked() {
                    state.queue_key(egui::Key::Home);
                }
                if ui.add_sized(size_1x1, Button::new("🔙")).clicked() {
                    state.queue_key(egui::Key::Backspace);
                }
            });
            ui.horizontal(|ui| {
                if ui.add_sized(size_1x1, Button::new("4")).clicked() {
                    state.queue_char('4');
                }
                if ui.add_sized(size_1x1, Button::new("5")).clicked() {
                    state.queue_char('5');
                }
                if ui.add_sized(size_1x1, Button::new("6")).clicked() {
                    state.queue_char('6');
                }
                if ui.add_sized(size_1x1, Button::new("⏭")).clicked() {
                    state.queue_key(egui::Key::End);
                }
                if ui.add_sized(size_1x1, Button::new("⎆")).clicked() {
                    state.queue_key(egui::Key::Enter);
                    trans = Transition::CloseOnNextFrame;
                }
            });
            ui.horizontal(|ui| {
                if ui.add_sized(size_1x1, Button::new("7")).clicked() {
                    state.queue_char('7');
                }
                if ui.add_sized(size_1x1, Button::new("8")).clicked() {
                    state.queue_char('8');
                }
                if ui.add_sized(size_1x1, Button::new("9")).clicked() {
                    state.queue_char('9');
                }
                if ui.add_sized(size_1x1, Button::new("⏶")).clicked() {
                    state.queue_key(egui::Key::ArrowUp);
                }
                if ui.add_sized(size_1x1, Button::new("⌨")).clicked() {
                    trans = Transition::CloseImmediately;
                }
            });
            ui.horizontal(|ui| {
                if ui.add_sized(size_1x1, Button::new("0")).clicked() {
                    state.queue_char('0');
                }
                if ui.add_sized(size_1x1, Button::new(".")).clicked() {
                    state.queue_char('.');
                }
                if ui.add_sized(size_1x1, Button::new("⏴")).clicked() {
                    state.queue_key(egui::Key::ArrowLeft);
                }
                if ui.add_sized(size_1x1, Button::new("⏷")).clicked() {
                    state.queue_key(egui::Key::ArrowDown);
                }
                if ui.add_sized(size_1x1, Button::new("⏵")).clicked() {
                    state.queue_key(egui::Key::ArrowRight);
                }
            });
        });

        trans
    }

    pub fn show(&self, ctx: &egui::Context) {
        let (focus, mut state) = ctx.memory(|m| {
            (
                m.focused(),
                m.data.get_temp::<State>(self.id).unwrap_or_default(),
            )
        });

        let is_first_show = ctx.egui_wants_keyboard_input() && state.focus != focus;
        if is_first_show {
            let y = ctx.global_style().spacing.interact_size.y * 1.25;
            state.open = true;
            state.start_pos = ctx.input(|i| {
                i.pointer
                    .hover_pos()
                    .map_or(pos2(100.0, 100.0), |p| p + vec2(0.0, y))
            });
            state.focus = focus;
        }

        if state.close_on_next_frame {
            state.open = false;
            state.close_on_next_frame = false;
            state.focus = None;
        }

        let mut open = state.open;

        let win = egui::Window::new("⌨ Keypad");
        let win = if is_first_show {
            win.current_pos(state.start_pos)
        } else {
            win.default_pos(state.start_pos)
        };
        let resp = win
            .movable(true)
            .resizable(false)
            .open(&mut open)
            .show(ctx, |ui| Self::buttons(ui, &mut state));

        state.open = open;

        if let Some(resp) = resp {
            match resp.inner {
                Some(Transition::CloseOnNextFrame) => {
                    state.close_on_next_frame = true;
                }
                Some(Transition::CloseImmediately) => {
                    state.open = false;
                    state.focus = None;
                }
                _ => {}
            }
            if !state.closable && resp.response.hovered() {
                state.closable = true;
            }
            if state.closable && resp.response.clicked_elsewhere() {
                state.open = false;
                state.closable = false;
                state.focus = None;
            }
            if is_first_show {
                ctx.move_to_top(resp.response.layer_id);
            }
        }

        if let (true, Some(focus)) = (state.open, state.focus) {
            ctx.memory_mut(|m| {
                m.request_focus(focus);
            });
        }

        ctx.memory_mut(|m| m.data.insert_temp(self.id, state));
    }
}

impl Default for Keypad {
    fn default() -> Self {
        Self::new()
    }
}
