use ratatui::prelude::*;
use ratatui::buffer::Buffer;
use crate::app::App;
use crate::games::roulette::*;
use crate::ui::theme;
pub fn draw_roulette(f: &mut Frame, app: &App, game: &RouletteGame, area: Rect) {
let buf = f.buffer_mut();
let w = area.width as usize;
let h = area.height as usize;
for y in area.top()..area.bottom() {
for x in area.left()..area.right() {
buf[(x, y)].set_char(' ').set_bg(theme::BG);
}
}
let title = "═══ R O U L E T T E ═══";
draw_str(buf, area, (w - title.len()) / 2, 1, title, theme::RED);
match &game.phase {
RoulettePhase::Betting => draw_betting(buf, area, game, app, w, h),
RoulettePhase::Spinning => draw_spinning(buf, area, game, app, w, h),
RoulettePhase::Result => draw_result(buf, area, game, app, w, h),
}
}
fn draw_betting(buf: &mut Buffer, area: Rect, game: &RouletteGame, app: &App, w: usize, h: usize) {
let options_y = 4;
draw_str(buf, area, 4, options_y, "SELECT BET TYPE:", theme::AMBER);
for (i, bet_type) in BET_OPTIONS.iter().enumerate() {
let y = options_y + 2 + i;
let selected = game.cursor == i;
let prefix = if selected { "▸ " } else { " " };
let label = bet_type.label();
let ratio = format!("{}:1", bet_type.payout_ratio());
let name_color = if selected { theme::WHITE } else { theme::GRAY };
let _ratio_color = if selected { theme::GREEN } else { theme::DARK_GRAY };
let color_indicator = match bet_type {
BetType::Red => " ■",
BetType::Black => " ■",
_ => "",
};
let ind_color = match bet_type {
BetType::Red => theme::RED,
BetType::Black => theme::CARD_BLACK,
_ => theme::BG,
};
draw_str(buf, area, 4, y, &format!("{}{:10} {:>5}", prefix, label, ratio), name_color);
if !color_indicator.is_empty() {
draw_str(buf, area, 4 + prefix.len() + label.len(), y, color_indicator, ind_color);
}
if selected {
for x in 3..(w.min(25)) {
let ax = area.left() + x as u16;
let ay = area.top() + y as u16;
if ax < area.right() && ay < area.bottom() {
buf[(ax, ay)].set_bg(Color::Rgb(15, 15, 25));
}
}
}
}
let input_x = w / 2 + 2;
let input_y = options_y + 2;
draw_str(buf, area, input_x, input_y, "BET AMOUNT:", theme::AMBER);
let bet_display = if game.bet_input.is_empty() { "_ ".to_string() } else { format!("{}_", game.bet_input) };
draw_str(buf, area, input_x, input_y + 2, &format!("◈ {} TOKENS", bet_display), theme::GREEN);
draw_str(buf, area, input_x, input_y + 4, &format!("Balance: {}", app.tokens), theme::GRAY);
if !game.bets.is_empty() {
draw_str(buf, area, input_x, input_y + 7, "ACTIVE BETS:", theme::AMBER);
for (i, bet) in game.bets.iter().enumerate() {
let y = input_y + 8 + i;
if y >= h.saturating_sub(3) { break; }
let line = format!(" {} — {} tokens", bet.bet_type.label(), bet.amount);
draw_str(buf, area, input_x, y, &line, theme::CYAN_DIM);
}
let total = format!(" TOTAL: {} tokens", game.total_bet);
draw_str(buf, area, input_x, input_y + 8 + game.bets.len(), &total, theme::AMBER_DIM);
}
draw_number_grid(buf, area, w, h, None);
let hint = "↑↓ Select · [0-9] Amount · [ENTER] Place Bet · [SPACE] Spin · [ESC] Back";
draw_str(buf, area, (w.saturating_sub(hint.len())) / 2, h - 2, hint, theme::DARK_GRAY);
}
fn draw_spinning(buf: &mut Buffer, area: Rect, game: &RouletteGame, app: &App, w: usize, h: usize) {
let cy = h / 2 - 2;
let num = game.spin_display;
let color = match number_color(num) {
RouletteColor::Red => theme::RED,
RouletteColor::Black => theme::WHITE,
RouletteColor::Green => theme::GREEN,
};
let num_str = format!("{:02}", num);
let display = format!("╔════════╗\n║ {} ║\n╚════════╝", num_str);
let lines: Vec<&str> = display.lines().collect();
for (i, line) in lines.iter().enumerate() {
draw_str(buf, area, (w - 10) / 2, cy + i, line, color);
}
let spinner = ["◐", "◓", "◑", "◒"];
let s = spinner[(app.frame as usize / 4) % spinner.len()];
draw_str(buf, area, (w - 1) / 2, cy + 4, s, theme::AMBER);
draw_number_grid(buf, area, w, h, None);
}
fn draw_result(buf: &mut Buffer, area: Rect, game: &RouletteGame, _app: &App, w: usize, h: usize) {
let cy = h / 2 - 4;
let result = game.result.unwrap_or(0);
let color = match number_color(result) {
RouletteColor::Red => theme::RED,
RouletteColor::Black => theme::WHITE,
RouletteColor::Green => theme::GREEN,
};
let color_name = match number_color(result) {
RouletteColor::Red => "RED",
RouletteColor::Black => "BLACK",
RouletteColor::Green => "GREEN",
};
let num_str = format!("{:02}", result);
draw_str(buf, area, (w - 10) / 2, cy, "╔════════╗", color);
draw_str(buf, area, (w - 10) / 2, cy + 1, &format!("║ {} ║", num_str), color);
draw_str(buf, area, (w - 10) / 2, cy + 2, "╚════════╝", color);
draw_str(buf, area, (w - color_name.len()) / 2, cy + 3, color_name, color);
let payout = game.last_payout;
let payout_str = if payout > 0 {
format!("+{} TOKENS", payout)
} else if payout < 0 {
format!("{} TOKENS", payout)
} else {
"BREAK EVEN".to_string()
};
let payout_color = if payout > 0 { theme::GREEN } else if payout < 0 { theme::RED } else { theme::GRAY };
draw_str(buf, area, (w - payout_str.len()) / 2, cy + 5, &payout_str, payout_color);
let by = cy + 7;
for (i, bet) in game.bets.iter().enumerate() {
if by + i >= h.saturating_sub(4) { break; }
let won = bet.bet_type.wins(result);
let icon = if won { "✓" } else { "✗" };
let c = if won { theme::GREEN } else { theme::RED_DIM };
let line = format!(" {} {} — {}", icon, bet.bet_type.label(), bet.amount);
draw_str(buf, area, (w - line.len()) / 2, by + i, &line, c);
}
draw_number_grid(buf, area, w, h, Some(result));
draw_str(buf, area, (w - 30) / 2, h - 2, "[ENTER] New Round · [ESC] Back", theme::DARK_GRAY);
}
fn draw_number_grid(buf: &mut Buffer, area: Rect, w: usize, h: usize, highlight: Option<u8>) {
let grid_y = h.saturating_sub(6);
let grid_x = (w.saturating_sub(48)) / 2;
let zero_color = if highlight == Some(0) { theme::GREEN } else { Color::Rgb(0, 80, 0) };
draw_str(buf, area, grid_x, grid_y, " 0 ", zero_color);
for row in 0..3 {
let y = grid_y + 1 + row;
for col in 0..12 {
let num = (col * 3 + (3 - row)) as u8; if num > 36 { continue; }
let x = grid_x + 3 + col * 4;
let is_highlighted = highlight == Some(num);
let base_color = match number_color(num) {
RouletteColor::Red => if is_highlighted { theme::RED } else { Color::Rgb(100, 0, 0) },
RouletteColor::Black => if is_highlighted { theme::WHITE } else { Color::Rgb(60, 60, 70) },
RouletteColor::Green => theme::GREEN,
};
let bg = if is_highlighted { Color::Rgb(40, 40, 0) } else { theme::BG };
let s = format!("{:>2} ", num);
let mut cx = area.left() + x as u16;
let cy = area.top() + y as u16;
if cy < area.bottom() {
for ch in s.chars() {
if cx >= area.right() { break; }
buf[(cx, cy)].set_char(ch).set_fg(base_color).set_bg(bg);
cx += 1;
}
}
}
}
}
fn draw_str(buf: &mut Buffer, area: Rect, x: usize, y: usize, s: &str, fg: Color) {
let mut cx = area.left() + x as u16;
let cy = area.top() + y as u16;
if cy >= area.bottom() { return; }
for ch in s.chars() {
if cx >= area.right() { break; }
buf[(cx, cy)].set_char(ch).set_fg(fg);
cx += 1;
}
}