use crate::connection::ConnectionManager;
use crate::recording::Recorder;
use crate::render::{ActiveMode, FingerMode, HandGatewayMode};
use crate::style::{ONE_DARK_PRO, apply_fonts, apply_theme, layout};
use crate::ui::SerialMode;
use crate::{
    matrix::{MATRIX_COLS, MATRIX_ROWS},
    render::{
        BackgroundRenderResources, PRESSURE_CELL_COUNT, PressureFrame, WgpuBackgroundCallback,
    },
    ui::{
        ConfigPanelState, ConnectPanelState, FloatingPanelState, MatrixConfigState,
        draw_config_panel, draw_export_panel, draw_matrix_config_panel, draw_stats_panel,
        panel_restore_item,
    },
};
use eframe::{egui, egui_wgpu};
use std::sync::Arc;

const DATA_LOG_EVERY_FRAMES: u64 = 30;

pub struct EskinDesktopApp {
    connect_panel: FloatingPanelState,
    connect_state: ConnectPanelState,
    connection: Arc<ConnectionManager>,
    pressure_matrix: PressureFrame,
    data_log_frame: u64,
    scene_panel: FloatingPanelState,
    config_panel: FloatingPanelState,
    config_state: ConfigPanelState,
    stats_panel: FloatingPanelState,
    // New: recording, export, matrix config, signal charts
    recorder: Recorder,
    export_panel: FloatingPanelState,
    export_path: String,
    matrix_config_panel: FloatingPanelState,
    matrix_config: MatrixConfigState,
    signal_history: Vec<f32>,
    active_mode: ActiveMode,
}

impl EskinDesktopApp {
    pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
        egui_extras::install_image_loaders(&cc.egui_ctx);
        apply_fonts(&cc.egui_ctx);
        apply_theme(&cc.egui_ctx, &ONE_DARK_PRO);

        let wgpu_state = cc
            .wgpu_render_state
            .as_ref()
            .expect("need open eframe wgpu renderer feature");

        let mut renderer = wgpu_state.renderer.write();
        renderer
            .callback_resources
            .insert(BackgroundRenderResources::new(
                &wgpu_state.device,
                &wgpu_state.queue,
                &wgpu_state.target_format,
                MATRIX_ROWS,
                MATRIX_COLS,
            ));

        Self {
            connect_panel: FloatingPanelState::new([0.0, 0.0], [layout::RIGHT_TAG_X, 48.0]),
            connect_state: ConnectPanelState::default(),
            connection: Arc::new(ConnectionManager::new()),
            pressure_matrix: [[0.0, 0.0]; PRESSURE_CELL_COUNT],
            data_log_frame: 0,
            scene_panel: FloatingPanelState::new(
                [layout::LEFT_X, layout::TOP_Y],
                [layout::LEFT_TAG_X, layout::TOP_Y],
            ),
            config_panel: FloatingPanelState::new(
                [layout::RIGHT_X, layout::TOP_Y],
                [layout::RIGHT_TAG_X, layout::TOP_Y],
            ),
            config_state: ConfigPanelState::default(),
            stats_panel: FloatingPanelState::new(
                [layout::LEFT_X, layout::LEFT_SECOND_Y],
                [layout::LEFT_TAG_X, layout::LEFT_SECOND_Y],
            ),
            recorder: Recorder::full(),
            export_panel: FloatingPanelState::new(
                [layout::LEFT_X, layout::TOP_Y],
                [layout::LEFT_TAG_X, layout::TOP_Y],
            ),
            export_path: String::new(),
            matrix_config_panel: FloatingPanelState::new(
                [layout::RIGHT_X, layout::RIGHT_SECOND_Y],
                [layout::RIGHT_TAG_X, layout::RIGHT_SECOND_Y],
            ),
            matrix_config: MatrixConfigState::default(),
            signal_history: Vec::with_capacity(128),
            active_mode: ActiveMode::Finger(FingerMode {
                rows: 12,
                cols: 7,
                range: 0..7000,
                dot: true,
            }),
        }
    }

    fn draw_wgpu_background(&mut self, ui: &mut egui::Ui) {
        self.update_pressure_matrix();

        let rect = ui.max_rect();
        let width = rect.width().max(1.0);
        let height = rect.height().max(1.0);

        ui.painter().add(egui_wgpu::Callback::new_paint_callback(
            rect,
            WgpuBackgroundCallback {
                width,
                height,
                pressure: self.pressure_matrix,
                active_mode: self.active_mode.clone(),
            },
        ));
    }

    fn update_pressure_matrix(&mut self) {
        if let Some(sample) = self.connection.take_latest_sample() {
            normalize_pressure_sample(
                &sample.matrix,
                sample.rows,
                sample.cols,
                &mut self.pressure_matrix,
            );

            // Feed data to recorder
            self.recorder.add_frame(&sample.matrix);

            // Update signal history (sum of all pressure values)
            let total: f32 = sample.matrix.iter().map(|v| *v as f32).sum();
            self.signal_history.push(total);
            if self.signal_history.len() > 128 {
                self.signal_history.remove(0);
            }

            self.data_log_frame += 1;
            if self.data_log_frame % DATA_LOG_EVERY_FRAMES == 0 {
                log_pressure_sample(&sample.matrix, sample.rows, sample.cols);
            }
        }
    }

    fn draw_title_bar(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
        let title_bar_height = layout::TITLE_BAR_HEIGHT;
        let title_bar_rect = ui
            .allocate_space(egui::vec2(ui.available_width(), title_bar_height))
            .1;

        // Paint background
        ui.painter().rect_filled(
            title_bar_rect,
            egui::CornerRadius::ZERO,
            ONE_DARK_PRO.panel_deep,
        );
        ui.painter().line_segment(
            [title_bar_rect.left_bottom(), title_bar_rect.right_bottom()],
            egui::Stroke::new(1.0, ONE_DARK_PRO.border),
        );

        // Drag-to-move: double-click to maximize, drag to move
        let title_bar_response = ui.interact(
            title_bar_rect,
            egui::Id::new("title_bar"),
            egui::Sense::click_and_drag(),
        );
        if title_bar_response.drag_started() {
            ui.ctx().send_viewport_cmd(egui::ViewportCommand::StartDrag);
        }
        if title_bar_response.double_clicked() {
            let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));
            ui.ctx()
                .send_viewport_cmd(egui::ViewportCommand::Maximized(!is_maximized));
        }

        // macOS traffic light buttons (close / minimize / maximize)
        let btn_size = 14.0;
        let btn_spacing = 8.0;
        let btns_start = title_bar_rect.left_center() + egui::vec2(12.0, 0.0);

        let btn_close_center = btns_start;
        let btn_min_center = btns_start + egui::vec2(btn_size + btn_spacing, 0.0);
        let btn_max_center = btns_start + egui::vec2((btn_size + btn_spacing) * 2.0, 0.0);

        // Close (red)
        let close_rect =
            egui::Rect::from_center_size(btn_close_center, egui::vec2(btn_size, btn_size));
        let close_resp = ui.interact(close_rect, egui::Id::new("btn_close"), egui::Sense::click());
        let close_color = egui::Color32::from_rgb(255, 95, 86);
        ui.painter()
            .circle_filled(btn_close_center, btn_size / 2.0, close_color);
        // Draw × when hovered
        if close_resp.hovered() {
            ui.painter().line_segment(
                [
                    btn_close_center + egui::vec2(-3.0, -3.0),
                    btn_close_center + egui::vec2(3.0, 3.0),
                ],
                egui::Stroke::new(1.5, egui::Color32::from_rgb(80, 0, 0)),
            );
            ui.painter().line_segment(
                [
                    btn_close_center + egui::vec2(3.0, -3.0),
                    btn_close_center + egui::vec2(-3.0, 3.0),
                ],
                egui::Stroke::new(1.5, egui::Color32::from_rgb(80, 0, 0)),
            );
        }
        if close_resp.clicked() {
            ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
        }

        // Minimize (yellow)
        let min_rect = egui::Rect::from_center_size(btn_min_center, egui::vec2(btn_size, btn_size));
        let min_resp = ui.interact(min_rect, egui::Id::new("btn_min"), egui::Sense::click());
        let min_color = egui::Color32::from_rgb(255, 189, 46);
        ui.painter()
            .circle_filled(btn_min_center, btn_size / 2.0, min_color);
        if min_resp.hovered() {
            ui.painter().line_segment(
                [
                    btn_min_center + egui::vec2(-3.0, 0.0),
                    btn_min_center + egui::vec2(3.0, 0.0),
                ],
                egui::Stroke::new(1.5, egui::Color32::from_rgb(120, 80, 0)),
            );
        }
        if min_resp.clicked() {
            ui.ctx()
                .send_viewport_cmd(egui::ViewportCommand::Minimized(true));
        }

        // Maximize (green)
        let max_rect = egui::Rect::from_center_size(btn_max_center, egui::vec2(btn_size, btn_size));
        let max_resp = ui.interact(max_rect, egui::Id::new("btn_max"), egui::Sense::click());
        let max_color = egui::Color32::from_rgb(39, 201, 63);
        ui.painter()
            .circle_filled(btn_max_center, btn_size / 2.0, max_color);
        if max_resp.hovered() {
            let s = 3.0;
            ui.painter().rect_stroke(
                egui::Rect::from_center_size(btn_max_center, egui::vec2(s * 2.0, s * 2.0)),
                egui::CornerRadius::same(1),
                egui::Stroke::new(1.5, egui::Color32::from_rgb(0, 80, 10)),
                egui::StrokeKind::Outside,
            );
        }
        if max_resp.clicked() {
            let is_maximized = ui.input(|i| i.viewport().maximized.unwrap_or(false));
            ui.ctx()
                .send_viewport_cmd(egui::ViewportCommand::Maximized(!is_maximized));
        }
    }

    fn draw_floating_panels(&mut self, ctx: &egui::Context) {
        // draw_scene_panel(ctx, &mut self.scene_panel);
        if let Some(next_mode) =
            draw_config_panel(ctx, &mut self.config_panel, &mut self.config_state)
        {
            self.switch_mode(next_mode);
        }
        draw_stats_panel(ctx, &mut self.stats_panel);
        draw_export_panel(
            ctx,
            &mut self.export_panel,
            &self.recorder,
            &mut self.export_path,
        );
        draw_matrix_config_panel(ctx, &mut self.matrix_config_panel, &mut self.matrix_config);
    }

    fn draw_panel_context_menu(&mut self, ui: &mut egui::Ui) {
        let response = ui.interact(
            ui.max_rect(),
            egui::Id::new("workspace_panel_context_menu"),
            egui::Sense::click(),
        );

        response.context_menu(|ui| {
            ui.label("显示面板");
            ui.separator();

            panel_restore_item(ui, "配置", &mut self.config_panel);
            panel_restore_item(ui, "录制", &mut self.export_panel);
            panel_restore_item(ui, "矩阵", &mut self.matrix_config_panel);
            panel_restore_item(ui, "统计", &mut self.stats_panel);

            ui.separator();

            if ui.button("全部显示").clicked() {
                self.config_panel.visible = true;
                self.export_panel.visible = true;
                self.matrix_config_panel.visible = true;
                self.stats_panel.visible = true;
                ui.close();
            }
        });
    }

    fn switch_mode(&mut self, next: SerialMode) {
        self.connect_state.mode = next;
        self.config_state.mode = next;
        self.connection.disconnect();
        self.pressure_matrix.fill([0.0, 0.0]);
        self.data_log_frame = 0;

        self.active_mode = match next {
            SerialMode::Finger => ActiveMode::Finger(FingerMode {
                rows: self.matrix_config.rows,
                cols: self.matrix_config.cols,
                range: 0..7000,
                dot: true,
            }),
            SerialMode::Hand => ActiveMode::Hand(HandGatewayMode { range: 0..7000 }),
        }
    }
}

fn log_pressure_sample(raw: &[u32], rows: u32, cols: u32) {
    let max = raw.iter().copied().max().unwrap_or(0);
    let sum: u64 = raw.iter().map(|value| *value as u64).sum();
    let non_zero = raw.iter().filter(|value| **value != 0).count();
    let preview = raw
        .iter()
        .take(12)
        .map(u32::to_string)
        .collect::<Vec<_>>()
        .join(", ");

    println!(
        "[pressure] {rows}x{cols} cells={} non_zero={non_zero} max={max} sum={sum} first=[{preview}]",
        raw.len()
    );
}

fn normalize_pressure_sample(raw: &[u32], rows: u32, cols: u32, normalized: &mut PressureFrame) {
    const RANGE_MIN: f32 = 0.0;
    const RANGE_MAX: f32 = 7000.0;

    normalized.fill([0.0, 0.0]);

    let src_cols = cols.max(1);
    let copy_rows = MATRIX_ROWS.min(rows);
    let copy_cols = MATRIX_COLS.min(cols);

    for row in 0..copy_rows {
        for col in 0..copy_cols {
            let src_index = (row * src_cols + col) as usize;
            let dst_index = (row * MATRIX_COLS + col) as usize;
            if let Some(value) = raw.get(src_index) {
                let raw_value = *value as f32;
                let mapped = ((raw_value - RANGE_MIN) / (RANGE_MAX - RANGE_MIN)).clamp(0.0, 1.0);
                let display_value = if raw_value <= RANGE_MIN + 4.0 {
                    0.0
                } else {
                    raw_value.round().min(9999.0)
                };
                normalized[dst_index] = [mapped, display_value];
            }
        }
    }
}

impl eframe::App for EskinDesktopApp {
    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
        let ctx = ui.ctx().clone();

        self.draw_wgpu_background(ui);
        self.draw_panel_context_menu(ui);
        self.draw_title_bar(ui, frame);
        self.draw_floating_panels(&ctx);

        // Keep repainting while the wgpu background is a realtime viewport.
        ctx.request_repaint();
    }
}
