use ratatui::prelude::*;
use ratatui::widgets::{Block, Borders, Paragraph};
use crate::scene::runner::{DisplayedLine, PreparedScene};
use crate::scene::types::MemoryRef;
use crate::state::types::MemoryObject;
use crate::types::AgePhase;
use crate::ui::theme;
use crate::ui::text_reveal::TextReveal;
use crate::ui::widgets::{dialogue, choice_menu, memory_echo};
pub fn render_scene(
frame: &mut Frame,
area: Rect,
prepared: &PreparedScene,
reveal: &TextReveal,
choice_cursor: usize,
age_phase: AgePhase,
chapter_label: &str,
location_label: &str,
memory_objects: &[MemoryObject],
) {
let choice_count = prepared.choices.len();
let choice_height = if reveal.all_complete && choice_count > 0 {
choice_count as u16 + 3 } else {
1 };
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1), Constraint::Min(6), Constraint::Length(choice_height),
])
.split(area);
render_top_bar(frame, chunks[0], chapter_label, location_label, age_phase);
render_dialogue_area(
frame,
chunks[1],
&prepared.lines,
reveal,
&prepared.memory_callbacks,
memory_objects,
prepared.pacing,
);
let choice_list = choice_menu::render_choice_menu(
&prepared.choices,
choice_cursor,
age_phase,
reveal.all_complete,
);
frame.render_widget(choice_list, chunks[2]);
}
fn render_top_bar(
frame: &mut Frame,
area: Rect,
chapter: &str,
location: &str,
age_phase: AgePhase,
) {
let accent = theme::age_accent(age_phase);
let label = theme::age_label(age_phase);
let left = format!(" {} \u{2014} {}", chapter, location);
let right = format!("{} ", label);
let padding = area.width.saturating_sub(left.len() as u16 + right.len() as u16);
let line = Line::from(vec![
Span::styled(left, Style::default().fg(Color::White)),
Span::raw(" ".repeat(padding as usize)),
Span::styled(right, Style::default().fg(accent)),
]);
frame.render_widget(Paragraph::new(line), area);
}
fn render_dialogue_area(
frame: &mut Frame,
area: Rect,
lines: &[DisplayedLine],
reveal: &TextReveal,
callbacks: &[MemoryRef],
memory_objects: &[MemoryObject],
pacing: crate::scene::types::PacingTag,
) {
let dialogue_para = dialogue::render_dialogue(lines, reveal, area.height);
let echo_lines = if reveal.all_complete {
memory_echo::render_echoes(callbacks, memory_objects)
} else {
Vec::new()
};
let border = theme::pacing_border(pacing);
let block = match border {
theme::PacingBorder::None => Block::default().borders(Borders::NONE),
theme::PacingBorder::Plain => Block::default()
.borders(Borders::TOP | Borders::BOTTOM)
.border_style(Style::default().fg(Color::DarkGray)),
theme::PacingBorder::Double => Block::default()
.borders(Borders::TOP | Borders::BOTTOM)
.border_style(Style::default().fg(Color::Rgb(180, 80, 60))),
theme::PacingBorder::Thick => Block::default()
.borders(Borders::TOP | Borders::BOTTOM)
.border_style(Style::default().fg(Color::White).add_modifier(Modifier::BOLD)),
};
let inner = block.inner(area);
frame.render_widget(block, area);
frame.render_widget(dialogue_para, inner);
if !echo_lines.is_empty() {
let echo_start = inner.y + inner.height.saturating_sub(echo_lines.len() as u16 + 1);
if echo_start > inner.y {
let echo_area = Rect::new(inner.x, echo_start, inner.width, echo_lines.len() as u16);
let echo_para = Paragraph::new(echo_lines);
frame.render_widget(echo_para, echo_area);
}
}
}
pub fn render_end_scene(
frame: &mut Frame,
area: Rect,
prepared: &PreparedScene,
reveal: &TextReveal,
age_phase: AgePhase,
chapter_label: &str,
location_label: &str,
memory_objects: &[MemoryObject],
) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1),
Constraint::Min(6),
Constraint::Length(2),
])
.split(area);
render_top_bar(frame, chunks[0], chapter_label, location_label, age_phase);
render_dialogue_area(
frame,
chunks[1],
&prepared.lines,
reveal,
&prepared.memory_callbacks,
memory_objects,
prepared.pacing,
);
if reveal.all_complete {
let hint = Paragraph::new(Line::from(Span::styled(
" [Press Enter to continue]",
theme::dim_style(),
)));
frame.render_widget(hint, chunks[2]);
}
}