use std::collections::HashSet;
use std::path::PathBuf;
use anyhow::Result;
use crate::api::ApiClient;
use crate::audio::{AudioPlayer, Sound};
use crate::buttons::Button;
use crate::config::{ApiConfig, PartyConfig};
use crate::events::{Event, EventReceiver, EventSender};
use crate::model::{CurrentUser, Tag, UserId, UserMode};
use crate::random;
enum EventHandlingResult {
KeepCurrentUser,
SetCurrentUser(CurrentUser),
ResetCurrentUser,
Abort,
}
pub(crate) struct Client {
audio_player: AudioPlayer,
user_mode: UserMode,
admin_tags: HashSet<Tag>,
api_client: ApiClient,
party_config: PartyConfig,
event_receiver: EventReceiver,
event_sender: EventSender,
}
impl Client {
pub(crate) fn new(
sounds_path: PathBuf,
user_mode: UserMode,
admin_tags: HashSet<Tag>,
api_config: &ApiConfig,
party_config: PartyConfig,
event_receiver: EventReceiver,
event_sender: EventSender,
) -> Result<Self> {
Ok(Self {
audio_player: AudioPlayer::new(sounds_path)?,
user_mode,
admin_tags,
api_client: ApiClient::new(api_config, party_config.party_id.clone()),
party_config,
event_receiver,
event_sender,
})
}
pub(crate) fn run(&self) -> Result<()> {
self.sign_on()?;
self.handle_events()?;
Ok(())
}
fn handle_events(&self) -> Result<()> {
let default_current_user = match self.user_mode {
UserMode::SingleUser(ref user_id) => CurrentUser::User(user_id.clone()),
UserMode::MultiUser => CurrentUser::None,
};
let mut current_user = default_current_user.clone();
for event in self.event_receiver.iter() {
let result = match self.user_mode {
UserMode::SingleUser(ref user_id) => {
self.handle_single_user_event(event, user_id.clone())?
}
UserMode::MultiUser => self.handle_multi_user_event(event, ¤t_user)?,
};
current_user = match result {
EventHandlingResult::KeepCurrentUser => current_user.clone(),
EventHandlingResult::SetCurrentUser(new_current_user) => new_current_user.clone(),
EventHandlingResult::ResetCurrentUser => default_current_user.clone(),
EventHandlingResult::Abort => {
break;
}
}
}
Ok(())
}
fn handle_single_user_event(
&self,
event: Event,
single_user_id: UserId,
) -> Result<EventHandlingResult> {
Ok(match event {
Event::TagRead { .. } => {
log::error!("Unexpected tag read event received.");
EventHandlingResult::ResetCurrentUser
}
Event::ButtonPressed { button } => {
log::debug!("Button pressed: {:?}", button);
self.handle_button_press_with_identified_user(&single_user_id, button)?
}
Event::ShutdownRequested => {
self.shutdown()?;
EventHandlingResult::Abort
}
})
}
fn handle_multi_user_event(
&self,
event: Event,
current_user: &CurrentUser,
) -> Result<EventHandlingResult> {
Ok(match event {
Event::TagRead { tag } => {
log::debug!("Tag read: {}", tag.value);
self.handle_tag_read(&tag)?
}
Event::ButtonPressed { button } => {
log::debug!("Button pressed: {:?}", button);
match current_user {
CurrentUser::Admin => self.handle_button_press_by_admin(button)?,
CurrentUser::User(user_id) => {
self.handle_button_press_with_identified_user(user_id, button)?
}
CurrentUser::None => EventHandlingResult::ResetCurrentUser,
}
}
_ => self.handle_common_event(event)?,
})
}
fn handle_common_event(&self, event: Event) -> Result<EventHandlingResult> {
Ok(match event {
Event::ShutdownRequested => {
self.shutdown()?;
EventHandlingResult::Abort
}
_ => {
log::warn!("Unexpected event, should have been be handled elsewhere.");
EventHandlingResult::KeepCurrentUser
}
})
}
fn sign_on(&self) -> Result<()> {
log::info!("Signing on ...");
match self.api_client.sign_on() {
Ok(()) => {
log::info!("Signed on.");
self.play_sound(Sound::SignOnSucceeded);
}
Err(e) => {
log::warn!("Signing on failed.\n{e}");
self.play_sound(Sound::SignOnFailed);
}
}
Ok(())
}
fn sign_off(&self) -> Result<()> {
log::info!("Signing off ...");
match self.api_client.sign_off() {
Ok(()) => {
log::info!("Signed off.");
self.play_sound(Sound::SignOffSucceeded);
}
Err(e) => {
log::warn!("Signing off failed.\n{e}");
self.play_sound(Sound::SignOffFailed);
}
}
Ok(())
}
fn handle_tag_read(&self, tag: &Tag) -> Result<EventHandlingResult> {
if self.admin_tags.contains(tag) {
self.play_sound(Sound::AdminModeEntered);
log::info!("Entering admin mode.");
return Ok(EventHandlingResult::SetCurrentUser(CurrentUser::Admin));
}
log::debug!("Requesting details for tag {} ...", tag.value);
match self.api_client.get_tag_details(tag) {
Ok(details) => match details {
Some(details) => {
log::debug!(
"User for tag {}: {} (ID: {})",
details.identifier,
details.user.screen_name.unwrap_or("<nameless>".to_string()),
details.user.id
);
let user_id = details.user.id;
if let Some(name) = details.sound_name {
self.play_sound(Sound::UserTagCustomGreeting(name));
}
log::debug!("Awaiting whereabouts for user {user_id} ...");
Ok(EventHandlingResult::SetCurrentUser(CurrentUser::User(
user_id,
)))
}
None => {
log::info!("Unknown user tag: {}", tag.value);
self.play_sound(Sound::UserTagUnknown);
Ok(EventHandlingResult::ResetCurrentUser)
}
},
Err(e) => {
log::warn!("Requesting tag details failed.\n{e}");
self.play_sound(Sound::CommunicationFailed);
Ok(EventHandlingResult::ResetCurrentUser)
}
}
}
fn handle_button_press_by_admin(&self, button: Button) -> Result<EventHandlingResult> {
Ok(match button {
Button::Button1 => {
log::info!("Leaving admin mode.");
self.play_sound(Sound::AdminModeLeft);
EventHandlingResult::ResetCurrentUser
}
Button::Button2 => {
self.event_sender.send_shutdown_requested()?;
EventHandlingResult::ResetCurrentUser
}
_ => {
EventHandlingResult::KeepCurrentUser
}
})
}
fn handle_button_press_with_identified_user(
&self,
user_id: &UserId,
button: Button,
) -> Result<EventHandlingResult> {
if let Some(whereabouts_name) = &self.party_config.buttons_to_whereabouts.get(&button) {
log::debug!("Updating whereabouts status for user {user_id} -> {whereabouts_name} ...");
let response = self.update_status(user_id, whereabouts_name);
match response {
Ok(_) => {
log::debug!("Whereabouts status successfully updated.");
let sound = self
.party_config
.whereabouts_sounds
.get(*whereabouts_name)
.and_then(|sound_names| random::choose_random_element(sound_names))
.map(Sound::WhereaboutsStatusUpdatedCustom)
.unwrap_or(Sound::WhereaboutsStatusUpdated);
self.play_sound(sound);
}
Err(e) => {
log::warn!("Whereabouts status update failed.\n{e}");
self.play_sound(Sound::CommunicationFailed);
}
}
}
Ok(EventHandlingResult::ResetCurrentUser)
}
fn shutdown(&self) -> Result<()> {
log::info!("Shutdown requested.");
self.sign_off()?;
log::info!("Shutting down ...");
Ok(())
}
fn update_status(&self, user_id: &UserId, whereabouts_name: &str) -> Result<()> {
self.api_client.update_status(user_id, whereabouts_name)
}
fn play_sound(&self, sound: Sound) {
let name = sound.get_name();
if let Err(e) = self.audio_player.play(&name) {
log::warn!("Could not play sound: {e}");
}
}
}