use ratatui::prelude::*;
use crate::state::State;
use crate::storage::DbConnection;
use crate::tui::action::NUM_TABS;
use crate::tui::views::MessageType;
use crate::tui::views::pack_reveal::PackRevealView;
use super::action::{Action, Route};
use super::views::games::GamesView;
use super::views::packs::PacksView;
use super::views::party::PartyView;
use super::views::store::StoreView;
use super::views::{View, ViewResult};
use super::widgets::{render_footer, render_header};
pub struct App<'a> {
route: Route,
message: Option<(MessageType, String)>,
tick: u32,
store: StoreView,
party: PartyView,
packs: PacksView,
pack_reveal: PackRevealView,
games: GamesView,
state: &'a mut State,
conn: &'a DbConnection,
display_points_offset: u64,
}
impl<'a> App<'a> {
pub fn new(state: &'a mut State, conn: &'a DbConnection) -> Self {
Self {
route: Route::default(),
message: None,
state,
tick: 0,
store: StoreView::default(),
party: PartyView::default(),
packs: PacksView::default(),
pack_reveal: PackRevealView::default(),
games: GamesView,
conn,
display_points_offset: 0,
}
}
pub fn tick(&mut self) {
self.tick = self.tick.wrapping_add(1);
}
pub fn save(&self) {
let _ = self.state.save(self.conn);
}
pub fn handle(&mut self, action: Action) -> bool {
self.message = None;
let is_tab_nav = matches!(action, Action::Tab(_) | Action::NextTab | Action::PrevTab);
if is_tab_nav {
let tab_idx = match action {
Action::Tab(n) => n,
Action::NextTab => (self.route.tab_index() + 1) % NUM_TABS,
Action::PrevTab => (NUM_TABS + self.route.tab_index() - 1) % NUM_TABS,
_ => unreachable!(),
};
self.route = match tab_idx {
0 => Route::Store(Default::default()),
1 => Route::Party,
2 => Route::Packs,
_ => Route::Games,
};
return true;
}
if matches!(action, Action::Quit) {
return false;
}
let result = match &mut self.route {
Route::Store(_) => self.store.handle(action, self.state),
Route::Party => self.party.handle(action, self.state),
Route::Packs => self.packs.handle(action, self.state),
Route::PackReveal => self.pack_reveal.handle(action, self.state),
Route::Games => self.games.handle(action, self.state),
};
match result {
ViewResult::Redraw => {}
ViewResult::Navigate(route) => {
self.display_points_offset = 0;
if let Route::Store(sub_route) = route {
self.store.set_route(sub_route);
}
self.route = route;
}
ViewResult::OpenPack(pack) => {
let points_before = self.state.party_points;
let pack_items = self.state.open_pack(pack);
self.save();
let offset = self.state.party_points - points_before;
self.display_points_offset = offset;
self.pack_reveal.set_items(pack_items);
self.route = Route::PackReveal;
}
ViewResult::RevealPoints(points) => {
self.display_points_offset = self.display_points_offset.saturating_sub(points);
}
ViewResult::Message(ty, msg) => {
self.message = Some((ty, msg));
self.save();
}
ViewResult::None => {}
}
true
}
pub fn render(&self, frame: &mut Frame) {
let area = frame.area();
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(2), Constraint::Min(10), Constraint::Length(1), ])
.split(area);
render_header(
frame,
chunks[0].inner(Margin::new(1, 0)),
&self.route,
self.state,
);
match &self.route {
Route::Store(_) => self.store.render(frame, chunks[1], self.state, self.tick),
Route::Party => self.party.render(frame, chunks[1], self.state, self.tick),
Route::Packs => self.packs.render(frame, chunks[1], self.state, self.tick),
Route::PackReveal => self
.pack_reveal
.render(frame, chunks[1], self.state, self.tick),
Route::Games => self.games.render(frame, chunks[1], self.state, self.tick),
}
let hints = match &self.route {
Route::Store(_) => self.store.key_hints(),
Route::Party => self.party.key_hints(),
Route::Packs => self.packs.key_hints(),
Route::PackReveal => self.pack_reveal.key_hints(),
Route::Games => self.games.key_hints(),
};
render_footer(
frame,
chunks[2],
&hints,
self.state.party_points - self.display_points_offset,
&self.message,
);
}
}