use std::collections::{HashMap, HashSet};
use chrono::{DateTime, Utc};
use macros::make_path_parts;
use serde::{Deserialize, Serialize};
use crate::{api::endpoints::PathPart, domain::module::StableModuleId};
use super::{JigId, JigPlayerSettings, JigResponse};
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PathPart, PartialEq, Eq)]
#[cfg_attr(feature = "backend", derive(sqlx::Type))]
#[cfg_attr(feature = "backend", sqlx(transparent))]
pub struct JigCode(pub i32);
impl ToString for JigCode {
fn to_string(&self) -> String {
format!("{:06}", self.0)
}
}
make_path_parts!(JigPlayerSessionCreatePath => "/v1/jig/codes");
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct JigPlayerSessionCreateRequest {
pub jig_id: JigId,
pub name: Option<String>,
pub settings: JigPlayerSettings,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct JigPlayerSessionCreateResponse {
pub index: JigCode,
}
make_path_parts!(JigCodePath => "/v1/jig/codes/{}" => JigCode);
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct JigCodeUpdateRequest {
pub name: Option<Option<String>>,
pub settings: Option<JigPlayerSettings>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JigCodeResponse {
pub index: JigCode,
pub jig_id: JigId,
pub name: Option<String>,
pub settings: JigPlayerSettings,
pub created_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
}
make_path_parts!(JigCodeListPath => "/v1/jig/codes");
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct JigCodeListRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub jig_id: Option<JigId>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JigCodeListResponse {
pub codes: Vec<JigCodeResponse>,
}
make_path_parts!(JigsWithCodesPath => "/v1/jig/codes/jig-codes");
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JigsWithCodesResponse {
pub jigs: Vec<JigWithCodes>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JigWithCodes {
pub jig: JigResponse,
pub codes: Vec<JigCodeResponse>,
}
make_path_parts!(JigCodeSessionsPath => "/v1/jig/codes/{}/sessions" => JigCode);
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JigCodeSessionsListResponse {
pub sessions: Vec<JigCodeSessionResponse>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JigCodeSessionResponse {
pub code: JigCode,
pub players_name: Option<String>,
pub started_at: DateTime<Utc>,
pub finished_at: Option<DateTime<Utc>>,
pub info: Option<JigPlaySession>,
}
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct JigPlaySession {
#[serde(default)]
pub modules: Vec<JigPlaySessionModule>,
#[serde(default)]
pub visited: HashSet<StableModuleId>,
}
impl JigPlaySessionModuleGetPointsEarned for JigPlaySession {
fn get_points_earned(&self) -> PointsEarned {
let mut available = 0.0;
let mut earned = 0.0;
for module in &self.modules {
let module_points = module.get_points_earned();
available += module_points.available;
earned += module_points.earned;
}
PointsEarned { available, earned }
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum JigPlaySessionModule {
Matching(JigPlaySessionMatching),
CardQuiz(JigPlaySessionCardQuiz),
DragDrop(JigPlaySessionDragDrop),
FindAnswer(JigPlaySessionFindAnswer),
}
impl JigPlaySessionModule {
pub fn stable_module_id(&self) -> StableModuleId {
match self {
Self::Matching(module) => module.stable_module_id,
Self::CardQuiz(module) => module.stable_module_id,
Self::DragDrop(module) => module.stable_module_id,
Self::FindAnswer(module) => module.stable_module_id,
}
}
}
impl JigPlaySessionModuleGetPointsEarned for JigPlaySessionModule {
fn get_points_earned(&self) -> PointsEarned {
match self {
JigPlaySessionModule::Matching(module) => module.get_points_earned(),
JigPlaySessionModule::CardQuiz(module) => module.get_points_earned(),
JigPlaySessionModule::DragDrop(module) => module.get_points_earned(),
JigPlaySessionModule::FindAnswer(module) => module.get_points_earned(),
}
}
}
pub trait JigPlaySessionModuleGetPointsEarned {
fn get_points_earned(&self) -> PointsEarned;
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PointsEarned {
pub available: f32,
pub earned: f32,
}
impl std::fmt::Display for PointsEarned {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}|{}", self.earned, self.available)
}
}
impl PointsEarned {
pub fn percent(&self) -> u16 {
let output = (self.earned / self.available) * 100.00;
output as u16
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JigPlaySessionMatching {
pub stable_module_id: StableModuleId,
pub rounds: Vec<HashMap<usize, JigPlaySessionMatchingCard>>,
}
impl JigPlaySessionMatching {
pub fn new(stable_module_id: StableModuleId) -> Self {
Self {
stable_module_id,
rounds: vec![],
}
}
}
impl JigPlaySessionModuleGetPointsEarned for JigPlaySessionMatching {
fn get_points_earned(&self) -> PointsEarned {
let mut available = 0.0;
let mut earned = 0.0;
for round in &self.rounds {
for (_, card) in round {
available += 1.0;
earned += match card.failed_tries {
0 => 1.0,
1 => 0.5,
_ => 0.0,
};
}
}
PointsEarned { available, earned }
}
}
#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
pub struct JigPlaySessionMatchingCard {
pub failed_tries: u16,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JigPlaySessionCardQuiz {
pub stable_module_id: StableModuleId,
pub rounds: Vec<JigPlaySessionCardQuizRound>,
}
impl JigPlaySessionCardQuiz {
pub fn new(stable_module_id: StableModuleId) -> Self {
Self {
stable_module_id,
rounds: Vec::new(),
}
}
}
impl JigPlaySessionModuleGetPointsEarned for JigPlaySessionCardQuiz {
fn get_points_earned(&self) -> PointsEarned {
let mut available = 0.0;
let mut earned = 0.0;
for card in &self.rounds {
available += 1.0;
earned += match card.failed_tries {
0 => 1.0,
1 => 0.5,
_ => 0.0,
};
}
PointsEarned { available, earned }
}
}
#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
pub struct JigPlaySessionCardQuizRound {
pub card_index: usize,
pub failed_tries: u16,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JigPlaySessionDragDrop {
pub stable_module_id: StableModuleId,
pub items: HashMap<usize, JigPlaySessionDragDropItem>,
}
impl JigPlaySessionDragDrop {
pub fn new(stable_module_id: StableModuleId) -> Self {
Self {
stable_module_id,
items: HashMap::new(),
}
}
}
impl JigPlaySessionModuleGetPointsEarned for JigPlaySessionDragDrop {
fn get_points_earned(&self) -> PointsEarned {
let mut available = 0.0;
let mut earned = 0.0;
for card in self.items.values() {
available += 1.0;
earned += match card.failed_tries {
0 => 1.0,
1 => 0.5,
_ => 0.0,
};
}
PointsEarned { available, earned }
}
}
#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
pub struct JigPlaySessionDragDropItem {
pub failed_tries: u16,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JigPlaySessionFindAnswer {
pub stable_module_id: StableModuleId,
pub items: Vec<JigPlaySessionFindAnswerItem>,
}
impl JigPlaySessionFindAnswer {
pub fn new(stable_module_id: StableModuleId) -> Self {
Self {
stable_module_id,
items: Vec::new(),
}
}
}
impl JigPlaySessionModuleGetPointsEarned for JigPlaySessionFindAnswer {
fn get_points_earned(&self) -> PointsEarned {
let mut available = 0.0;
let mut earned = 0.0;
for card in &self.items {
available += 1.0;
earned += match card.failed_tries {
0 => 1.0,
1 => 0.5,
_ => 0.0,
};
}
PointsEarned { available, earned }
}
}
#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
pub struct JigPlaySessionFindAnswerItem {
pub failed_tries: u16,
}
pub mod instance {
use macros::make_path_parts;
use serde::{Deserialize, Serialize};
use crate::domain::jig::{
codes::{JigCode, JigPlaySession},
JigId, JigPlayerSettings,
};
make_path_parts!(PlayerSessionInstanceCreatePath => "/v1/jig/codes/instance");
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PlayerSessionInstanceCreateRequest {
pub code: JigCode,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PlayerSessionInstanceResponse {
pub jig_id: JigId,
pub settings: JigPlayerSettings,
pub token: String,
}
make_path_parts!(PlayerSessionInstanceCompletePath => "/v1/jig/codes/instance/complete");
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PlayerSessionInstanceCompleteRequest {
pub token: String,
pub session: JigPlaySession,
pub players_name: Option<String>,
}
}