1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
//! This module contaings data structs for block entity data
//!
//! # Sources
//! - [minecraft.fandom.com](https://minecraft.fandom.com/wiki/Chunk_format#Block_entity_format)

mod chest_data;
use std::collections::HashMap;

pub use chest_data::*;

mod banner_data;
pub use banner_data::*;
use datatypes::Position;
use entmet_lib::entities::{entity::Bee, entity_types::EntityEnum};
use minecraft_assets::{effects::Effect, recipes::BlastFurnaceRecipe};
use nbt_lib::{
    convert_to_bool,
    traits::{AsNbtValue, FromNbtValue},
    unwrap_to_empty, unwrap_to_empty_if_exists, NbtValue,
};

/// A Struct for common struct data
#[derive(PartialEq, Debug, Default)]
pub struct BlockEntityData {
    /// The block id
    pub id: String,
    /// If true the block entity should not be placed
    pub keep_packed: bool,
    /// The X coordinate
    pub x: i32,
    /// The Y coordinate
    pub y: i32,
    /// The Z coordinate
    pub z: i32,
}

impl TryFrom<&HashMap<String, NbtValue>> for BlockEntityData {
    type Error = ();

    fn try_from(data: &HashMap<String, NbtValue>) -> Result<Self, Self::Error> {
        Ok(Self {
            id: unwrap_to_empty!(data.get("id"), string),
            keep_packed: convert_to_bool!(unwrap_to_empty!(data.get("keepPacked"), i8)),
            x: unwrap_to_empty!(data.get("x"), i32),
            y: unwrap_to_empty!(data.get("y"), i32),
            z: unwrap_to_empty!(data.get("z"), i32),
        })
    }
}

/// A struct storing block entity data
///
/// These are blocks, that store more data than normal blocks. e.g. Chest. These blocks where
/// called "tile entities" until they where renamed in 1.18
///
/// # Sources
/// - [minecraft.fandom.com](https://minecraft.fandom.com/wiki/Chunk_format#Block_entity_format)
///
/// # Example
/// ```
/// use level_lib::anvil::region::chunk::block_entity::{BlockEntity, ChestData, BlockEntityData};
/// let cd = ChestData::default();
/// let be = BlockEntity::Barrel{ chest_data: cd.clone() , block_entity_data:
/// BlockEntityData::default()};
/// let be_lock = match be {
///     BlockEntity::Barrel { chest_data, .. } => Some(chest_data),
///     _ => None,
/// };
/// assert!(matches!(be_lock, Some( ChestData { .. })));
/// let be_lock = be_lock.unwrap();
/// assert_eq!( be_lock.lock , cd.lock);
/// ```
#[deprecated(note = "This enum is still a work in progress and subject to change")]
#[derive(PartialEq, Debug)]
pub enum BlockEntity {
    /// Data block of an Block entity that only used the common tags used of all `BlockEntity`s
    ///
    /// # Used for
    /// - Bed
    /// - Bell
    Empty {
        /// Common data to all Block entities
        block_entity_data: BlockEntityData,
    },
    /// Additional data of a `Banner`
    Banner {
        /// Common data to all Block entities
        block_entity_data: BlockEntityData,
        /// Optional custom name
        custom_name: Option<String>,
        /// The pattern data of the banner
        patterns: Vec<banner_data::BannerPattern>,
    },
    /// Aditional data of a `Barrel`
    Barrel {
        /// Common data to all Block entities
        block_entity_data: BlockEntityData,
        /// The chest data of the barrel
        chest_data: ChestData,
    },
    /// Additional data of a `Beacon`
    Beacon {
        /// Common data to all Block entities
        block_entity_data: BlockEntityData,
        /// Optional custom name
        custom_name: Option<String>,
        /// Optional lock
        lock: Option<String>,
        /// Level of the beacon
        ///
        /// # Note
        ///
        /// Always 0 if the beam is blocked
        levels: i32,
        /// The selected primary effect
        primary: Option<Effect>,
        /// The selected secondary effect
        secondary: Option<Effect>,
    },
    /// Additional data of a `Beehive`
    Beehive {
        /// Common data to all Block entities
        block_entity_data: BlockEntityData,
        /// The bees currently inside of the hive
        bees: Vec<BeeData>,
        /// Stores the location of a flower, so that the bees can go to it
        flower_pos: Position,
    },
    /// Additional data of a `BlastFurnace`
    BlastFurnace {
        /// Common data to all Block entities
        block_entity_data: BlockEntityData,
        /// The amount of ticks, until the current fuel runs out
        burn_time: u16,
        /// The amount of ticks, that the current item is already cooking for
        cook_time: u16,
        /// The amount of ticks it takes for the item to be smelted
        cook_time_total: u16,
        /// An optional custom name
        custom_name: Option<String>,
        /// List of items in the blast furnace
        ///
        /// # Note
        ///
        /// 1. Slot is the item beeing smelted
        /// 2. Slot is the item used as next fuel
        /// 3. Slot is the item in the result slot
        items: Vec<Item>,
        /// A list of all used recipes to calculate the stored xp
        recepies_used: Vec<BlastFurnaceRecipe>,
    },
    /// Additional data of a chest
    Chest {
        /// Common data to all Block entities
        block_entity_data: BlockEntityData,
        /// The chest data of the chest
        chest_data: ChestData,
    },
    /// Additional data of a brushable block
    BrushableBlock {
        /// Common data to all Block entities
        block_entity_data: BlockEntityData,
        /// The loot table of the bruchable block
        loot_table: String,
        /// the loot table seed of the brushable block
        loot_table_seed: i64,
    },
    /// Additional data of a brushable block
    #[allow(missing_docs)]
    MobSpawner {
        /// Common data to all Block entities
        block_entity_data: BlockEntityData,
        /// the delay until the next mob is spawned
        delay: i16,
        max_nearby_entities: Option<i16>,
        max_spawn_delay: Option<i16>,
        min_spawn_delay: Option<i16>,
        required_player_range: Option<i16>,
        spawn_count: Option<i16>,
        /// the nbt data that is copied onto the next mob that spawns
        spawn_data: Option<NbtValue>,
        /// List of possible entities to spawn
        ///
        /// # Note
        /// Not fully implemented
        spawn_potentials: Option<Vec<NbtValue>>,
        /// The radius around which the spawner attempts to place mobs randomly
        spawn_range: i16,
    },
}
impl FromNbtValue for BlockEntity {
    fn from_nbt_value(value: nbt_lib::NbtValue) -> Result<Self, ()>
    where
        Self: Sized,
    {
        let (_, data) = unwrap_to_empty!(Some(value), compound);
        match unwrap_to_empty!(data.get("id"), str) {
            "minecraft:chest" => Ok(Self::Chest {
                block_entity_data: BlockEntityData::try_from(&data)?,
                chest_data: ChestData::try_from(&data)?,
            }),
            "minecraft:barrel" => Ok(Self::Barrel {
                block_entity_data: BlockEntityData::try_from(&data)?,
                chest_data: ChestData::try_from(&data)?,
            }),
            "minecraft:brushable_block" => Ok(Self::BrushableBlock {
                block_entity_data: BlockEntityData::try_from(&data)?,
                loot_table: unwrap_to_empty!(data, "LootTable", string),
                loot_table_seed: unwrap_to_empty!(data, "LootTableSeed", i64),
            }),
            "minecraft:mob_spawner" => Ok(Self::MobSpawner {
                block_entity_data: BlockEntityData::try_from(&data)?,
                delay: unwrap_to_empty!(data, "Delay", i16),
                max_spawn_delay: unwrap_to_empty_if_exists!(data, "MaxSpawnDelay", i16),
                min_spawn_delay: unwrap_to_empty_if_exists!(data, "MinSpawnDelay", i16),
                max_nearby_entities: unwrap_to_empty_if_exists!(data, "MaxNearbyEntities", i16),
                required_player_range: unwrap_to_empty_if_exists!(data, "RequiredPlayerRange", i16),
                spawn_count: unwrap_to_empty_if_exists!(data, "SpawnCount", i16),
                spawn_data: None,
                spawn_potentials: None,
                spawn_range: unwrap_to_empty!(data, "SpawnRange", i16),
            }),
            _ => panic!("not_implemented: {:?}", data.get("id")),
        }
    }
}
impl AsNbtValue for BlockEntity {
    fn as_nbt_value(&self) -> Result<nbt_lib::NbtValue, ()> {
        todo!()
    }
}
/// A struct holding bee data
#[derive(PartialEq, Debug)]
pub struct BeeData {
    /// The data of the `Bee`
    pub bee: Bee,
    /// The minimum amount of ticks, that the bee stays in the hive
    pub min_occupation_ticks: i32,
    /// The amount of ticks, that the bee is in the hive
    pub ticks_in_hive: i32,
}

impl BlockEntity {
    /// Returns `true` if the block entity is [`Banner`].
    ///
    /// [`Banner`]: BlockEntity::Banner
    #[must_use]
    pub fn is_banner(&self) -> bool {
        matches!(self, Self::Banner { .. })
    }

    /// Returns `true` if the block entry stores [`ChestData`]
    ///
    /// [`ChestData`]: ChestData
    #[must_use]
    pub fn has_chest_data(&self) -> bool {
        match self {
            Self::Empty { .. } => false,
            Self::Banner { .. } => false,
            Self::Beacon { .. } => false,
            Self::Barrel { .. } => true,
            Self::Beehive { .. } => false,
            Self::BlastFurnace { .. } => false,
            Self::Chest { .. } => true,
            Self::BrushableBlock { .. } => false,
            Self::MobSpawner { .. } => false,
        }
    }

    /// Returns `true` if the block entity is [`Barrel`].
    ///
    /// [`Barrel`]: BlockEntity::Barrel
    #[must_use]
    pub fn is_barrel(&self) -> bool {
        matches!(self, Self::Barrel { .. })
    }

    /// Returns `true` if the block entity is [`Beacon`].
    ///
    /// [`Beacon`]: BlockEntity::Beacon
    #[must_use]
    pub fn is_beacon(&self) -> bool {
        matches!(self, Self::Beacon { .. })
    }

    /// Returns `true` if the block entity is [`Beehive`].
    ///
    /// [`Beehive`]: BlockEntity::Beehive
    #[must_use]
    pub fn is_beehive(&self) -> bool {
        matches!(self, Self::Beehive { .. })
    }

    /// Returns `true` if the block entity is [`Empty`].
    ///
    /// [`Empty`]: BlockEntity::Empty
    #[must_use]
    pub fn is_empty(&self) -> bool {
        matches!(self, Self::Empty { .. })
    }

    /// Returns `true` if the block entity is [`BrushableBlock`].
    ///
    /// [`BrushableBlock`]: BlockEntity::BrushableBlock
    #[must_use]
    pub fn is_brushable_block(&self) -> bool {
        matches!(self, Self::BrushableBlock { .. })
    }

    /// Returns `true` if the block entity is [`Chest`].
    ///
    /// [`Chest`]: BlockEntity::Chest
    #[must_use]
    pub fn is_chest(&self) -> bool {
        matches!(self, Self::Chest { .. })
    }
}