use std::{
fmt, sync::{Mutex, atomic::{AtomicUsize, Ordering}}, hash::{Hash, Hasher}
};
use ncursesw::{SCREEN, WINDOW, Orientation, shims::constants};
use crate::{
Screen, RipoffWindow, NCurseswWinError, HasHandle, ncurses::INITSCR_CALLED
};
pub(in crate) const MAX_RIPOFF_LINES: usize = 5;
lazy_static! {
static ref RIPOFFCOUNT: AtomicUsize = AtomicUsize::new(0);
static ref RIPOFFINITCOUNT: AtomicUsize = AtomicUsize::new(0);
static ref RIPOFFLINESCREENS: Mutex<Vec<Option<Screen>>> = Mutex::new(Vec::with_capacity(MAX_RIPOFF_LINES));
static ref RIPOFFLINES: Mutex<Vec<(RipoffWindow, i32)>> = Mutex::new(Vec::with_capacity(MAX_RIPOFF_LINES));
}
#[no_mangle]
extern fn ripoff_init(window: WINDOW, columns: i32) -> i32 {
let number = RIPOFFINITCOUNT.fetch_add(1, Ordering::SeqCst);
let screen = &RIPOFFLINESCREENS
.lock()
.unwrap_or_else(|_| panic!("ripoff_init() : &RIPOFFLINESCREENS.lock()[{}] failed!!!", number))[number];
RIPOFFLINES
.lock()
.unwrap_or_else(|_| panic!("ripoff_init() : RIPOFFLINES.lock() failed!!!"))
.insert(number, (RipoffWindow::_from(screen.as_ref().map(|screen| screen._handle()), window, false), columns));
constants::OK
}
pub struct RipoffLine {
screen: Option<SCREEN>,
orientation: Orientation,
number: usize
}
impl RipoffLine {
fn _from(screen: Option<SCREEN>, orientation: Orientation, number: usize) -> Self {
assert!(screen.map_or_else(|| true, |screen| !screen.is_null()), "RipoffLine::_from() : screen.is_null()");
Self { screen, orientation, number }
}
}
impl RipoffLine {
pub fn new(orientation: Orientation) -> result!(Self) {
let number = create_ripoff_line(None, orientation)?;
Ok(Self::_from(None, orientation, number))
}
pub fn new_sp(screen: &Screen, orientation: Orientation) -> result!(Self) {
let number = create_ripoff_line(Some(screen._handle()), orientation)?;
Ok(Self::_from(Some(screen._handle()), orientation, number))
}
pub fn screen(&self) -> Option<Screen> {
self.screen.map(|screen| Screen::_from(screen, false))
}
pub fn orientation(&self) -> Orientation {
self.orientation
}
pub fn number(&self) -> usize {
self.number
}
pub fn update<F: Fn(&RipoffWindow, u16) -> anyhow::Result<T>, T>(&self, func: F) -> anyhow::Result<T> {
if !INITSCR_CALLED.load(Ordering::SeqCst) {
return Err(NCurseswWinError::InitscrNotCalled.into())
}
let (ref ripoff_window, ripoff_columns) = &RIPOFFLINES
.lock()
.unwrap_or_else(|_| panic!("RipoffLine::update() : RIPOFFLINES.lock()[{}] failed!!!", self.number))[self.number];
func(ripoff_window, u16::try_from(*ripoff_columns)?)
}
}
unsafe impl Send for RipoffLine { } unsafe impl Sync for RipoffLine { }
impl PartialEq for RipoffLine {
fn eq(&self, rhs: &Self) -> bool {
self.number == rhs.number
}
}
impl Eq for RipoffLine { }
impl Hash for RipoffLine {
fn hash<H: Hasher>(&self, state: &mut H) {
self.number.hash(state);
}
}
impl AsRef<RipoffLine> for RipoffLine {
fn as_ref(&self) -> &Self {
self
}
}
impl Clone for RipoffLine {
fn clone(&self) -> Self {
Self::_from(self.screen.clone(), self.orientation.clone(), self.number.clone())
}
}
impl fmt::Debug for RipoffLine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RipoffLine {{ screen: {:?}, number: {} }}", self.screen, self.number)
}
}
fn create_ripoff_line(screen: Option<SCREEN>, orientation: Orientation) -> result!(usize) {
if INITSCR_CALLED.load(Ordering::SeqCst) {
return Err(NCurseswWinError::InitscrAlreadyCalled)
}
let number = RIPOFFCOUNT.fetch_add(1, Ordering::SeqCst);
if number >= MAX_RIPOFF_LINES {
return Err(NCurseswWinError::MaximumRipoffLines { number })
}
if let Err(source) = screen
.map_or_else(|| ncursesw::ripoffline(orientation, ripoff_init), |screen| ncursesw::ripoffline_sp(screen, orientation, ripoff_init))
{
return Err(NCurseswWinError::NCurseswError { source })
}
RIPOFFLINESCREENS
.lock()
.unwrap_or_else(|_| panic!("RipoffLine::new_sp() : RIPOFFLINESCREENS.lock() failed!!!"))
.insert(number, screen.map(|screen| Screen::_from(screen, false)));
Ok(number)
}