use crate::config::TukaiConfig;
use crate::event_handler::{EventHandler, TukaiEvent};
use crate::screens::ActiveScreenEnum;
use crate::screens::repeat::RepeatScreen;
use crate::screens::{Screen, stats::StatsScreen, typing::TypingScreen};
use crate::storage::storage_handler::StorageHandler;
use std::{cell::RefCell, rc::Rc};
use ratatui::Terminal;
use ratatui::prelude::CrosstermBackend;
use ratatui::{
Frame,
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
layout::{Constraint, Layout},
};
use anyhow::Result;
type TukaiTerminal = Terminal<CrosstermBackend<std::io::Stdout>>;
pub struct Tukai<'a> {
pub config: Rc<RefCell<TukaiConfig>>,
event_handler: &'a mut EventHandler,
storage_handler: StorageHandler,
is_terminated: bool,
screen: Box<dyn Screen>,
}
impl<'a> Tukai<'a> {
pub fn try_new(event_handler: &'a mut EventHandler, mut config: TukaiConfig) -> Result<Self> {
let storage_handler = StorageHandler::new(config.get_file_path()).init()?;
config.typing_duration = storage_handler.get_typing_duration();
config.has_transparent_bg = storage_handler.get_has_transparent_bg();
{
let mut layout = config.get_layout_mut();
layout.active_layout_name(storage_handler.get_layout_name().clone());
}
{
let mut language = config.get_language_mut();
language.current_index(storage_handler.get_language_index());
}
let config = Rc::new(RefCell::new(config));
let typing_screen = TypingScreen::new(Rc::clone(&config));
Ok(Self {
config,
event_handler,
storage_handler,
is_terminated: false,
screen: Box::new(typing_screen),
})
}
pub async fn run(&mut self, terminal: &mut TukaiTerminal) -> Result<()> {
while !self.is_terminated {
match self.event_handler.next().await? {
TukaiEvent::Key(key_event) => self.handle_events(key_event),
TukaiEvent::Tick => {
if self.screen.is_running() {
self.screen.increment_time_secs();
}
}
};
if self.screen.get_remaining_time() == 0 {
self.screen.stop(&mut self.storage_handler);
}
terminal.draw(|frame| self.draw(frame))?;
}
Ok(())
}
fn draw(&mut self, frame: &mut Frame) {
let main_layout = Layout::default()
.constraints(vec![Constraint::Min(0), Constraint::Length(3)])
.split(frame.area());
self.screen.render(frame, main_layout[0]);
self.screen.render_instructions(frame, main_layout[1]);
if self.screen.is_popup_visible() {
self.screen.render_popup(frame);
}
}
fn reset(&mut self) {
self.screen.reset();
}
fn exit(&mut self) {
self.is_terminated = true;
self
.storage_handler
.flush()
.expect("Error occured while saving into the file");
}
fn switch_screen(&mut self, switch_to_screen: ActiveScreenEnum) {
self.screen = match switch_to_screen {
ActiveScreenEnum::Typing => Box::new(TypingScreen::new(self.config.clone())),
ActiveScreenEnum::Repeat => Box::new(RepeatScreen::new(self.config.clone())),
ActiveScreenEnum::Stats => Box::new(StatsScreen::new(self.config.clone())),
}
}
fn handle_events(&mut self, key_event: KeyEvent) {
if key_event.modifiers.contains(KeyModifiers::CONTROL) {
if self.screen.handle_control_events(key_event) {
return;
}
if let KeyCode::Char(c) = key_event.code {
match c {
'r' => self.reset(),
'l' => {
if let Some(next_screen) = self.screen.get_next_screen() {
self.switch_screen(next_screen);
}
}
'h' => {
if let Some(previous_screen) = self.screen.get_previous_screen() {
self.switch_screen(previous_screen);
}
}
'c' => self.exit(),
'd' => {
self
.storage_handler
.set_typing_duration(self.config.borrow_mut().switch_typing_duration());
self.reset();
}
't' => {
let new_state = self.config.borrow_mut().toggle_transparent_bg();
self.storage_handler.set_transparent_bg(new_state);
}
's' => {
let new_layout = self
.config
.borrow_mut()
.get_layout_mut()
.switch_to_next_layout();
self.storage_handler.set_layout(new_layout);
}
'p' => {
let new_language_index = self
.config
.borrow_mut()
.get_language_mut()
.switch_language();
self.storage_handler.set_language_index(new_language_index);
self.reset();
}
_ => {}
}
}
return;
}
if self.screen.handle_events(key_event) {
return;
}
if key_event.code == KeyCode::Esc {
self.exit();
} else if key_event.code == KeyCode::Left {
if let Some(previous_screen) = self.screen.get_previous_screen() {
self.switch_screen(previous_screen);
}
} else if key_event.code == KeyCode::Right {
if let Some(next_screen) = self.screen.get_next_screen() {
self.switch_screen(next_screen);
}
}
}
}