use ratatui::Frame;
use ratatui::layout::{Constraint, Direction, Layout};
use ratatui::style::Modifier;
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Paragraph};
use crate::presentation::tui::components::bottom_bar::bottom_bar;
use crate::presentation::tui::components::schedule_time::schedule_time;
use crate::presentation::tui::components::title_bar::render_title_bar;
use crate::presentation::tui::styles::{BORDER_COLOR, COPY_COLOR, TOP_BAR_HEIGHT, content_style};
pub struct FormField<'a> {
pub label: &'a str,
pub value: &'a str,
pub focused: bool,
pub choices: Option<&'a [&'a str]>,
pub selected_choice: Option<usize>,
pub lines: usize,
pub highlighted_line: Option<usize>,
pub values: Option<&'a [String]>,
}
pub struct FormTemplate<'a> {
pub subtitle: &'a str,
pub fields: &'a [FormField<'a>],
pub help: &'a str,
pub mode: &'a str,
}
impl<'a> FormTemplate<'a> {
pub fn render(&self, f: &mut Frame) {
let mut constraints = vec![Constraint::Length(TOP_BAR_HEIGHT)];
for field in self.fields.iter() {
constraints.push(Constraint::Length(field.lines as u16 + 2));
}
constraints.push(Constraint::Min(1));
constraints.push(Constraint::Length(1));
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(f.area());
render_title_bar(f, chunks[0], self.subtitle);
for (i, field) in self.fields.iter().enumerate() {
let label_style = content_style().fg(BORDER_COLOR);
let mut block = Block::default()
.borders(Borders::ALL)
.border_style(content_style().fg(BORDER_COLOR))
.style(content_style());
if field.focused {
block = block.title(Span::styled(
field.label,
label_style.add_modifier(Modifier::BOLD),
));
block = block.border_style(
content_style()
.fg(BORDER_COLOR)
.add_modifier(Modifier::BOLD),
);
} else {
block = block.title(Span::styled(field.label, label_style));
}
let value_text_style = content_style().fg(BORDER_COLOR);
if let Some(choices) = field.choices {
let selected = field.selected_choice.unwrap_or(0);
let parts: Vec<String> = choices
.iter()
.enumerate()
.map(|(idx, c)| {
if idx == selected {
format!("(*) {}", c)
} else {
format!("( ) {}", c)
}
})
.collect();
let content = parts.join(" ");
let paragraph = Paragraph::new(Line::from(Span::styled(content, value_text_style)))
.block(block);
f.render_widget(paragraph, chunks[i + 1]);
} else {
if let Some(values) = field.values {
let selected_idx = field.highlighted_line.unwrap_or(0);
let count = std::cmp::max(1, field.lines);
let widget = schedule_time(count, values, selected_idx, field.focused);
f.render_widget(widget, chunks[i + 1]);
} else if field.lines > 1 {
let lines_vec: Vec<Line> = field
.value
.split('\n')
.enumerate()
.map(|(li, l)| {
let mut style = if field.focused {
content_style().add_modifier(Modifier::BOLD)
} else {
content_style().fg(COPY_COLOR)
};
if field.focused && field.highlighted_line == Some(li) {
style = style.fg(BORDER_COLOR).add_modifier(Modifier::REVERSED);
}
Line::from(Span::styled(l.to_string(), style))
})
.collect();
let paragraph = Paragraph::new(lines_vec).block(block);
f.render_widget(paragraph, chunks[i + 1]);
} else {
let content = field.value.to_string();
let paragraph =
Paragraph::new(Line::from(Span::styled(content, value_text_style)))
.block(block);
f.render_widget(paragraph, chunks[i + 1]);
}
}
}
let help_index = self.fields.len() + 2;
let combined = format!("{} | MODE: {}", self.help, self.mode);
f.render_widget(bottom_bar(&combined), chunks[help_index]);
}
}