use crate::serial::SerialPort;
use crate::state::AppState;
use crate::state::ansi::{
CLEAR_SCREEN, COLOR_DIVIDER, COLOR_INFO, COLOR_PROMPT, COLOR_RESET, COLOR_SYSTEM,
CURSOR_DIVIDER_LINE, CURSOR_HOME, CURSOR_INPUT_LINE, CURSOR_SCROLL_BOTTOM, ERASE_LINE,
RESTORE_CURSOR, SAVE_CURSOR, SCROLL_REGION_22,
};
use crate::types::Message;
use crate::utils::write_u32;
pub fn write_str(port: &mut impl SerialPort, string: &str) {
for &byte in string.as_bytes() {
port.write_byte(byte);
}
}
pub fn init_viewport(port: &mut impl SerialPort) {
write_str(port, CLEAR_SCREEN);
write_str(port, CURSOR_HOME);
write_str(port, SCROLL_REGION_22);
write_str(port, CURSOR_DIVIDER_LINE);
write_str(port, COLOR_DIVIDER);
for _ in 0..80 {
port.write_byte(b'-');
}
write_str(port, COLOR_RESET);
write_str(port, CURSOR_INPUT_LINE);
}
pub fn render_prompt(state: &AppState, port: &mut impl SerialPort) {
write_str(port, CURSOR_INPUT_LINE);
write_str(port, ERASE_LINE);
write_str(port, COLOR_PROMPT);
if let Some(user_id) = state.current_user_id {
let mut found = false;
for user in &state.users {
if user.id != user_id || user.status != 0 {
continue;
}
let Ok(name) = user.name.as_str() else {
continue;
};
if name.is_empty() {
continue;
}
write_str(port, name);
found = true;
break;
}
if !found {
write_str(port, "User");
write_u32(port, u32::from(user_id));
}
} else {
write_str(port, "Guest");
}
write_str(port, "> ");
write_str(port, COLOR_RESET);
let Ok(input) = state.input_buffer.as_str() else {
return;
};
write_str(port, input);
}
pub fn render_message(state: &AppState, message: &Message, port: &mut impl SerialPort) {
write_str(port, SAVE_CURSOR);
write_str(port, CURSOR_SCROLL_BOTTOM);
write_str(port, "[");
write_u32(port, message.tick);
write_str(port, "] <");
let mut found = false;
for user in &state.users {
if user.id != message.user_id || user.status != 0 {
continue;
}
let Ok(name) = user.name.as_str() else {
continue;
};
if name.is_empty() {
continue;
}
write_str(port, name);
found = true;
break;
}
if !found {
write_str(port, "User");
write_u32(port, u32::from(message.user_id));
}
write_str(port, "> ");
if let Ok(content) = message.content.as_str() {
write_str(port, content);
}
write_str(port, "\r\n");
write_str(port, RESTORE_CURSOR);
}
pub fn render_system_message(port: &mut impl SerialPort, tick: u32, msg: &str) {
write_str(port, SAVE_CURSOR);
write_str(port, CURSOR_SCROLL_BOTTOM);
write_str(port, COLOR_SYSTEM);
write_str(port, "[");
write_u32(port, tick);
write_str(port, "] <System> ");
write_str(port, msg);
write_str(port, COLOR_RESET);
write_str(port, "\r\n");
write_str(port, RESTORE_CURSOR);
}
pub fn render_empty_line(port: &mut impl SerialPort) {
write_str(port, SAVE_CURSOR);
write_str(port, CURSOR_SCROLL_BOTTOM);
write_str(port, "\r\n");
write_str(port, RESTORE_CURSOR);
}
pub fn render_welcome(port: &mut impl SerialPort) {
render_system_message(
port,
0,
"==================================================",
);
render_system_message(port, 0, " CLI Chat Embedded");
render_system_message(port, 0, " Zero-heap, bare-metal compatible chat core.");
render_system_message(port, 0, " Type /help to see available commands.");
render_system_message(
port,
0,
"==================================================",
);
}
pub fn render_active_users(state: &AppState, port: &mut impl SerialPort) {
render_system_message(port, state.relative_tick, "--- Active Users ---");
let mut count = 0;
for user in &state.users {
if user.status != 0 {
continue;
}
count += 1;
write_str(port, SAVE_CURSOR);
write_str(port, CURSOR_SCROLL_BOTTOM);
write_str(port, COLOR_INFO);
write_str(port, "[");
write_u32(port, state.relative_tick);
write_str(port, "] <Info> ID: ");
write_u32(port, u32::from(user.id));
write_str(port, " | Name: ");
let Ok(name) = user.name.as_str() else {
write_str(port, "Unnamed");
write_str(port, COLOR_RESET);
write_str(port, "\r\n");
write_str(port, RESTORE_CURSOR);
continue;
};
if name.is_empty() {
write_str(port, "Unnamed");
} else {
write_str(port, name);
}
write_str(port, COLOR_RESET);
write_str(port, "\r\n");
write_str(port, RESTORE_CURSOR);
}
if count == 0 {
render_system_message(port, state.relative_tick, "No users registered.");
}
}
#[allow(clippy::cast_possible_truncation)]
pub fn render_status(state: &AppState, port: &mut impl SerialPort) {
render_system_message(port, state.relative_tick, "--- System Status ---");
write_str(port, SAVE_CURSOR);
write_str(port, CURSOR_SCROLL_BOTTOM);
write_str(port, COLOR_INFO);
write_str(port, "[");
write_u32(port, state.relative_tick);
write_str(port, "] <Info> Uptime Ticks: ");
write_u32(port, state.relative_tick);
write_str(port, COLOR_RESET);
write_str(port, "\r\n");
write_str(port, RESTORE_CURSOR);
write_str(port, SAVE_CURSOR);
write_str(port, CURSOR_SCROLL_BOTTOM);
write_str(port, COLOR_INFO);
write_str(port, "[");
write_u32(port, state.relative_tick);
write_str(port, "] <Info> Messages in History: ");
write_u32(port, state.history.len() as u32);
write_str(port, " / ");
write_u32(port, crate::types::MAX_HISTORY as u32);
write_str(port, COLOR_RESET);
write_str(port, "\r\n");
write_str(port, RESTORE_CURSOR);
}
pub fn render_time_info(state: &AppState, port: &mut impl SerialPort) {
render_system_message(port, state.relative_tick, "--- Time Info ---");
write_str(port, SAVE_CURSOR);
write_str(port, CURSOR_SCROLL_BOTTOM);
write_str(port, COLOR_INFO);
write_str(port, "[");
write_u32(port, state.relative_tick);
write_str(port, "] <Info> Boot Epoch: ");
write_u32(port, state.boot_epoch);
write_str(port, COLOR_RESET);
write_str(port, "\r\n");
write_str(port, RESTORE_CURSOR);
}
pub fn render_about(state: &AppState, port: &mut impl SerialPort) {
render_system_message(port, state.relative_tick, "CLI Chat Embedded");
render_system_message(port, state.relative_tick, "Author: Dzulkifli Anwar");
render_system_message(port, state.relative_tick, "License: MIT");
}