use crate::{ sm2::Rating, records::Record };
use tui::widgets::{Block,Borders,Gauge};
use tui::style::{Color,Style};
pub struct Card {
record: Record,
is_mastered: bool,
failed: bool,
}
impl Card {
fn from(record: Record) -> Self {
Self {
record,
is_mastered: false,
failed: false,
}
}
fn into_inner(self) -> Record {
self.record
}
fn mastered(&mut self) {
self.is_mastered = true;
}
pub fn review(&mut self,rating: Rating) {
if let Rating::Again = rating {
self.failed = true
}
else if self.failed {
self.record.review(Rating::Again);
self.is_mastered = true;
}
else {
self.record.review(rating);
self.is_mastered = true;
}
}
}
pub struct Cards {
cards: Vec<Card>,
position: usize,
selected: usize,
}
impl Cards {
fn cards_left(&self) -> Option<usize> {
let cards = self.cards.iter().filter(|c| !c.is_mastered).count();
if cards != 0 { Some(cards) }
else { None }
}
pub fn from(records: Vec<Record>) -> Self {
let records = sort_records(records);
let cards = records.into_iter()
.map(Card::from)
.collect::<Vec<Card>>();
Self { cards, position: 0, selected: 0 }
}
pub fn select(mut self,n: usize) -> Self {
self.cards.iter_mut().enumerate()
.filter(|(i,_)| *i >= n )
.for_each(|(_,r)| r.mastered());
if n <= self.cards.len() { self.selected = n }
else { self.selected = self.cards.len() }
self
}
pub fn records(self) -> Vec<Record> {
self.cards.into_iter()
.map(|c| c.into_inner())
.collect()
}
pub fn current_record(&mut self) -> &Record {
&mut self.cards[self.position].record
}
pub fn review_current_card(&mut self,rating: Rating) {
let card = &mut self.cards[self.position];
card.review(rating.clone());
if let Rating::Again = rating {
let hack = self.cards.remove(self.position);
self.cards.push(hack);
}
}
pub fn status(&self) -> Gauge {
let cards = self.cards_left().unwrap();
let label = cards.to_string();
let percent = cards * 100;
let percent = percent / self.selected;
let block = Block::default()
.borders(Borders::ALL)
.title("Cards Left");
let style = Style::default()
.fg(Color::Blue)
.bg(Color::Black);
Gauge::default()
.block(block)
.gauge_style(style)
.percent(percent as u16)
.label(label)
}
}
impl Iterator for Cards {
type Item = ();
fn next(&mut self) -> Option<Self::Item> {
self.cards_left()?;
let cards = self.cards.iter()
.enumerate()
.cycle();
let mut pos = self.position;
let last = self.cards.len() - 1;
for (i,card) in cards {
if i >= pos && !card.is_mastered { pos = i; break; }
if i == last { pos = 0 }
};
self.position = pos;
Some(())
}
}
fn sort_records(records: Vec<Record>) -> Vec<Record> {
let mut records = records;
let mut new = Vec::new();
let mut old = Vec::new();
for record in records.drain(..) {
if record.last_date.is_none() { new.push(record) }
else { old.push(record) }
};
old.sort_by_key(|r| r.percent_overdue().unwrap());
old.reverse();
new.append(&mut old);
new
}