use crate::ui;
use crossterm::cursor::Hide;
use crossterm::event::EnableMouseCapture;
use crossterm::terminal::{Clear, ClearType, EnterAlternateScreen, SetTitle};
use frittura_ssh_core::{SshWriterProxy, TerminalEvent};
use ratatui::layout::Rect;
use ratatui::prelude::CrosstermBackend;
use ratatui::text::Line;
use ratatui::{Terminal, TerminalOptions, Viewport};
use sshattrick_core::constants::UI_SCREEN_SIZE;
use sshattrick_core::{AppResult, Game, GameSide};
use tokio::sync::mpsc::Receiver;
#[derive(Debug)]
pub struct Tui {
username: String,
games_played: usize,
games_won: usize,
terminal: Terminal<CrosstermBackend<SshWriterProxy>>,
events: Receiver<TerminalEvent>,
}
impl Tui {
pub fn new(
username: String,
writer: SshWriterProxy,
events: Receiver<TerminalEvent>,
) -> AppResult<Self> {
let backend = CrosstermBackend::new(writer);
let opts = TerminalOptions {
viewport: Viewport::Fixed(Rect {
x: 0,
y: 0,
width: UI_SCREEN_SIZE.0,
height: UI_SCREEN_SIZE.1,
}),
};
let terminal = Terminal::with_options(backend, opts)?;
let mut tui = Self {
username,
games_played: 0,
games_won: 0,
terminal,
events,
};
tui.init()?;
Ok(tui)
}
fn init(&mut self) -> AppResult<()> {
crossterm::execute!(
self.terminal.backend_mut(),
EnterAlternateScreen,
EnableMouseCapture,
SetTitle("ssHattrick"),
Clear(ClearType::All),
Hide
)?;
Ok(())
}
pub fn username(&self) -> &str {
&self.username
}
pub fn record_game(&mut self, won: bool) {
self.games_played += 1;
if won {
self.games_won += 1;
}
}
pub async fn next(&mut self) -> TerminalEvent {
self.events.recv().await.unwrap_or(TerminalEvent::Quit)
}
pub fn try_next(&mut self) -> Option<TerminalEvent> {
match self.events.try_recv() {
Ok(event) => Some(event),
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => None,
Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => Some(TerminalEvent::Quit),
}
}
pub fn draw(
&mut self,
game: &Game,
image_lines: &[Line],
viewer: GameSide,
idle_warning: Option<u32>,
) -> AppResult<()> {
self.terminal
.draw(|frame| ui::render(frame, game, image_lines, viewer, idle_warning))?;
Ok(())
}
pub fn draw_lobby(
&mut self,
stats: &crate::lobby::LobbyStats,
view: crate::lobby::LobbyView,
kick_warning_secs: Option<u32>,
) -> AppResult<()> {
let Self {
username,
games_played,
games_won,
terminal,
..
} = self;
terminal.draw(|frame| {
ui::render_lobby(
frame,
username,
*games_played,
*games_won,
stats,
view,
kick_warning_secs,
)
})?;
Ok(())
}
pub async fn push_data(&mut self) -> AppResult<()> {
self.terminal.backend_mut().writer_mut().send().await?;
Ok(())
}
pub async fn close(mut self) {
self.terminal
.backend_mut()
.writer_mut()
.send_and_close()
.await;
}
}