use std::{collections::HashMap, ops::{Deref, DerefMut}, str::FromStr};
use serde::{Deserialize, Serialize};
use crate::Either;
use super::{ItemOrList,BoolInt,bool_is_false,bool_is_true};
fn filler_default()->bool{
true
}
#[derive(Debug,Clone,PartialEq, Eq)]
pub struct AOrString<T: FromStr>{
x: T
}
impl<T: Serialize+FromStr> Serialize for AOrString<T>{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer {
self.x.serialize(serializer)
}
}
impl<'de,T: Deserialize<'de> + FromStr> Deserialize<'de> for AOrString<T>{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>
{
let o=<Either<T,String> as Deserialize<'de>>::deserialize(deserializer)?;
Ok(match o{
Either::Left(v)=>AOrString {x:v},
Either::Right(v)=>AOrString{
x: T::from_str(&v).ok().expect(&format!("Failed to parse {}",v))
}
})
}
}
impl<T: FromStr> Deref for AOrString<T>{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.x
}
}
impl<T: FromStr> DerefMut for AOrString<T>{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.x
}
}
fn default_count()->AOrString<u32>{
AOrString { x: 1 }
}
impl<T:FromStr> From<T> for AOrString<T>{
fn from(value: T) -> Self {
Self { x: value }
}
}
#[derive(Debug,Serialize,Deserialize,PartialEq, Eq,Clone)]
pub struct Item{
pub name: String,
#[serde(default,skip_serializing_if="ItemOrList::is_empty")]
pub category: ItemOrList<String>,
#[serde(default="default_count")]
pub count: AOrString<u32>,
#[serde(default,skip_serializing_if="HashMap::is_empty")]
pub value: HashMap<String,u32>,
#[serde(default,skip_serializing_if="bool_is_false")]
pub progression: bool,
#[serde(default,skip_serializing_if="bool_is_false")]
pub progression_skip_balancing: bool,
#[serde(default,skip_serializing_if="bool_is_false")]
pub useful: bool,
#[serde(default,skip_serializing_if="bool_is_false")]
pub trap: bool,
#[serde(default="filler_default",skip_serializing_if="bool_is_true")]
pub filler: bool,
#[serde(default,skip_serializing_if="BoolInt::is_false_or_zero")]
pub early: BoolInt,
#[serde(default,skip_serializing_if="bool_is_false")]
pub local: bool,
#[serde(default,skip_serializing_if="BoolInt::is_false_or_zero")]
pub local_early: BoolInt,
#[serde(default,skip_serializing_if="HashMap::is_empty")]
pub classification_count: HashMap<String,u32>,
#[serde(default,skip_serializing_if = "Option::is_none")]
pub id: Option<u32>
}
impl Item{
pub fn new(name: String)->Self{
Self { name, category: ItemOrList::default(), count: 1.into(), value: HashMap::default(), progression: false, progression_skip_balancing: false, useful: false, trap: false, filler:true, early: BoolInt::default(), local: false, local_early: BoolInt::default(), classification_count: HashMap::default(), id: None }
}
pub fn add_category(&mut self,c: String){
self.category.push(c);
}
pub fn remove_category(&mut self,idx: usize)->Option<String>{
self.category.remove(idx)
}
pub fn num_categories(&self)->usize{
self.category.len()
}
pub fn with_category(mut self,c: String)->Self{
self.add_category(c);
self
}
pub fn as_progression(mut self)->Self{
self.progression=true;
self.filler=false;
self
}
pub fn as_progression_skip_balancing(mut self)->Self{
self.progression_skip_balancing=true;
self.filler=false;
self
}
pub fn as_useful(mut self)->Self{
self.useful=true;
self.filler=false;
self
}
pub fn as_trap(mut self)->Self{
self.trap=true;
self.filler=false;
self
}
pub fn builder(name: String)->Self{
Self::new(name)
}
}
#[cfg(feature="item_flags")]
pub mod flagged{
use bitflags::bitflags;
use super::Item;
bitflags! {
pub struct ItemFlags: u8{
const progression=1;
const skip_balancing=8;
const trap=4;
const useful=2;
}
}
impl Item{
pub fn set_flags(&mut self,f: ItemFlags){
self.progression = f.contains(ItemFlags::progression);
self.progression_skip_balancing=f.contains(ItemFlags::progression | ItemFlags::skip_balancing);
self.trap=f.contains(ItemFlags::trap);
self.useful=f.contains(ItemFlags::useful);
self.filler=f.is_empty();
}
pub fn get_flags(&self)->ItemFlags{
let mut out = ItemFlags::empty();
out.set(ItemFlags::skip_balancing,self.progression_skip_balancing);
out.set(ItemFlags::progression,self.progression_skip_balancing || self.progression);
out.set(ItemFlags::trap,self.trap);
out.set(ItemFlags::useful,self.useful);
out
}
}
}
#[cfg(feature="item_flags")]
pub use flagged::{ItemFlags};