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
//     dis-rust - A rust implementation of the DIS simulation protocol.
//     Copyright (C) 2022 Thomas Mann
// 
//     This software is dual-licensed. It is available under the conditions of
//     the GNU Affero General Public License (see the LICENSE file included) 
//     or under a commercial license (email contact@coffeebreakdevs.com for
//     details).

use bytes::{BytesMut, BufMut, Buf};
use num_derive::FromPrimitive;
use serde::{Serialize, Deserialize}; 

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
/// General Appearance Record as defined in IEEE 1278.1 standard. Used to communicate the general appearance of an entity during the simulation.
pub struct GeneralAppearanceRecord {
    pub entity_paint_scheme_field: EntityPaintScheme,
    pub entity_mobility_kill_field: EntityMobilityKill,
    pub entity_fire_power_field: EntityFirePower,
    pub entity_damage_field: EntityDamage,
    pub entity_smoke_field: EntitySmoke,
    pub entity_trailing_effect_field: EntityTrailingEffect,
    pub entity_hatch_state_field: EntityHatchState,
    pub entity_lights_field: EntityLights,
    pub entity_flaming_effect_field: EntityFlamingEffect,
}

impl GeneralAppearanceRecord {
    /// Provides a function to create a new GeneralAppearanceRecord.
    pub fn new(entity_paint_scheme_field: EntityPaintScheme,
        entity_mobility_kill_field: EntityMobilityKill,
        entity_fire_power_field: EntityFirePower,
        entity_damage_field: EntityDamage,
        entity_smoke_field: EntitySmoke,
        entity_trailing_effect_field: EntityTrailingEffect,
        entity_hatch_state_field: EntityHatchState,
        entity_lights_field: EntityLights,
        entity_flaming_effect_field: EntityFlamingEffect,) -> Self {
        GeneralAppearanceRecord {
            entity_paint_scheme_field,
            entity_mobility_kill_field,
            entity_fire_power_field,
            entity_damage_field,
            entity_smoke_field,
            entity_trailing_effect_field,
            entity_hatch_state_field,
            entity_lights_field,
            entity_flaming_effect_field,
        }
    }

    /// Provides a function to default a new GeneralAppearanceRecord.
    /// The default record shows an entity in uniform with no damage or effects.
    pub fn default() -> Self {
        GeneralAppearanceRecord {
            entity_paint_scheme_field: EntityPaintScheme::UniformColor,
            entity_mobility_kill_field: EntityMobilityKill::NoMobilityKill,
            entity_fire_power_field: EntityFirePower::NoFirePowerKill,
            entity_damage_field: EntityDamage::NoDamage,
            entity_smoke_field: EntitySmoke::NotSmoking,
            entity_trailing_effect_field: EntityTrailingEffect::None,
            entity_hatch_state_field: EntityHatchState::NotApplicable,
            entity_lights_field: EntityLights::None,
            entity_flaming_effect_field: EntityFlamingEffect::None,
        }
    }

    /// Fills a BytesMut struct with a GeneralAppearanceRecord serialised into binary. This buffer is then ready to be sent via
    /// UDP to the simluation network.
    pub fn serialize(&self, buf: &mut BytesMut) {
        let entity_paint_scheme : u16 = self.entity_paint_scheme_field as u16;
        let entity_paint_scheme = entity_paint_scheme << 15;
        let entity_mobility_kill : u16 = self.entity_mobility_kill_field as u16;
        let entity_mobility_kill = entity_mobility_kill << 14;
        let entity_fire_power : u16 = self.entity_fire_power_field as u16;
        let entity_fire_power = entity_fire_power << 13;
        let entity_damage : u16 = self.entity_damage_field as u16;
        let entity_damage = entity_damage << 11;
        let entity_smoke : u16 = self.entity_smoke_field as u16;
        let entity_smoke = entity_smoke << 9;
        let entity_trailing_effect : u16 = self.entity_trailing_effect_field as u16;
        let entity_trailing_effect = entity_trailing_effect << 7;
        let entity_hatch_state : u16 = self.entity_hatch_state_field as u16;
        let entity_hatch_state = entity_hatch_state << 4;
        let entity_lights : u16 = self.entity_lights_field as u16;
        let entity_lights = entity_lights << 1;
        let entity_flaming_effect : u16 = self.entity_flaming_effect_field as u16;

        let general_appearance : u16 = 0u16 | entity_paint_scheme | entity_mobility_kill
            | entity_fire_power | entity_damage | entity_smoke | entity_trailing_effect
            | entity_hatch_state | entity_lights | entity_flaming_effect;
        buf.put_u16(general_appearance);
    }

    pub fn decode(buf: &mut BytesMut) -> GeneralAppearanceRecord {
        let bytes = buf.get_u16();
        GeneralAppearanceRecord { 
            entity_paint_scheme_field: EntityPaintScheme::from_u8((bytes >> 1) as u8), 
            entity_mobility_kill_field: EntityMobilityKill::from_u8((bytes >> 1) as u8), 
            entity_fire_power_field: EntityFirePower::from_u8((bytes >> 1) as u8), 
            entity_damage_field: EntityDamage::from_u8((bytes >> 2) as u8), 
            entity_smoke_field: EntitySmoke::from_u8((bytes >> 2) as u8), 
            entity_trailing_effect_field: EntityTrailingEffect::from_u8((bytes >> 2) as u8), 
            entity_hatch_state_field: EntityHatchState::from_u8((bytes >> 3) as u8), 
            entity_lights_field: EntityLights::from_u8((bytes >> 3) as u8), 
            entity_flaming_effect_field: EntityFlamingEffect::from_u8((bytes >> 1) as u8) 
        }
    }
}

#[derive(Debug, FromPrimitive, PartialEq, Clone, Copy, Serialize, Deserialize)]
/// Enum to represent an entity's paint scheme.
pub enum EntityPaintScheme {
    UniformColor = 0,
    Camouflage = 1,
}

impl EntityPaintScheme {
    pub fn from_u8(bit: u8) -> EntityPaintScheme {
        match bit {
            0 => EntityPaintScheme::UniformColor,
            1 => EntityPaintScheme::Camouflage,
            2_u8..=u8::MAX => EntityPaintScheme::UniformColor
        }
    }
}

#[derive(Debug, FromPrimitive, PartialEq, Clone, Copy, Serialize, Deserialize)]
/// Enum to represent an entity's mobility kill status.
pub enum EntityMobilityKill {
    NoMobilityKill = 0,
    MobilityKill = 1,
}

impl EntityMobilityKill {
    pub fn from_u8(bit: u8) -> EntityMobilityKill {
        match bit {
            0 => EntityMobilityKill::NoMobilityKill,
            1 => EntityMobilityKill::MobilityKill,
            2_u8..=u8::MAX => EntityMobilityKill::NoMobilityKill
        }
    }
}

#[derive(Debug, FromPrimitive, PartialEq, Clone, Copy, Serialize, Deserialize)]
/// Enum to represent an entity's firepower kill status.
pub enum EntityFirePower {
    NoFirePowerKill = 0,
    FirePowerKill = 1,
}

impl EntityFirePower {
    pub fn from_u8(bit: u8) -> EntityFirePower {
        match bit {
            0 => EntityFirePower::NoFirePowerKill,
            1 => EntityFirePower::FirePowerKill,
            2_u8..=u8::MAX => EntityFirePower::NoFirePowerKill
        }
    }
}

#[derive(Debug, FromPrimitive, PartialEq, Clone, Copy, Serialize, Deserialize)]
/// Enum to represent an entity's damage.
pub enum EntityDamage {
    NoDamage = 0,
    SlightDamage = 1,
    ModerateDamage = 2,
    Destroyed = 3,
}

impl EntityDamage {
    pub fn from_u8(bit: u8) -> EntityDamage {
        match bit {
            0 => EntityDamage::NoDamage,
            1 => EntityDamage::SlightDamage,
            2 => EntityDamage::ModerateDamage,
            3 => EntityDamage::Destroyed,
            3_u8..=u8::MAX => EntityDamage::Destroyed
        }
    }
}

#[derive(Debug, FromPrimitive, PartialEq, Clone, Copy, Serialize, Deserialize)]
/// Enum to represent an entity's smoke effect.
pub enum EntitySmoke {
    NotSmoking = 0,
    SmokePlumeRising = 1,
    EngineSmoke = 2,
    EngineSmokeAndSmokePlumeRising = 3,
}

impl EntitySmoke {
    pub fn from_u8(bit: u8) -> EntitySmoke {
        match bit {
            0 => EntitySmoke::NotSmoking,
            1 => EntitySmoke::SmokePlumeRising,
            2 => EntitySmoke::EngineSmoke,
            3_u8..=u8::MAX => EntitySmoke::EngineSmokeAndSmokePlumeRising
        }
    }
}

#[derive(Debug, FromPrimitive, PartialEq, Clone, Copy, Serialize, Deserialize)]
/// Enum to represent an entity's trailing effect.
pub enum EntityTrailingEffect {
    None = 0,
    Small = 1,
    Medium = 2,
    Large = 3,
}

impl EntityTrailingEffect {
    pub fn from_u8(bit: u8) -> EntityTrailingEffect {
        match bit {
            0 => EntityTrailingEffect::None,
            1 => EntityTrailingEffect::Small,
            2 => EntityTrailingEffect::Medium,
            3_u8..=u8::MAX => EntityTrailingEffect::Large
        }
    }
}

#[derive(Debug, FromPrimitive, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub enum EntityHatchState {
    NotApplicable = 0,
    PrimaryHatchClosed = 1,
    PrimaryHatchPopped = 2,
    PrimaryHatchPoppedAndPersonVisibleUnderHatch = 3,
    PrimaryHatchOpen = 4,
    PrimaryHatchOpenAndPersonVisible = 5,
}

impl EntityHatchState {
    pub fn from_u8(bit: u8) -> EntityHatchState {
        match bit {
            0 => EntityHatchState::NotApplicable,
            1 => EntityHatchState::PrimaryHatchClosed,
            2 => EntityHatchState::PrimaryHatchPopped,
            3 => EntityHatchState::PrimaryHatchPoppedAndPersonVisibleUnderHatch,
            4 => EntityHatchState::PrimaryHatchOpen,
            5_u8..=u8::MAX => EntityHatchState::PrimaryHatchOpenAndPersonVisible
        }
    }
}

#[derive(Debug, FromPrimitive, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub enum EntityLights {
    None = 0,
    RunningLightsOn = 1,
    NavigationLightsOn = 2,
    FormationLightsOn = 3,
}

impl EntityLights {
    pub fn from_u8(bit: u8) -> EntityLights {
        match bit {
            0 => EntityLights::None,
            1 => EntityLights::RunningLightsOn,
            2 => EntityLights::NavigationLightsOn,
            3_u8..=u8::MAX => EntityLights::None
        }
    }
}

#[derive(Debug, FromPrimitive, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub enum EntityFlamingEffect {
    None = 0,
    FlamesPresent = 1,
}

impl EntityFlamingEffect {
    pub fn from_u8(bit: u8) -> EntityFlamingEffect {
        match bit {
            0 => EntityFlamingEffect::None,
            1 => EntityFlamingEffect::FlamesPresent,
            2_u8..=u8::MAX => EntityFlamingEffect::None
        }
    }
}