use std::collections::VecDeque;
use std::path::PathBuf;
use std::time::Instant;
use badterm_core::script::Script;
use badterm_core::session::Session;
use badterm_core::transfer::Direction;
use super::Redial;
use crate::ui::login::LoginPrompt;
use crate::ui::save::SavePrompt;
use crate::ui::transfer::{DownloadPrompt, UploadPrompt};
pub(super) const MAX_TABS: usize = 9;
pub(super) const CHIP_W: usize = 3;
pub(super) struct Tab {
pub(super) session: Session,
pub(super) id: u64,
pub(super) index: Option<usize>,
pub(super) title: String,
pub(super) login: Option<LoginPrompt>,
pub(super) remember: Option<String>,
pub(super) transfer_direction: Option<Direction>,
pub(super) upload_prompt: Option<UploadPrompt>,
pub(super) download_prompt: Option<DownloadPrompt>,
pub(super) save_prompt: Option<SavePrompt>,
pub(super) upload_dir: PathBuf,
pub(super) macro_steps: VecDeque<crate::macros::Step>,
pub(super) macro_at: Instant,
pub(super) redials: u32,
pub(super) redial: Option<Redial>,
pub(super) scroll: usize,
pub(super) script: Option<Script>,
pub(super) unseen: bool,
pub(super) attention: bool,
}
impl Tab {
pub(super) fn new(session: Session, id: u64, index: Option<usize>) -> Tab {
let title = format!("BadTerm \u{2014} {}", session.entry.name);
Tab {
session,
id,
index,
title,
login: None,
remember: None,
transfer_direction: None,
upload_prompt: None,
download_prompt: None,
save_prompt: None,
upload_dir: PathBuf::new(),
macro_steps: VecDeque::new(),
macro_at: Instant::now(),
redials: 0,
redial: None,
scroll: 0,
script: None,
unseen: false,
attention: false,
}
}
pub(super) fn mark(&self) -> char {
mark(self.unseen, self.attention)
}
}
fn mark(unseen: bool, attention: bool) -> char {
if attention {
'!'
} else if unseen {
'*'
} else {
' '
}
}
pub(super) fn after_close(active: usize, closed: usize, len: usize) -> usize {
let next = if closed < active { active - 1 } else { active };
next.min(len.saturating_sub(2))
}
pub(super) fn cycle(active: usize, len: usize, back: bool) -> usize {
if len == 0 {
return 0;
}
if back { (active + len - 1) % len } else { (active + 1) % len }
}
pub(super) fn chips(marks: &[char]) -> Vec<String> {
marks.iter().enumerate().map(|(i, m)| format!(" {}{m}", i + 1)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn closing_a_tab_shows_its_neighbour() {
assert_eq!(after_close(1, 1, 3), 1);
assert_eq!(after_close(2, 2, 3), 1);
assert_eq!(after_close(0, 0, 2), 0);
assert_eq!(after_close(2, 0, 3), 1);
assert_eq!(after_close(0, 2, 3), 0);
assert_eq!(after_close(0, 0, 1), 0);
}
#[test]
fn switching_goes_round() {
assert_eq!(cycle(0, 3, false), 1);
assert_eq!(cycle(2, 3, false), 0);
assert_eq!(cycle(0, 3, true), 2);
assert_eq!(cycle(1, 3, true), 0);
assert_eq!(cycle(0, 1, false), 0);
assert_eq!(cycle(0, 0, false), 0);
}
#[test]
fn chips_say_what_is_waiting() {
let marks = [mark(false, false), mark(true, false), mark(true, true)];
assert_eq!(chips(&marks), [" 1 ", " 2*", " 3!"]);
let all = chips(&[' '; MAX_TABS]);
assert!(all.iter().all(|c| c.chars().count() == CHIP_W));
assert!(all.concat().chars().count() < 28);
}
}