use std::io::{BufRead, Read};
use std::thread;
use std::time::Duration;
#[allow(unused_imports)]
use beep::beep as sound;
use dim::{dimensions::Frequency, si::Hertz};
#[allow(unused_imports)]
use log::{debug, info};
use num_enum::{FromPrimitive, IntoPrimitive};
use num_traits::{Num, ToPrimitive};
use termcolor::{Color, ColorSpec, WriteColor};
use crate::error::StarTrustError;
#[allow(unused_imports)]
use crate::{StResult, TheGame};
const ESC_KEY: u8 = 27;
const ENTER_KEY: u8 = 10; const MINUS_ENTER_KEY: u8 = ((-(ENTER_KEY as i32)) & 0xFF) as u8;
const NULL_C: u8 = b'\0';
const F1_KEY: u8 = (-59 & 0xFF) as u8;
const BKSPC_KEY: u8 = 8;
const SPC: u8 = 32;
const ASCHI: u8 = 126;
const CTL_BKSPC_KEY: u8 = 127;
pub fn delay(ms: usize) {
thread::sleep(Duration::from_millis(ms as u64));
}
fn getbyte<R: Read>(sin: &mut R) -> StResult<Option<u8>> {
sin.bytes().next().transpose().map_err(|e| {
let e: StarTrustError = e.into();
e
})
}
fn getch<R: Read>(sin: &mut R) -> StResult<Option<char>> {
getbyte(sin).map(|option_b| option_b.map(|b| b as char))
}
fn is_ansi() -> bool {
true
}
pub fn clrscr<W: WriteColor>(sout: &mut W) -> StResult<()> {
let clear_sequence = if is_ansi() { "\x1b[2J\x1b[H" } else { "\x0c" };
write!(sout, "{}", clear_sequence)?;
sout.flush().map_err(|e| e.into())
}
fn nosound() {
}
fn speaker<HZ, N>(_freq: HZ, dur: Duration)
where
HZ: Frequency + Into<Hertz<N>>,
N: Num + ToPrimitive,
{
thread::sleep(dur);
nosound();
}
#[allow(dead_code)]
fn clearkeyboard<R: BufRead>(_stdin: &mut R) -> StResult<()> {
loop {
let c = getch(_stdin)?;
if c.is_none() {
return Ok(());
}
}
}
#[allow(dead_code)]
pub fn fgetline<R: BufRead>(_stream: &mut R) -> Result<String, StarTrustError> {
let mut buff = String::new();
let j = _stream.read_line(&mut buff)?;
Ok(if j > 0 {
let i = buff.trim_end();
i.to_string()
} else {
buff
})
}
pub fn yesno<R: BufRead>(
sin: &mut R, ) -> Result<char, StarTrustError> {
loop {
if let Some(c) = getch(sin)? {
let c = c.to_uppercase().next().unwrap_or('\0');
if "YN".contains(c) {
return Ok(c);
}
} else {
debug!("EOF in yesno")
}
}
}
#[allow(dead_code)]
pub fn keytocont<R: BufRead, W: WriteColor>(sin: &mut R, sout: &mut W) -> StResult<()> {
write!(sout, "\nPRESS A KEY TO CONTINUE ... ")?;
sout.flush()?;
clearkeyboard(sin)?;
let _ = getch(sin)?;
clearkeyboard(sin)?;
writeln!(sout)?;
Ok(())
}
pub fn beep() {
speaker(Hertz::new(880.0f64), Duration::from_millis(80));
}
pub fn buzz() {
speaker(Hertz::new(50.0f64), Duration::from_millis(200));
}
fn charokay(cc: u8, mode: InputMode) -> bool {
match mode {
InputMode::Mode0 => (cc >= (b' ')) && (cc <= ASCHI),
InputMode::Mode1 => (cc >= b'A') && (cc <= b'Z') || (cc == b'*') || (cc == b' '),
InputMode::Mode2 => {
((cc >= b'0') && (cc <= b'9')) || (cc == b'.') || (cc == b',') || (cc == b'-')
}
InputMode::Mode3 => {
((cc >= b'A') && (cc <= b'Z')) || ((cc >= b'0') && (cc <= b'9')) || (cc == b' ')
}
InputMode::InvalidMode => false,
}
}
fn ctlbkspc<W: WriteColor>(sout: &mut W, bl: &mut i32) -> StResult<()> {
if *bl > 0 {
for _ in 0..(*bl) {
write!(sout, "{} {}", BKSPC_KEY as char, BKSPC_KEY as char)?;
sout.flush()?;
}
*bl = 0;
}
Ok(())
}
#[derive(Copy, Clone, Debug, IntoPrimitive, FromPrimitive, Eq, PartialEq)]
#[repr(i32)]
pub enum InputMode {
Mode0 = 0,
Mode1 = 1,
Mode2 = 2,
Mode3 = 3,
#[num_enum(default)]
InvalidMode = -1,
}
pub enum InputValue {
InputString(String),
Esc,
Blank,
}
impl InputValue {
#[allow(dead_code)]
fn as_integer(&self) -> i32 {
match self {
InputValue::InputString(_) => 0,
InputValue::Esc => -1,
InputValue::Blank => 1,
}
}
}
pub fn getinp<R: BufRead, W: WriteColor>(
sin: &mut R,
sout: &mut W,
_length: usize,
mode: InputMode,
) -> StResult<InputValue> {
let md: i32 = mode.into();
let mut cc = 0;
let mut buff: Vec<u8> = Vec::new();
while (cc != ENTER_KEY) && (cc != ESC_KEY) && (cc != MINUS_ENTER_KEY) {
debug!("cc = {} ({})", cc, cc as char);
if let Some(the_char) = getch(sin)? {
cc = the_char as u8;
} else {
continue;
}
if cc == NULL_C
{
if let Some(n_cc) = getch(sin)? {
let n_cc = (-(n_cc as i32) & 0xFF) as u8;
let mut l = buff.len() as i32;
if md == 2 {
match n_cc {
F1_KEY => {
if l > 0 {
ctlbkspc(sout, &mut l)?;
buff.clear()
}
cc = ENTER_KEY;
}
_ => buzz(),
}
} else {
buzz();
}
} else {
buzz();
}
} else if (cc < BKSPC_KEY)
|| ((cc > BKSPC_KEY) && (cc < ENTER_KEY))
|| ((cc > ENTER_KEY) && (cc < ESC_KEY))
|| ((cc > ESC_KEY) && (cc < SPC))
|| (cc > ASCHI)
{
beep();
} else {
match cc {
BKSPC_KEY => {
if !buff.is_empty() {
write!(sout, "{} {}", cc as char, cc as char)?;
sout.flush()?;
let _ = buff.pop();
} else {
buzz();
}
}
ESC_KEY => {
let mut l = buff.len() as i32;
if l > 0 {
ctlbkspc(sout, &mut l)?;
}
}
CTL_BKSPC_KEY => {
let mut l = buff.len() as i32;
if l > 0 {
ctlbkspc(sout, &mut l)?;
} else {
buzz();
}
}
ENTER_KEY => { }
_ => {
if buff.len() < _length {
cc = ((cc as char).to_uppercase().next().ok_or_else(|| {
StarTrustError::GeneralError(format!(
"Error converting {} to upper case",
cc
))
})?) as u8;
if charokay(cc, md.into()) {
buff.push(cc);
write!(sout, "{}", cc as char)?;
sout.flush()?;
} else {
beep();
}
} else {
beep();
}
}
}
}
}
Ok(if cc == ESC_KEY {
InputValue::Esc
} else if buff.is_empty() {
InputValue::Blank
} else {
InputValue::InputString(String::from_utf8(buff).unwrap())
})
}
pub fn getcourse<R: BufRead, W: WriteColor>(
sin: &mut R,
sout: &mut W,
) -> StResult<f64> {
write!(sout, "COURSE (1-8.99)? ")?;
sout.flush()?;
let gb = getinp(sin, sout, 4, 2.into());
writeln!(sout)?;
Ok(if let InputValue::InputString(ibuff) = gb? {
ibuff.parse().unwrap_or(0.0)
} else {
0.0
})
}
pub fn getwarp<R: BufRead, W: WriteColor>(sin: &mut R, sout: &mut W) -> StResult<f64> {
write!(sout, "WARP (0-12.0)? ")?;
sout.flush()?;
let gb = getinp(sin, sout, 4, 2.into())?;
writeln!(sout)?;
Ok(if let InputValue::InputString(ibuff) = gb {
ibuff.parse().unwrap_or(0.0)
} else {
0.0
})
}
pub fn draw_number_in_color<W: WriteColor>(
sout: &mut W,
e: i32,
color: Color,
bold: bool,
) -> StResult<()> {
let mut color_spec = ColorSpec::new();
sout.set_color(
color_spec
.set_fg(if e != 0 { Some(color) } else { None })
.set_bold(bold)
.set_intense(bold),
)?;
write!(sout, "{}", e)?;
sout.reset()?;
Ok(())
}