use crate::State;
use glam::{Mat2, Vec2};
use imgui::{
    im_str, AngleSlider, CollapsingHeader, Condition, Context, MouseButton, Slider, SliderFlags,
    Window,
};
use imgui_wgpu::{Renderer, RendererConfig};
use imgui_winit_support::{HiDpiMode, WinitPlatform};
use std::f32::consts::{PI, TAU};
use std::time::Instant;
use wgpu::*;
use winit::event::Event;
use winit::window::Window as WinitWindow;

pub struct Gui {
    context: Context,
    renderer: Renderer,
    platform: WinitPlatform,
    clock: Instant,
}

impl Gui {
    pub fn new(
        window: &WinitWindow,
        device: &Device,
        queue: &Queue,
        texture_format: TextureFormat,
        depth_format: Option<TextureFormat>,
    ) -> Self {
        let mut context = Context::create();
        let renderer = imgui_wgpu::Renderer::new(
            &mut context,
            device,
            queue,
            RendererConfig {
                texture_format,
                depth_format,
                ..RendererConfig::default()
            },
        );
        let mut platform = WinitPlatform::init(&mut context);
        // https://github.com/Yatekii/imgui-wgpu-rs/issues/53
        platform.attach_window(context.io_mut(), window, HiDpiMode::Locked(1.0));

        Self {
            context,
            renderer,
            platform,
            clock: Instant::now(),
        }
    }

    pub fn handle_event(&mut self, window: &WinitWindow, event: &Event<()>) {
        self.platform
            .handle_event(self.context.io_mut(), window, event);
    }

    pub fn render<'pass>(
        &'pass mut self,
        pass: &mut RenderPass<'pass>,
        window: &WinitWindow,
        device: &Device,
        queue: &Queue,
        state: &mut State,
    ) {
        let now = Instant::now();
        self.context.io_mut().update_delta_time(now - self.clock);
        self.clock = now;

        self.platform
            .prepare_frame(self.context.io_mut(), window)
            .unwrap();
        let ui = self.context.frame();
        let io = ui.io();

        if !io.want_capture_mouse {
            // Camera zooming.
            state.camera.zoom *= f32::exp(io.mouse_wheel * state.wheel_sensitivity);

            // Camera panning.
            if io[MouseButton::Left] {
                let [dx, dy] = io.mouse_delta;
                let rotation = Mat2::from_angle(state.camera.horizontal_angle);
                let rotated = rotation * Vec2::new(dx, -dy);
                state.camera.position[0] += rotated.x / state.camera.zoom * state.mouse_sensitivity;
                state.camera.position[1] += rotated.y / state.camera.zoom * state.mouse_sensitivity;
            }

            // Camera rotation.
            if io[MouseButton::Right] {
                let [dx, dy] = io.mouse_delta;
                state.camera.horizontal_angle = (state.camera.horizontal_angle
                    + dx as f32 * state.mouse_sensitivity)
                    .rem_euclid(TAU);
                state.camera.vertical_angle = f32::clamp(
                    state.camera.vertical_angle + dy as f32 * state.mouse_sensitivity,
                    0.0,
                    PI / 2.0,
                );
            }
        }

        ui.show_demo_window(&mut false);
        Window::new(im_str!("Ant Simulator"))
            .position([0.0, 0.0], Condition::Once)
            .always_auto_resize(true)
            .build(&ui, || {
                ui.checkbox(im_str!("Run"), &mut state.is_running);
                ui.text(format!("FPS: {:5.2}", io.framerate));

                if CollapsingHeader::new(im_str!("Simulation"))
                    .default_open(true)
                    .build(&ui)
                {
                    Slider::new(im_str!("Ant speed"))
                        .flags(SliderFlags::LOGARITHMIC)
                        .range(0.0..=0.1)
                        .build(&ui, &mut state.simulation_parameters.ant_speed);
                    Slider::new(im_str!("Ant direction sigma"))
                        .flags(SliderFlags::LOGARITHMIC)
                        .range(0.0..=1.0)
                        .build(&ui, &mut state.simulation_parameters.ant_sigma);
                    Slider::new(im_str!("Ant heuristics rate"))
                        .range(0.0..=10.0)
                        .build(&ui, &mut state.simulation_parameters.heuristics_rate);
                    Slider::new(im_str!("Pheromone strength"))
                        .range(0.0..=1.0)
                        .build(&ui, &mut state.simulation_parameters.pheromone_strength);
                    Slider::new(im_str!("Pheromone evaporation rate"))
                        .flags(SliderFlags::LOGARITHMIC)
                        .range(0.0..=0.01)
                        .build(
                            &ui,
                            &mut state.simulation_parameters.pheromone_evaporation_rate,
                        );
                }

                if CollapsingHeader::new(im_str!("Camera")).build(&ui) {
                    Slider::new(im_str!("Zoom"))
                        .flags(SliderFlags::LOGARITHMIC)
                        .range(0.1..=500.0)
                        .build(&ui, &mut state.camera.zoom);
                    ui.input_float2(im_str!("Position"), &mut state.camera.position)
                        .build();
                    AngleSlider::new(im_str!("Angle (horizontal)"))
                        .range_degrees(0.0..=360.0)
                        .build(&ui, &mut state.camera.horizontal_angle);
                    AngleSlider::new(im_str!("Angle (vertical)"))
                        .range_degrees(0.0..=90.0)
                        .build(&ui, &mut state.camera.vertical_angle);
                }

                if CollapsingHeader::new(im_str!("Control Options")).build(&ui) {
                    Slider::new(im_str!("Mouse sensitivity"))
                        .flags(SliderFlags::LOGARITHMIC)
                        .range(0.001..=1.0)
                        .build(&ui, &mut state.mouse_sensitivity);
                    Slider::new(im_str!("Wheel sensitivity"))
                        .flags(SliderFlags::LOGARITHMIC)
                        .range(0.02..=50.0)
                        .build(&ui, &mut state.wheel_sensitivity);
                }
            });

        self.platform.prepare_render(&ui, window);
        self.renderer
            .render(ui.render(), queue, device, pass)
            .unwrap();
    }
}
