use std::time::Duration;
use async_trait::async_trait;
use either::Either;
use eyre::Result;
use makeup_console::Keypress;
use tokio::sync::mpsc::UnboundedSender;
use crate::post_office::PostOffice;
use crate::{Coordinates, Dimensions, DrawCommand};
pub type Key = u64;
pub type DrawCommandBatch = (Key, Vec<DrawCommand>);
pub type RawComponentMessage<M> = Either<M, MakeupMessage>;
pub type ExtractMessageFromComponent<C> = <C as Component>::Message;
pub type ComponentMessage<C> = RawComponentMessage<ExtractMessageFromComponent<C>>;
pub type Mailbox<C> = Vec<ComponentMessage<C>>;
pub type ContextTx<M> = UnboundedSender<(Key, RawComponentMessage<M>)>;
pub type MakeupUpdate<'a, C> = UpdateContext<'a, ExtractMessageFromComponent<C>>;
#[derive(Debug)]
pub struct UpdateContext<'a, M: std::fmt::Debug + Send + Sync + Clone + 'static> {
pub post_office: &'a mut PostOffice<M>,
pub sender: MessageSender<M>,
pub focus: Key,
pub dimensions: Dimensions,
}
impl<'a, M: std::fmt::Debug + Send + Sync + Clone + 'static> UpdateContext<'a, M> {
pub fn new(
post_office: &'a mut PostOffice<M>,
sender: ContextTx<M>,
focus: Key,
dimensions: Dimensions,
) -> Self {
Self {
post_office,
sender: MessageSender::new(sender, focus),
focus,
dimensions,
}
}
pub fn sender(&self) -> MessageSender<M> {
self.sender.clone()
}
}
#[derive(Debug, Clone)]
pub struct MessageSender<M: std::fmt::Debug + Send + Sync + Clone + 'static> {
focus: Key,
tx: ContextTx<M>,
}
impl<M: std::fmt::Debug + Send + Sync + Clone + 'static> MessageSender<M> {
pub fn new(tx: ContextTx<M>, focus: Key) -> Self {
Self { tx, focus }
}
pub fn send_message(&self, key: Key, msg: M) -> Result<()> {
let sender = self.tx.clone();
tokio::spawn(async move {
sender.send((key, Either::Left(msg))).unwrap();
});
Ok(())
}
pub fn send_makeup_message(&self, key: Key, msg: MakeupMessage) -> Result<()> {
let sender = self.tx.clone();
tokio::spawn(async move {
sender.send((key, Either::Right(msg))).unwrap();
});
Ok(())
}
pub fn send_message_after(&self, key: Key, msg: M, duration: Duration) -> Result<()> {
let sender = self.tx.clone();
tokio::spawn(async move {
tokio::time::sleep(duration).await;
sender.send((key, Either::Left(msg))).unwrap();
});
Ok(())
}
pub fn send_makeup_message_after(
&self,
key: Key,
msg: MakeupMessage,
duration: Duration,
) -> Result<()> {
let sender = self.tx.clone();
tokio::spawn(async move {
tokio::time::sleep(duration).await;
sender.send((key, Either::Right(msg))).unwrap();
});
Ok(())
}
pub fn send_message_to_focused(&self, msg: M) -> Result<()> {
self.send_message(self.focus, msg)
}
pub fn send_makeup_message_to_focused(&self, msg: MakeupMessage) -> Result<()> {
self.send_makeup_message(self.focus, msg)
}
pub fn send_message_to_focused_after(&self, msg: M, duration: Duration) -> Result<()> {
self.send_message_after(self.focus, msg, duration)
}
pub fn send_makeup_message_to_focused_after(
&self,
msg: MakeupMessage,
duration: Duration,
) -> Result<()> {
self.send_makeup_message_after(self.focus, msg, duration)
}
}
#[derive(Debug, Clone)]
pub struct RenderContext {
pub last_frame_time: Option<Duration>,
pub frame_counter: u128,
pub fps: f64,
pub effective_fps: f64,
pub cursor: Coordinates,
pub dimensions: Dimensions,
pub focus: Key,
}
#[derive(Debug, Clone)]
pub enum MakeupMessage {
TimerTick(Duration),
TextUpdate(String),
Keypress(Keypress),
}
#[async_trait]
pub trait Component: std::fmt::Debug + Send + Sync {
type Message: std::fmt::Debug + Send + Sync + Clone;
#[allow(clippy::borrowed_box)]
fn children(&self) -> Option<Vec<&Box<dyn Component<Message = Self::Message>>>>;
fn children_mut(&mut self) -> Option<Vec<&mut Box<dyn Component<Message = Self::Message>>>>;
async fn update(&mut self, ctx: &mut MakeupUpdate<Self>) -> Result<()>;
async fn render(&self, ctx: &RenderContext) -> Result<DrawCommandBatch>;
fn key(&self) -> Key;
fn batch(&self, commands: Vec<DrawCommand>) -> Result<DrawCommandBatch> {
Ok((self.key(), commands))
}
fn dimensions(&self) -> Result<Option<Dimensions>>;
fn style(&self) -> Option<taffy::style::Style> {
None
}
}
pub fn generate_key() -> Key {
rand::random::<Key>()
}