use chrono::{DateTime, Local, TimeZone};
use serde::Deserialize;
use serde_json::Value;
use std::collections::HashMap;
use serde::de::DeserializeOwned;
use uuid::Uuid;
use crate::api::{ColorCodes, MonthlyPackageRank, PackageRank, StaffLevel};
use crate::error::HypixelApiError;
use crate::util::leveling;
#[derive(Debug, Clone, Deserialize)]
pub struct PlayerReply {
success: bool,
player: Option<PlayerData>,
}
impl PlayerReply {
pub fn success(&self) -> bool {
self.success
}
pub fn player(&self) -> Option<&PlayerData> {
self.player.as_ref()
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct PlayerData {
uuid: Uuid,
#[serde(rename = "displayname")]
display_name: Option<String>,
#[serde(rename = "knownAliases")]
known_aliases: Option<Vec<String>>,
#[serde(rename = "playername")]
player_name: Option<String>,
#[serde(rename = "username")]
user_name: Option<String>,
#[serde(rename = "rank")]
staff_level: Option<StaffLevel>,
#[serde(rename = "packageRank")]
package_rank: Option<PackageRank>,
#[serde(rename = "newPackageRank")]
new_package_rank: Option<PackageRank>,
#[serde(rename = "monthlyPackageRank")]
is_plus_plus: Option<MonthlyPackageRank>,
#[serde(rename = "rankPlusColor")]
rank_plus_color: Option<ColorCodes>,
#[serde(rename = "monthlyRankColor")]
superstar_tag_color: Option<ColorCodes>,
#[serde(rename = "buildTeam", default)]
build_team: bool,
#[serde(rename = "buildTeamAdmin", default)]
build_team_admin: bool,
#[serde(rename = "firstLogin")]
first_login: Option<u64>,
#[serde(rename = "lastLogin")]
last_login: Option<u64>,
#[serde(rename = "lastLogout")]
last_logout: Option<u64>,
#[serde(rename = "networkExp", default)]
network_exp: f64,
#[serde(rename = "networkLevel", default)]
network_lvl: f64,
#[serde(default)]
karma: u64,
stats: Option<HashMap<String, Value>>,
#[serde(flatten)]
other: HashMap<String, Value>,
}
impl PlayerData {
pub fn uuid(&self) -> Uuid {
self.uuid
}
pub fn name(&self) -> Option<&str> {
if self.display_name.is_some() {
return self.display_name.as_deref();
}
if let Some(aliases) = &self.known_aliases {
if aliases.len() > 0 {
return Some(&aliases[aliases.len() - 1]);
}
}
if self.player_name.is_some() {
return self.player_name.as_deref();
}
if self.user_name.is_some() {
return self.user_name.as_deref();
}
None
}
pub fn network_xp(&self) -> u64 {
(self.network_exp + leveling::network::total_xp_to_full_level(self.network_lvl + 1.0)) as u64
}
pub fn network_level(&self) -> f64 {
let xp = self.network_exp + leveling::network::total_xp_to_full_level(self.network_lvl + 1.0);
leveling::network::exact_level(xp)
}
pub fn karma(&self) -> u64 {
self.karma
}
pub fn first_login(&self) -> Option<DateTime<Local>> {
self.first_login.map(|v| Local.timestamp_millis(v as i64))
}
pub fn last_login(&self) -> Option<DateTime<Local>> {
self.last_login.map(|v| Local.timestamp_millis(v as i64))
}
pub fn last_logout(&self) -> Option<DateTime<Local>> {
self.last_logout.map(|v| Local.timestamp_millis(v as i64))
}
pub fn selected_plus_color(&self) -> ColorCodes {
self.rank_plus_color.unwrap_or(ColorCodes::Red)
}
pub fn superstar_tag_color(&self) -> ColorCodes {
self.superstar_tag_color.unwrap_or(ColorCodes::Gold)
}
pub fn staff_level(&self) -> &StaffLevel {
self.staff_level.as_ref().unwrap_or(&StaffLevel::Normal)
}
pub fn package_rank(&self) -> PackageRank {
if self.is_plus_plus.filter(|v| *v != MonthlyPackageRank::None).is_some() {
PackageRank::MvpPlusPlus
} else if let Some(rank) = self.new_package_rank.filter(|v| *v != PackageRank::None) {
rank
} else if let Some(rank) = self.package_rank.filter(|v| *v != PackageRank::None) {
rank
} else {
PackageRank::None
}
}
pub fn has_rank(&self) -> bool {
*self.staff_level() != StaffLevel::Normal || self.package_rank() != PackageRank::None
}
pub fn on_build_team(&self) -> bool {
self.build_team || self.build_team_admin
}
pub fn stat_value(&self, name: &str) -> Option<&Value> {
self.stats.as_ref().map(|m| m.get(name)).flatten()
}
pub fn stat_json<T: DeserializeOwned>(&self, name: &str) -> Option<Result<T, HypixelApiError>> {
self.stats.as_ref().map(|m| m.get(name))
.flatten()
.map(|v| serde_json::from_value(v.clone()).map_err(|e| e.into()))
}
pub fn property_value(&self, name: &str) -> Option<&Value> {
self.other.get(name)
}
pub fn property_json<T: DeserializeOwned>(&self, name: &str) -> Option<Result<T, HypixelApiError>> {
self.other.get(name)
.map(|v| serde_json::from_value(v.clone()).map_err(|e| e.into()))
}
}