1use crate::lang::Language;
2use crate::stats::Timer;
3use crate::terminalui::{Input, Terminal};
4use itertools::peek_nth;
5use std::io::{stdin, stdout, Write};
6use termion::{cursor, event::Key, input::TermRead, raw::IntoRawMode};
7
8#[derive(Clone)]
9pub enum Mode {
10 Words(String, usize),
11}
12
13impl Default for Mode {
14 fn default() -> Self {
15 Self::Words("english".to_string(), 10)
16 }
17}
18
19fn initialize_lines(words: &Vec<String>, words_per_line: u16) -> Vec<String> {
20 let mut resu = vec![String::new()];
21 let mut w = words.clone();
22 let count = words.len() / words_per_line as usize + 1;
23 for _ in 0..count {
24 let mut line = vec![];
25 for _ in 0..words_per_line {
26 if let Some(word) = w.pop() {
27 line.push(word);
28 }
29 }
30 resu.push(line.join(" "));
31 }
32 resu.remove(0);
33 if words.len() % words_per_line as usize == 0 {
34 resu.remove(count - 1);
35 }
36 resu
37}
38
39pub struct FerrisType {
40 mode: Mode,
41 lines: Vec<String>,
42 timer: Timer,
43 terminal: Terminal,
44 line_peek: u16,
45 words_per_line: u16,
46}
47
48impl FerrisType {
49 pub fn new(mode: Mode) -> Self {
50 let words = match &mode {
51 Mode::Words(lang, num) => {
52 let language = Language::new(lang.to_string());
53 language.get_random(num.to_owned())
54 }
55 };
56 let (w, _) = termion::terminal_size().unwrap();
57 let lines = initialize_lines(&words, w / 3 / 5);
58 Self {
59 mode,
60 lines,
61 timer: Timer::default(),
62 terminal: Terminal::new(stdout().into_raw_mode().unwrap()),
63 line_peek: 3,
64 words_per_line: w / 3 / 5,
65 }
66 }
67
68 pub fn words(lang: String, n: usize) -> Self {
69 Self::new(Mode::Words(lang, n))
70 }
71
72 pub fn line_peek(mut self, n: u16) -> Self {
73 self.line_peek = n;
74 self
75 }
76 pub fn words_per_line(mut self, n: u16) -> Self {
77 self.words_per_line = n;
78 self
79 }
80
81 pub fn play(mut self) -> std::io::Result<()> {
82 let mut stdin = stdin().keys();
83 let mut on_last_word = false;
84 self.terminal.initialize_screen()?;
85 let l = self.lines.clone();
86 let line_peek = self.line_peek;
87 let mut lines = peek_nth(l.iter());
88 let mut lastline = None;
89 'lines: while let Some(current_line) = lines.next() {
90 let mut input = Input::new(current_line);
91 self.terminal.draw_current_line(current_line)?;
92 self.terminal.draw_prev_line(lastline)?;
93 self.terminal.clear_current_line()?;
94 write!(self.terminal.stdout, "{}", input)?;
95 for i in 0..line_peek {
96 self.terminal.clear_next_line(i)?;
97 let next_line = lines.peek_nth(i.into());
98 self.terminal.draw_future_line(next_line, i)?;
99 if i == 0 {
100 on_last_word = true;
101 }
102 }
103 lastline = Some(current_line);
104 loop {
105 write!(
106 self.terminal.stdout,
107 "{}",
108 cursor::Goto(self.terminal.type_width, self.terminal.type_height)
109 )?;
110 self.terminal.stdout.flush()?;
111 let c = stdin.next().unwrap()?;
112 self.timer.record_key(c);
113 match c {
114 Key::Char('\t') => return FerrisType::new(self.mode.clone()).play(),
115 Key::Char('\n') => {
116 break;
117 }
118 Key::Char(' ') if input.is_complete() => break,
119 Key::Char(c) => {
120 let correct = input.update(c);
121 if correct && on_last_word && input.is_complete() {
122 break;
123 }
124 }
125 Key::Backspace => {
126 self.terminal.clear_current_line()?;
127 input.delete_one();
128 }
129 Key::Ctrl('c') => {
130 break 'lines;
131 }
132 _ => {}
133 }
134 write!(self.terminal.stdout, "{}", input)?;
135 self.terminal.stdout.flush()?;
136 }
137 self.terminal.clear_current_line()?;
138 }
139 self.terminal.finalize(self.timer, self.lines)?;
140 Ok(())
141 }
142}