use std::io::{Read, Write};
use mcproto_codec::error::{CodecError, CodecKind, InvalidEncodingReason};
use crate::{Identifier, PrefixedArray, TypeCodec, TypeStructCodec, VarInt};
use super::{Slot, components::DataComponentType};
macro_rules! display_struct {
($(#[$meta:meta])* $name:ident { $($(#[$field_meta:meta])* $field:ident: $ty:ty),* $(,)? }) => {
$(#[$meta])*
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = SlotDisplay)]
pub struct $name { $($(#[$field_meta])* pub $field: $ty,)* }
};
}
display_struct!( WithAnyPotionSlotDisplay { base: Box<SlotDisplay> });
display_struct!(OnlyWithComponentSlotDisplay {
base: Box<SlotDisplay>,
component_type: DataComponentType,
});
display_struct!( ItemSlotDisplay { item_type_id: VarInt });
display_struct!( ItemStackSlotDisplay { item_stack: Slot });
display_struct!( TagSlotDisplay { tag: Identifier });
display_struct!( DyedSlotDisplay { dye: Box<SlotDisplay>, target: Box<SlotDisplay> });
display_struct!(SmithingTrimSlotDisplay {
base: Box<SlotDisplay>,
material: Box<SlotDisplay>,
pattern_id: VarInt,
});
display_struct!(WithRemainderSlotDisplay {
ingredient: Box<SlotDisplay>,
remainder: Box<SlotDisplay>,
});
display_struct!( CompositeSlotDisplay { options: PrefixedArray<SlotDisplay> });
#[derive(Debug, Clone, PartialEq)]
pub enum SlotDisplay {
Empty,
AnyFuel,
WithAnyPotion(WithAnyPotionSlotDisplay),
OnlyWithComponent(OnlyWithComponentSlotDisplay),
Item(ItemSlotDisplay),
ItemStack(ItemStackSlotDisplay),
Tag(TagSlotDisplay),
Dyed(DyedSlotDisplay),
SmithingTrim(SmithingTrimSlotDisplay),
WithRemainder(WithRemainderSlotDisplay),
Composite(CompositeSlotDisplay),
}
impl TypeCodec for SlotDisplay {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
let (id, payload): (i32, Option<&dyn EncodableDisplay>) = match self {
Self::Empty => (0, None),
Self::AnyFuel => (1, None),
Self::WithAnyPotion(v) => (2, Some(v)),
Self::OnlyWithComponent(v) => (3, Some(v)),
Self::Item(v) => (4, Some(v)),
Self::ItemStack(v) => (5, Some(v)),
Self::Tag(v) => (6, Some(v)),
Self::Dyed(v) => (7, Some(v)),
Self::SmithingTrim(v) => (8, Some(v)),
Self::WithRemainder(v) => (9, Some(v)),
Self::Composite(v) => (10, Some(v)),
};
VarInt(id)
.encode(writer)
.map_err(|e| e.with_context(CodecKind::SlotDisplay))?;
if let Some(payload) = payload {
payload
.encode_display(writer)
.map_err(|e| e.with_context(CodecKind::SlotDisplay))?;
}
Ok(())
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let id = VarInt::decode(reader)
.map_err(|e| e.with_context(CodecKind::SlotDisplay))?
.0;
match id {
0 => Ok(Self::Empty),
1 => Ok(Self::AnyFuel),
2 => Ok(Self::WithAnyPotion(WithAnyPotionSlotDisplay::decode(
reader,
)?)),
3 => Ok(Self::OnlyWithComponent(
OnlyWithComponentSlotDisplay::decode(reader)?,
)),
4 => Ok(Self::Item(ItemSlotDisplay::decode(reader)?)),
5 => Ok(Self::ItemStack(ItemStackSlotDisplay::decode(reader)?)),
6 => Ok(Self::Tag(TagSlotDisplay::decode(reader)?)),
7 => Ok(Self::Dyed(DyedSlotDisplay::decode(reader)?)),
8 => Ok(Self::SmithingTrim(SmithingTrimSlotDisplay::decode(reader)?)),
9 => Ok(Self::WithRemainder(WithRemainderSlotDisplay::decode(
reader,
)?)),
10 => Ok(Self::Composite(CompositeSlotDisplay::decode(reader)?)),
value => Err(CodecError::invalid_encoding(
CodecKind::SlotDisplay,
0,
InvalidEncodingReason::InvalidEnumValue {
value: i128::from(value),
},
)),
}
}
}
trait EncodableDisplay {
fn encode_display(&self, writer: &mut dyn Write) -> Result<(), CodecError>;
}
macro_rules! impl_encodable_display {
($($ty:ty),+ $(,)?) => {$(
impl EncodableDisplay for $ty {
fn encode_display(&self, writer: &mut dyn Write) -> Result<(), CodecError> {
struct DynWriter<'a>(&'a mut dyn Write);
impl Write for DynWriter<'_> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { self.0.write(buf) }
fn flush(&mut self) -> std::io::Result<()> { self.0.flush() }
}
self.encode(&mut DynWriter(writer))
}
}
)+};
}
impl_encodable_display!(
WithAnyPotionSlotDisplay,
OnlyWithComponentSlotDisplay,
ItemSlotDisplay,
ItemStackSlotDisplay,
TagSlotDisplay,
DyedSlotDisplay,
SmithingTrimSlotDisplay,
WithRemainderSlotDisplay,
CompositeSlotDisplay,
);