#![doc(html_root_url = "https://docs.rs/termine/3.3.0")]
use std::error::Error;
use std::time;
use std::sync::mpsc;
use termion::event::{Event, Key, MouseEvent};
use termion::{color, color::Rgb};
use termioff::Termioff;
use minefield::MineField;
use mvc_rs::{TPacket, TView};
pub struct Term<T> {
pub colors: Vec<T>,
pub tm: Termioff
}
impl<T: color::Color + Clone> TView<T> for Term<T> {
fn wr(&mut self, p: impl TPacket) -> Result<(), Box<dyn Error>> {
let v = p.to_vec();
let (x, y, st, bgc, fgc) = (v[0], v[1], v[2], v[3], v[4]);
let msg = &p.as_str().to_string();
self.tm.wr(x + 1, y + 1, st, self.col(bgc), self.col(fgc), msg)?;
Ok(())
}
fn reg(&mut self, c: Vec<T>) -> () {
self.colors = c;
}
fn col(&self, n: u16) -> T {
self.colors[n as usize].clone()
}
}
impl<T: color::Color + Clone> Term<T> {
pub fn new(k: u16) -> Result<Self, Box<dyn Error>> {
Ok(Term{colors: vec![], tm: Termioff::new(k)?})
}
}
pub struct Termine {
pub m: MineField,
pub v: Term<Rgb>,
pub t: time::Instant
}
impl Drop for Termine {
fn drop(&mut self) {
self.v.tm.fin().expect("fin");
}
}
impl Termine {
pub fn new(m: MineField) -> Result<Self, Box<dyn Error>> {
let mut s = Termine{m, v: Term::new(2)?, t: time::Instant::now()};
let colors = [ [96, 240, 32, 0], [32, 96, 240, 0], [32, 96, 240, 0], [240, 192, 32, 0], [240, 32, 96, 0], [240, 192, 32, 0] ].into_iter().map(|c| Rgb(c[0], c[1], c[2])).collect::<Vec<_>>();
s.v.reg(colors);
s.v.tm.begin()?;
s.m.reset_tick(&mut s.v)?;
Ok(s)
}
pub fn status_t(&mut self, h: u16, st: u16,
bgc: impl color::Color, fgc: impl color::Color) ->
Result<(), Box<dyn Error>> {
self.v.tm.wr(1, self.v.tm.h - h + 1, st, bgc, fgc,
&self.msg(self.v.tm.w, self.v.tm.h))?;
Ok(())
}
pub fn status_p(&mut self, h: u16, st: u16,
bgc: impl color::Color, fgc: impl color::Color, x: u16, y: u16) ->
Result<(), Box<dyn Error>> {
self.v.tm.wr(1, self.v.tm.h - h + 1, st, bgc, fgc,
&self.msg(x, y))?;
Ok(())
}
pub fn status_m(&mut self, h: u16, st: u16,
bgc: impl color::Color, fgc: impl color::Color) ->
Result<(), Box<dyn Error>> {
self.v.tm.wr(1, self.v.tm.h - h + 1, st, bgc, fgc,
&self.msg(self.m.m, self.m.s & 0x3fff))?;
Ok(())
}
pub fn msg(&self, x: u16, y: u16) -> String {
format!("({}, {}) {:?}", x, y, self.t.elapsed())
}
pub fn key(&mut self, k: Key) -> bool {
let mut f = true;
match k {
Key::Left | Key::Char('h') => { self.m.left(); },
Key::Down | Key::Char('j') => { self.m.down(); },
Key::Up | Key::Char('k') => { self.m.up(); },
Key::Right | Key::Char('l') => { self.m.right(); },
Key::Char(' ') => { self.m.click(); },
_ => { f = false; }
}
f
}
pub fn proc(&mut self, rx: &mpsc::Receiver<Result<Event, std::io::Error>>) ->
Result<bool, Box<dyn Error>> {
match rx.recv_timeout(self.m.ms) {
Err(mpsc::RecvTimeoutError::Disconnected) => Err("Disconnected".into()),
Err(mpsc::RecvTimeoutError::Timeout) => { self.status_m(3, 1, Rgb(192, 192, 192), Rgb(8, 8, 8))?;
self.m.tick(&mut self.v)?;
Ok(true)
},
Ok(ev) => {
Ok(match ev {
Ok(Event::Key(k)) => {
let f = match k {
Key::Ctrl('c') | Key::Char('q') => false,
Key::Esc | Key::Char('\x1b') => false,
_ => true
};
if !f { return Ok(false); }
if self.key(k) { self.m.reset_tick(&mut self.v)?; }
if self.m.is_end() { self.m.ending(&mut self.v)?; return Ok(false); }
true
},
Ok(Event::Mouse(m)) => {
match m {
MouseEvent::Press(_btn, x, y) => {
self.status_p(4, 1, color::Cyan, color::Green, x, y)?;
if self.m.click() { self.m.reset_tick(&mut self.v)?; }
if self.m.is_end() { self.m.ending(&mut self.v)?; return Ok(false); }
true
},
_ => true
}
},
_ => true
})
}
}
}
pub fn mainloop(&mut self) -> Result<(), Box<dyn Error>> {
let (_tx, rx) = self.v.tm.prepare_thread()?;
loop { if !self.proc(&rx)? { break; } }
Ok(())
}
}
pub fn main() -> Result<(), Box<dyn Error>> {
let m = MineField::new(16, 8, 12);
let mut g = Termine::new(m)?;
g.status_t(1, 3, color::Magenta, Rgb(240, 192, 32))?;
g.mainloop()?;
g.status_m(3, 1, Rgb(240, 192, 32), Rgb(192, 32, 240))?;
g.status_t(2, 3, Rgb(255, 0, 0), Rgb(255, 255, 0))?;
Ok(())
}
#[cfg(test)]
mod tests {
#[test]
fn test_a() {
assert_eq!(true, true);
}
}