use egui::emath::TSTransform;
use egui::TextWrapMode;

#[derive(Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct PanZoom {
    transform: TSTransform,
    drag_value: f32,
}

impl Eq for PanZoom {}

impl crate::Demo for PanZoom {
    fn name(&self) -> &'static str {
        "🔍 Pan Zoom"
    }

    fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
        use crate::View as _;
        let window = egui::Window::new("Pan Zoom")
            .default_width(300.0)
            .default_height(300.0)
            .vscroll(false)
            .open(open);
        window.show(ctx, |ui| self.ui(ui));
    }
}

impl crate::View for PanZoom {
    fn ui(&mut self, ui: &mut egui::Ui) {
        ui.label(
            "Pan, zoom in, and zoom out with scrolling (see the plot demo for more instructions). \
                   Double click on the background to reset.",
        );
        ui.vertical_centered(|ui| {
            ui.add(crate::egui_github_link_file!());
        });
        ui.separator();

        let (id, rect) = ui.allocate_space(ui.available_size());
        let response = ui.interact(rect, id, egui::Sense::click_and_drag());
        // Allow dragging the background as well.
        if response.dragged() {
            self.transform.translation += response.drag_delta();
        }

        // Plot-like reset
        if response.double_clicked() {
            self.transform = TSTransform::default();
        }

        let transform =
            TSTransform::from_translation(ui.min_rect().left_top().to_vec2()) * self.transform;

        if let Some(pointer) = ui.ctx().input(|i| i.pointer.hover_pos()) {
            // Note: doesn't catch zooming / panning if a button in this PanZoom container is hovered.
            if response.hovered() {
                let pointer_in_layer = transform.inverse() * pointer;
                let zoom_delta = ui.ctx().input(|i| i.zoom_delta());
                let pan_delta = ui.ctx().input(|i| i.smooth_scroll_delta);

                // Zoom in on pointer:
                self.transform = self.transform
                    * TSTransform::from_translation(pointer_in_layer.to_vec2())
                    * TSTransform::from_scaling(zoom_delta)
                    * TSTransform::from_translation(-pointer_in_layer.to_vec2());

                // Pan:
                self.transform = TSTransform::from_translation(pan_delta) * self.transform;
            }
        }

        for (i, (pos, callback)) in [
            (
                egui::Pos2::new(0.0, 0.0),
                Box::new(|ui: &mut egui::Ui, _: &mut Self| ui.button("top left!"))
                    as Box<dyn Fn(&mut egui::Ui, &mut Self) -> egui::Response>,
            ),
            (
                egui::Pos2::new(0.0, 120.0),
                Box::new(|ui: &mut egui::Ui, _| ui.button("bottom left?")),
            ),
            (
                egui::Pos2::new(120.0, 120.0),
                Box::new(|ui: &mut egui::Ui, _| ui.button("right bottom :D")),
            ),
            (
                egui::Pos2::new(120.0, 0.0),
                Box::new(|ui: &mut egui::Ui, _| ui.button("right top ):")),
            ),
            (
                egui::Pos2::new(60.0, 60.0),
                Box::new(|ui, state| {
                    use egui::epaint::{pos2, CircleShape, Color32, QuadraticBezierShape, Stroke};
                    // Smiley face.
                    let painter = ui.painter();
                    painter.add(CircleShape::filled(pos2(0.0, -10.0), 1.0, Color32::YELLOW));
                    painter.add(CircleShape::filled(pos2(10.0, -10.0), 1.0, Color32::YELLOW));
                    painter.add(QuadraticBezierShape::from_points_stroke(
                        [pos2(0.0, 0.0), pos2(5.0, 3.0), pos2(10.0, 0.0)],
                        false,
                        Color32::TRANSPARENT,
                        Stroke::new(1.0, Color32::YELLOW),
                    ));

                    ui.add(egui::Slider::new(&mut state.drag_value, 0.0..=100.0).text("My value"))
                }),
            ),
        ]
        .into_iter()
        .enumerate()
        {
            let window_layer = ui.layer_id();
            let id = egui::Area::new(id.with(("subarea", i)))
                .default_pos(pos)
                .order(egui::Order::Middle)
                .constrain(false)
                .show(ui.ctx(), |ui| {
                    ui.set_clip_rect(transform.inverse() * rect);
                    egui::Frame::default()
                        .rounding(egui::Rounding::same(4.0))
                        .inner_margin(egui::Margin::same(8.0))
                        .stroke(ui.ctx().style().visuals.window_stroke)
                        .fill(ui.style().visuals.panel_fill)
                        .show(ui, |ui| {
                            ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
                            callback(ui, self)
                        });
                })
                .response
                .layer_id;
            ui.ctx().set_transform_layer(id, transform);
            ui.ctx().set_sublayer(window_layer, id);
        }
    }
}
