use eframe::egui;

use crate::style::{self, ACCENT_BLUE, ACCENT_GREEN, ACCENT_ORANGE, ACCENT_RED, ONE_DARK_PRO};

const BRICK_ROWS: usize = 7;
const BRICK_COLS: usize = 12;
const PADDLE_W: f32 = 0.24;
const PADDLE_H: f32 = 0.030;
const PADDLE_Y: f32 = 0.88;
const PADDLE_SPEED: f32 = 0.92;
const BALL_RADIUS: f32 = 0.010;
const BALL_SPEED: f32 = 0.52;
const MAX_BALL_SPEED: f32 = 0.86;
const PRESSURE_RANGE_MIN: f32 = 0.0;
const PRESSURE_RANGE_MAX: f32 = 7000.0;
const PAUSE_COOLDOWN: f64 = 0.64;

#[derive(Clone, Copy, PartialEq, Eq)]
enum BreakoutPhase {
    Idle,
    Running,
    Paused,
    Won,
    Over,
}

struct Brick {
    rect: egui::Rect,
    alive: bool,
    flash: f32,
}

#[derive(Default, Clone, Copy)]
pub struct BreakoutControl {
    pub axis: f32,
    pub top_force: f32,
    pub left_force: f32,
    pub right_force: f32,
}

pub struct BreakoutGame {
    phase: BreakoutPhase,
    bricks: Vec<Brick>,
    paddle_x: f32,
    ball_pos: egui::Pos2,
    ball_vel: egui::Vec2,
    score: u32,
    combo: u32,
    lives: u32,
    last_time: Option<f64>,
    previous_pause_gesture: bool,
    pause_locked_until: f64,
}

impl Default for BreakoutGame {
    fn default() -> Self {
        let mut game = Self {
            phase: BreakoutPhase::Idle,
            bricks: Vec::new(),
            paddle_x: 0.5,
            ball_pos: egui::pos2(0.5, PADDLE_Y - PADDLE_H - BALL_RADIUS),
            ball_vel: egui::vec2(0.0, 0.0),
            score: 0,
            combo: 0,
            lives: 3,
            last_time: None,
            previous_pause_gesture: false,
            pause_locked_until: 0.0,
        };
        game.rebuild_bricks();
        game
    }
}

impl BreakoutGame {
    pub fn draw_viewport(
        &mut self,
        ctx: &egui::Context,
        rect: egui::Rect,
        visible: &mut bool,
        control: BreakoutControl,
    ) {
        if !*visible {
            self.last_time = None;
            return;
        }

        ctx.request_repaint();
        let now = ctx.input(|input| input.time);
        let dt = self
            .last_time
            .map(|last| (now - last) as f32)
            .unwrap_or(1.0 / 60.0)
            .clamp(0.0, 1.0 / 30.0);
        self.last_time = Some(now);

        let mut close_requested = false;
        egui::Area::new(egui::Id::new("breakout_game_viewport"))
            .fixed_pos(rect.min)
            .order(egui::Order::Middle)
            .show(ctx, |ui| {
                ui.set_min_size(rect.size());
                ui.set_max_size(rect.size());
                style::group_frame().show(ui, |ui| {
                    ui.set_min_size(rect.size() - egui::vec2(20.0, 16.0));
                    if self.draw_contents(ui, control, dt, now, true) {
                        close_requested = true;
                    }
                });
            });

        if close_requested {
            *visible = false;
        }
    }

    fn draw_contents(
        &mut self,
        ui: &mut egui::Ui,
        control: BreakoutControl,
        dt: f32,
        now: f64,
        embedded: bool,
    ) -> bool {
        let mut close_requested = false;
        ui.horizontal(|ui| {
            ui.colored_label(
                ACCENT_BLUE,
                egui::RichText::new("NEON BREAKOUT").monospace().size(13.0),
            );
            ui.label(style::subtle_text("压力矩阵控制 / 左右分区驱动"));
            ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                if embedded && ui.add(style::tag_button("退出分屏")).clicked() {
                    close_requested = true;
                }
                if ui.add(style::tag_button("重开")).clicked() {
                    self.reset();
                    self.start();
                }
                if ui.add(style::tag_button("暂停")).clicked() {
                    self.toggle_pause();
                }
            });
        });

        ui.add_space(5.0);
        ui.horizontal(|ui| {
            status_chip(ui, "状态", self.status_text(), status_color(self.phase));
            status_chip(ui, "分数", self.score.to_string(), ACCENT_GREEN);
            status_chip(ui, "连击", self.combo.to_string(), ACCENT_ORANGE);
            status_chip(ui, "生命", self.lives.to_string(), ACCENT_RED);
        });

        ui.add_space(7.0);
        let available = ui.available_size();
        let game_size = egui::vec2(available.x.max(500.0), (available.y - 6.0).max(330.0));
        let (rect, response) = ui.allocate_exact_size(game_size, egui::Sense::click());
        if response.clicked() && self.phase != BreakoutPhase::Running {
            self.start();
        }

        let keyboard_axis = ui.input(|input| {
            (input.key_down(egui::Key::ArrowRight) as i32
                - input.key_down(egui::Key::ArrowLeft) as i32) as f32
        });
        if ui.input(|input| input.key_pressed(egui::Key::Space)) {
            self.start();
        }
        if ui.input(|input| input.key_pressed(egui::Key::P)) {
            self.toggle_pause();
        }

        let axis = if keyboard_axis.abs() > 0.0 {
            keyboard_axis
        } else {
            control.axis
        };
        self.handle_pressure_gesture(control.top_force, now);
        self.update(dt, axis);
        self.paint(ui, rect, control);
        close_requested
    }

    fn reset(&mut self) {
        self.phase = BreakoutPhase::Idle;
        self.paddle_x = 0.5;
        self.ball_pos = egui::pos2(0.5, PADDLE_Y - PADDLE_H - BALL_RADIUS);
        self.ball_vel = egui::Vec2::ZERO;
        self.score = 0;
        self.combo = 0;
        self.lives = 3;
        self.rebuild_bricks();
    }

    fn start(&mut self) {
        if self.phase == BreakoutPhase::Over || self.phase == BreakoutPhase::Won {
            self.reset();
        }
        if self.phase != BreakoutPhase::Running {
            self.phase = BreakoutPhase::Running;
            if self.ball_vel == egui::Vec2::ZERO {
                self.launch_ball();
            }
        }
    }

    fn toggle_pause(&mut self) {
        self.phase = match self.phase {
            BreakoutPhase::Running => BreakoutPhase::Paused,
            BreakoutPhase::Paused => BreakoutPhase::Running,
            other => other,
        };
    }

    fn handle_pressure_gesture(&mut self, top_force: f32, now: f64) {
        let threshold = pressure_pause_threshold();
        let active = top_force >= threshold;
        if active && !self.previous_pause_gesture && now >= self.pause_locked_until {
            if matches!(
                self.phase,
                BreakoutPhase::Idle | BreakoutPhase::Over | BreakoutPhase::Won
            ) {
                self.start();
            } else {
                self.toggle_pause();
            }
            self.pause_locked_until = now + PAUSE_COOLDOWN;
        }
        self.previous_pause_gesture = active;
    }

    fn update(&mut self, dt: f32, axis: f32) {
        for brick in &mut self.bricks {
            brick.flash = (brick.flash - dt * 2.4).max(0.0);
        }

        if self.phase != BreakoutPhase::Running {
            self.ball_pos = egui::pos2(self.paddle_x, PADDLE_Y - PADDLE_H - BALL_RADIUS);
            return;
        }

        self.paddle_x = (self.paddle_x + axis.clamp(-1.0, 1.0) * PADDLE_SPEED * dt)
            .clamp(PADDLE_W * 0.5, 1.0 - PADDLE_W * 0.5);

        self.ball_pos += self.ball_vel * dt;
        self.resolve_wall_collision();
        self.resolve_paddle_collision();
        self.resolve_brick_collision();
    }

    fn launch_ball(&mut self) {
        let direction = 0.24;
        self.ball_pos = egui::pos2(self.paddle_x, PADDLE_Y - PADDLE_H - BALL_RADIUS);
        self.ball_vel = egui::vec2(direction, -1.0).normalized() * BALL_SPEED;
    }

    fn resolve_wall_collision(&mut self) {
        if self.ball_pos.x - BALL_RADIUS <= 0.0 {
            self.ball_pos.x = BALL_RADIUS;
            self.ball_vel.x = self.ball_vel.x.abs();
        } else if self.ball_pos.x + BALL_RADIUS >= 1.0 {
            self.ball_pos.x = 1.0 - BALL_RADIUS;
            self.ball_vel.x = -self.ball_vel.x.abs();
        }

        if self.ball_pos.y - BALL_RADIUS <= 0.0 {
            self.ball_pos.y = BALL_RADIUS;
            self.ball_vel.y = self.ball_vel.y.abs();
        }

        if self.ball_pos.y - BALL_RADIUS > 1.0 {
            self.lives = self.lives.saturating_sub(1);
            self.combo = 0;
            if self.lives == 0 {
                self.phase = BreakoutPhase::Over;
                self.ball_vel = egui::Vec2::ZERO;
            } else {
                self.ball_pos = egui::pos2(self.paddle_x, PADDLE_Y - PADDLE_H - BALL_RADIUS);
                self.launch_ball();
            }
        }
    }

    fn resolve_paddle_collision(&mut self) {
        if self.ball_vel.y <= 0.0 {
            return;
        }

        let paddle = paddle_rect(self.paddle_x);
        if !circle_hits_rect(self.ball_pos, BALL_RADIUS, paddle) {
            return;
        }

        self.ball_pos.y = paddle.top() - BALL_RADIUS;
        let offset = ((self.ball_pos.x - self.paddle_x) / (PADDLE_W * 0.5)).clamp(-1.0, 1.0);
        let speed = self.ball_vel.length().clamp(BALL_SPEED, MAX_BALL_SPEED);
        self.ball_vel = egui::vec2(offset * 0.82, -1.0).normalized() * speed;
        self.combo = 0;
    }

    fn resolve_brick_collision(&mut self) {
        for brick in &mut self.bricks {
            if !brick.alive || !circle_hits_rect(self.ball_pos, BALL_RADIUS, brick.rect) {
                continue;
            }

            let overlap_x = (self.ball_pos.x + BALL_RADIUS - brick.rect.left())
                .min(brick.rect.right() - (self.ball_pos.x - BALL_RADIUS));
            let overlap_y = (self.ball_pos.y + BALL_RADIUS - brick.rect.top())
                .min(brick.rect.bottom() - (self.ball_pos.y - BALL_RADIUS));
            if overlap_x < overlap_y {
                self.ball_vel.x *= -1.0;
            } else {
                self.ball_vel.y *= -1.0;
            }

            brick.alive = false;
            brick.flash = 1.0;
            self.combo += 1;
            self.score += 40 + self.combo * 5;
            let speed = (self.ball_vel.length() * 1.014).min(MAX_BALL_SPEED);
            self.ball_vel = self.ball_vel.normalized() * speed;
            break;
        }

        if self.bricks.iter().all(|brick| !brick.alive) {
            self.phase = BreakoutPhase::Won;
            self.ball_vel = egui::Vec2::ZERO;
        }
    }

    fn rebuild_bricks(&mut self) {
        self.bricks.clear();
        let gap_x = 0.012;
        let gap_y = 0.017;
        let left = 0.075;
        let top = 0.10;
        let total_w = 0.85;
        let brick_w = (total_w - gap_x * (BRICK_COLS - 1) as f32) / BRICK_COLS as f32;
        let brick_h = 0.040;

        for row in 0..BRICK_ROWS {
            for col in 0..BRICK_COLS {
                let x = left + col as f32 * (brick_w + gap_x);
                let y = top + row as f32 * (brick_h + gap_y);
                self.bricks.push(Brick {
                    rect: egui::Rect::from_min_size(egui::pos2(x, y), egui::vec2(brick_w, brick_h)),
                    alive: true,
                    flash: 0.0,
                });
            }
        }
    }

    fn status_text(&self) -> &'static str {
        match self.phase {
            BreakoutPhase::Idle => "待机",
            BreakoutPhase::Running => "运行",
            BreakoutPhase::Paused => "暂停",
            BreakoutPhase::Won => "过关",
            BreakoutPhase::Over => "结束",
        }
    }

    fn paint(&self, ui: &egui::Ui, rect: egui::Rect, control: BreakoutControl) {
        let painter = ui.painter_at(rect);
        painter.rect_filled(
            rect,
            egui::CornerRadius::same(6),
            egui::Color32::from_rgb(8, 14, 19),
        );
        painter.rect_stroke(
            rect,
            egui::CornerRadius::same(6),
            egui::Stroke::new(1.0, color_alpha(ONE_DARK_PRO.accent, 110)),
            egui::StrokeKind::Outside,
        );

        paint_arena_grid(&painter, rect);
        for (index, brick) in self.bricks.iter().enumerate() {
            paint_brick(&painter, rect, brick, index);
        }

        paint_paddle(&painter, rect, self.paddle_x);
        paint_ball(&painter, rect, self.ball_pos);
        paint_control_meter(&painter, rect, control);

        if self.phase == BreakoutPhase::Idle
            || self.phase == BreakoutPhase::Paused
            || self.phase == BreakoutPhase::Won
            || self.phase == BreakoutPhase::Over
        {
            paint_center_overlay(&painter, rect, self.phase);
        }
    }
}

pub fn control_from_matrix(raw: &[u32], rows: u32, cols: u32) -> BreakoutControl {
    if raw.is_empty() {
        return BreakoutControl::default();
    }

    let rows = rows.max(1) as usize;
    let cols = cols.max(1) as usize;
    let sample_rows = rows.min(2);
    let sample_cols = cols.min(2);
    let top_gesture_rows = rows.min(1);
    let top_gesture_cols = (cols / 3).clamp(1, cols);
    let top_gesture_col_start = (cols - top_gesture_cols) / 2;
    let top_gesture_col_end = top_gesture_col_start + top_gesture_cols;
    let avg = |row_start: usize, row_end: usize, col_start: usize, col_end: usize| -> f32 {
        let mut sum = 0.0;
        let mut count = 0.0;
        for row in row_start..row_end {
            for col in col_start..col_end {
                let index = row * cols + col;
                if let Some(value) = raw.get(index) {
                    sum += *value as f32;
                    count += 1.0;
                }
            }
        }
        if count > 0.0 { sum / count } else { 0.0 }
    };

    let tl = avg(0, sample_rows, 0, sample_cols);
    let tr = avg(0, sample_rows, cols.saturating_sub(sample_cols), cols);
    let bl = avg(rows.saturating_sub(sample_rows), rows, 0, sample_cols);
    let br = avg(
        rows.saturating_sub(sample_rows),
        rows,
        cols.saturating_sub(sample_cols),
        cols,
    );

    let left_force = tl + bl;
    let right_force = tr + br;
    let top_force = avg(
        0,
        top_gesture_rows,
        top_gesture_col_start,
        top_gesture_col_end,
    );
    let span = 1200.0_f32.max((PRESSURE_RANGE_MAX - PRESSURE_RANGE_MIN) * 0.22);
    let raw_axis = ((right_force - left_force) / span).clamp(-1.0, 1.0);
    let axis = if raw_axis.abs() < 0.045 {
        0.0
    } else {
        raw_axis
    };

    BreakoutControl {
        axis,
        top_force,
        left_force,
        right_force,
    }
}

fn pressure_pause_threshold() -> f32 {
    420.0_f32.max(((PRESSURE_RANGE_MAX - PRESSURE_RANGE_MIN).max(1000.0) * 0.07).round())
}

fn paddle_rect(paddle_x: f32) -> egui::Rect {
    egui::Rect::from_center_size(
        egui::pos2(paddle_x, PADDLE_Y),
        egui::vec2(PADDLE_W, PADDLE_H),
    )
}

fn circle_hits_rect(center: egui::Pos2, radius: f32, rect: egui::Rect) -> bool {
    let closest_x = center.x.clamp(rect.left(), rect.right());
    let closest_y = center.y.clamp(rect.top(), rect.bottom());
    let dx = center.x - closest_x;
    let dy = center.y - closest_y;
    dx * dx + dy * dy <= radius * radius
}

fn status_chip(ui: &mut egui::Ui, label: &'static str, value: impl ToString, color: egui::Color32) {
    egui::Frame::new()
        .fill(color_alpha(ONE_DARK_PRO.panel_deep, 190))
        .stroke(egui::Stroke::new(1.0, color_alpha(color, 92)))
        .corner_radius(egui::CornerRadius::same(4))
        .inner_margin(egui::Margin::symmetric(8, 4))
        .show(ui, |ui| {
            ui.horizontal(|ui| {
                ui.colored_label(ONE_DARK_PRO.text_subtle, label);
                ui.colored_label(color, value.to_string());
            });
        });
}

fn status_color(phase: BreakoutPhase) -> egui::Color32 {
    match phase {
        BreakoutPhase::Idle => ONE_DARK_PRO.text_dim,
        BreakoutPhase::Running => ACCENT_GREEN,
        BreakoutPhase::Paused => ACCENT_ORANGE,
        BreakoutPhase::Won => ACCENT_GREEN,
        BreakoutPhase::Over => ACCENT_RED,
    }
}

fn to_screen(rect: egui::Rect, point: egui::Pos2) -> egui::Pos2 {
    egui::pos2(
        rect.left() + point.x * rect.width(),
        rect.top() + point.y * rect.height(),
    )
}

fn to_screen_rect(rect: egui::Rect, local: egui::Rect) -> egui::Rect {
    egui::Rect::from_min_max(
        rect.left_top() + local.min.to_vec2() * rect.size(),
        rect.left_top() + local.max.to_vec2() * rect.size(),
    )
}

fn paint_arena_grid(painter: &egui::Painter, rect: egui::Rect) {
    // 游戏背景保持纯净，避免网格和外框干扰砖块本身的色块节奏。
    painter.rect_filled(
        rect,
        egui::CornerRadius::same(6),
        egui::Color32::from_rgb(2, 8, 10),
    );

    painter.rect_stroke(
        rect,
        egui::CornerRadius::same(6),
        egui::Stroke::new(1.0, color_alpha(ONE_DARK_PRO.accent, 70)),
        egui::StrokeKind::Inside,
    );
}

fn paint_brick(painter: &egui::Painter, arena: egui::Rect, brick: &Brick, index: usize) {
    let rect = to_screen_rect(arena, brick.rect);
    if brick.alive {
        let row = index / BRICK_COLS;
        let col = index % BRICK_COLS;
        // 参考示例图：纯色矩形砖块，黑色间隔由砖块之间的空隙自然露出。
        painter.rect_filled(rect, egui::CornerRadius::ZERO, brick_color(row, col));
    } else if brick.flash > 0.0 {
        let alpha = (brick.flash * 150.0) as u8;
        painter.rect_stroke(
            rect.expand(4.0 * brick.flash),
            egui::CornerRadius::same(4),
            egui::Stroke::new(1.4, color_alpha(ONE_DARK_PRO.accent_hot, alpha)),
            egui::StrokeKind::Outside,
        );
    }
}

fn paint_paddle(painter: &egui::Painter, arena: egui::Rect, paddle_x: f32) {
    let rect = to_screen_rect(arena, paddle_rect(paddle_x));
    // 托盘也改成纯色块，和砖块保持同一种视觉语言。
    painter.rect_filled(rect, egui::CornerRadius::ZERO, ACCENT_BLUE);
}

fn paint_ball(painter: &egui::Painter, arena: egui::Rect, ball_pos: egui::Pos2) {
    let center = to_screen(arena, ball_pos);
    let radius = BALL_RADIUS * arena.width().min(arena.height());
    // 球保留为一个普通实心圆，避免额外高光和光晕。
    painter.circle_filled(center, radius, egui::Color32::from_rgb(241, 207, 70));
}

fn paint_control_meter(painter: &egui::Painter, arena: egui::Rect, control: BreakoutControl) {
    let meter_width = arena.width().mul_add(0.30, 0.0).clamp(180.0, 240.0);
    let meter = egui::Rect::from_min_size(
        arena.left_bottom() + egui::vec2(18.0, -34.0),
        egui::vec2(meter_width, 18.0),
    );
    painter.rect_filled(
        meter,
        egui::CornerRadius::same(4),
        color_alpha(ONE_DARK_PRO.panel_deep, 220),
    );
    painter.rect_stroke(
        meter,
        egui::CornerRadius::same(4),
        egui::Stroke::new(1.0, color_alpha(ONE_DARK_PRO.border, 120)),
        egui::StrokeKind::Outside,
    );
    let center_x = meter.center().x;
    painter.line_segment(
        [
            egui::pos2(center_x, meter.top()),
            egui::pos2(center_x, meter.bottom()),
        ],
        egui::Stroke::new(1.0, ONE_DARK_PRO.border_soft),
    );
    let marker_x = center_x + control.axis.clamp(-1.0, 1.0) * meter.width() * 0.45;
    painter.circle_filled(
        egui::pos2(marker_x, meter.center().y),
        5.0,
        color_alpha(ACCENT_GREEN, 210),
    );
    painter.circle_filled(
        egui::pos2(marker_x, meter.center().y),
        2.3,
        color_alpha(egui::Color32::WHITE, 150),
    );
    painter.text(
        meter.right_center() + egui::vec2(10.0, 0.0),
        egui::Align2::LEFT_CENTER,
        format!(
            "L {:>4.0}  R {:>4.0}  T {:>4.0}",
            control.left_force, control.right_force, control.top_force
        ),
        egui::FontId::monospace(10.0),
        ONE_DARK_PRO.text_dim,
    );
}

fn paint_center_overlay(painter: &egui::Painter, rect: egui::Rect, phase: BreakoutPhase) {
    painter.rect_filled(
        rect,
        egui::CornerRadius::same(6),
        color_alpha(egui::Color32::BLACK, 82),
    );
    let (title, detail) = match phase {
        BreakoutPhase::Idle => ("按压顶部或点击开始", "左右分区压力控制挡板"),
        BreakoutPhase::Paused => ("已暂停", "再次按压顶部、P 或点击暂停继续"),
        BreakoutPhase::Won => ("恭喜过关", "按压顶部、点击或空格重新开始"),
        BreakoutPhase::Over => ("游戏结束", "点击或空格重开"),
        BreakoutPhase::Running => ("", ""),
    };
    painter.text(
        rect.center() - egui::vec2(0.0, 10.0),
        egui::Align2::CENTER_CENTER,
        title,
        egui::FontId::proportional(24.0),
        ONE_DARK_PRO.text,
    );
    painter.text(
        rect.center() + egui::vec2(0.0, 20.0),
        egui::Align2::CENTER_CENTER,
        detail,
        egui::FontId::proportional(13.0),
        ONE_DARK_PRO.text_dim,
    );
}

fn brick_color(row: usize, col: usize) -> egui::Color32 {
    const PALETTE: [egui::Color32; 5] = [
        egui::Color32::from_rgb(46, 178, 104),
        egui::Color32::from_rgb(73, 188, 220),
        egui::Color32::from_rgb(234, 199, 73),
        egui::Color32::from_rgb(232, 125, 64),
        egui::Color32::from_rgb(237, 68, 62),
    ];

    PALETTE[(col + row * 2) % PALETTE.len()]
}

fn color_alpha(color: egui::Color32, alpha: u8) -> egui::Color32 {
    egui::Color32::from_rgba_premultiplied(color.r(), color.g(), color.b(), alpha)
}
