Documentation
use std::fmt::{
	self,
	Write
};
use std::io::Write as IoWrite;
use std::time::{
	Duration,
	SystemTime
};

use bincode::{
	config,
	error::DecodeError,
	error::EncodeError
};
use bincode::serde::{
	decode_from_slice,
	encode_into_std_write,
	encode_to_vec
};
use conciliator::Buffer;
use serde::{Serialize, Deserialize, de::DeserializeOwned};
use tokio::time::{
	timeout,
	Timeout
};

use crate::peer::NodeID;


pub mod tree {
	pub const ROOT: &str = "";
	pub const STEM: &str = "";
	pub const KNOT: &str = "├─";
	pub const TAIL: &str = "└─";
}

pub type Task = tokio::task::JoinHandle<()>;

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct PeerInfo {
	pub id: NodeID,
	pub name: String
}

impl PeerInfo {
	pub fn create(name: String) -> Self {
		Self {
			id: NodeID::random(),
			name
		}
	}
}

pub fn fmt_bytes(arr: &[u8]) -> String {
	let mut text = String::new();
	for b in arr {
		write!(text, "{b:02X}").expect("Formatting bytes failed!");
	}
	text
}

pub fn fmt_time(f: &mut fmt::Formatter<'_>, time: SystemTime) -> fmt::Result {
	match time.elapsed() {
		Ok(elapsed) => write!(f, "{elapsed:.2?} ago"),
		Err(future) => write!(f, "in {:.2?}", future.duration())
	}
}

pub fn push_time(buf: &mut Buffer, time: SystemTime) {
	match time.elapsed() {
		Ok(elapsed) => buf.push(format_args!("{elapsed:.2?} ago")),
		Err(future) => buf.push(format_args!("in {:.2?}", future.duration()))
	};
}

pub trait TimeoutFuture: Sized + futures::Future {
	fn timeout(self, duration: Duration) -> Timeout<Self> {
		timeout(duration, self)
	}
	fn timeout_ms(self, ms: u64) -> Timeout<Self> {
		timeout(Duration::from_millis(ms), self)
	}
}
impl<Fut: futures::Future> TimeoutFuture for Fut {}

#[doc(hidden)]
pub trait NeverFn {type Output;}
impl<T> NeverFn for fn() -> T {type Output = T;}
/// This is better than [`std::convert::Infallible`]
pub type Never = <fn()-> ! as NeverFn>::Output;


pub(crate) trait OptTimeExt {
	fn set_now(&mut self);
}

impl OptTimeExt for Option<SystemTime> {
	fn set_now(&mut self) {
		self.replace(SystemTime::now());
	}
}

pub fn bincode_serialize<T: Serialize>(thing: T)
	-> Result<Vec<u8>, EncodeError>
{
	encode_to_vec(thing, config::legacy())
}

pub fn bincode_serialize_into<T: Serialize, W: IoWrite>(dst: &mut W, thing: T)
	-> Result<usize, EncodeError>
{
	encode_into_std_write(thing, dst, config::legacy())
}

pub fn bincode_deserialize<T: DeserializeOwned>(buf: &[u8])
	-> Result<T, DecodeError>
{
	decode_from_slice(buf, config::legacy())
		.map(|(t, _len)| t)
}