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
/*
    This file is part of Donet.

    Copyright © 2024 Max Rodriguez

    Donet is free software; you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License,
    as published by the Free Software Foundation, either version 3
    of the License, or (at your option) any later version.

    Donet is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public
    License along with Donet. If not, see <https://www.gnu.org/licenses/>.
*/

//! Base data model for DC Field elements. Alone, it represents
//! an attribute of a structure or Distributed Class.

use crate::datagram::datagram::Datagram;
use crate::dcatomic::DCAtomicField;
use crate::dckeyword::{DCKeywordList, IdentifyKeyword};
use crate::dclass::DClass;
use crate::dcmolecular::DCMolecularField;
use crate::dconfig::*;
use crate::dcstruct::DCStruct;
use crate::dctype::DCTypeDefinition;
use crate::globals;
use crate::hashgen::*;

/// Enumerator representing the 3 types of fields that inherit DC Field,
/// which can legally be declared within a Distributed Class.
///
/// Plain DC Fields represent a property, or member, of a structure
/// or class. DC fields have a data type assigned to them.
///
/// DC Atomic Fields represent a method of a Distributed Class, which
/// is always implemented as a remote procedure call (RPC). Unlike
/// attribute fields, atomic fields cannot be declared within structs.
///
/// DC Molecular Fields represent a collection of one or more
/// DC Atomic Fields as one field under one identifier. The parameters
/// of a molecular field are the parameters of all the fields it
/// represents, joined together in the order in which they were declared
/// when the molecular field was declared.
#[derive(Debug)]
pub enum ClassField<'dc> {
    Field(DCField<'dc>),
    Atomic(DCAtomicField<'dc>),
    Molecular(DCMolecularField<'dc>),
}

/// A different enumerator representing DC Field types used
/// for DC Structs, since they cannot contain DC Atomic Fields.
#[derive(Debug)]
pub enum StructField<'dc> {
    Field(DCField<'dc>),
    Molecular(DCMolecularField<'dc>),
}

/// A DC field element can be declared within a dclass or a
/// struct declaration. The DC field element must have a
/// reference to its parent, which is stored in this enum type.
#[derive(Debug)]
pub enum FieldParent<'dc> {
    DClass(&'dc DClass<'dc>),
    Strukt(&'dc DCStruct<'dc>), // 'strukt' due to reserved keyword
}

/// Macro for Panda historical keywords inline functions.
macro_rules! has_keyword {
    ($self:ident, $i:literal) => {
        $self
            .keyword_list
            .has_keyword(IdentifyKeyword::ByName($i.to_owned()))
    };
}

/// A field of a Distributed Class. The DCField struct is a base for
/// struct and dclass fields. In the DC language, there are three types
/// of field declarations, which are: plain fields, atomic, and molecular.
#[derive(Debug)]
pub struct DCField<'dc> {
    keyword_list: DCKeywordList<'dc>,
    parent_element: FieldParent<'dc>,
    field_name: String,
    field_id: globals::FieldId,
    field_type: Option<DCTypeDefinition>,
    default_value_stale: bool,
    has_default_value: bool,
    default_value: Vec<u8>, // stored as byte array
    bogus_field: bool,
}

impl<'dc> std::fmt::Display for DCField<'dc> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "TODO")
    }
}

impl<'dc> DCFileConfigAccessor for DCField<'dc> {
    fn get_dc_config(&self) -> &DCFileConfig {
        match self.parent_element {
            FieldParent::DClass(dc) => dc.get_dc_config(),
            FieldParent::Strukt(s) => s.get_dc_config(),
        }
    }
}

impl<'dc> LegacyDCHash for DCField<'dc> {
    fn generate_hash(&self, hashgen: &mut DCHashGenerator) {
        self.keyword_list.generate_hash(hashgen);
        self.field_type.clone().unwrap().generate_hash(hashgen);

        // It shouldn't be necessary to explicitly add the field ID
        // to the hash--this is computed based on the relative
        // position of this field with the other fields, so adding it
        // explicitly will be redundant. However, the field name is
        // significant.
        hashgen.add_string(self.field_name.clone());

        // The field ID is added to the hash here, since we need to
        // ensure the hash code comes out different in the
        // DC_MULTIPLE_INHERITANCE case.
        if self.get_dc_config().dc_multiple_inheritance {
            hashgen.add_int(i32::from(self.field_id));
        }
    }
}

impl<'dc> DCField<'dc> {
    #[inline(always)]
    pub fn get_field_id(&self) -> globals::FieldId {
        self.field_id
    }

    #[inline(always)]
    pub fn get_field_name(&self) -> String {
        self.field_name.clone()
    }

    /// Gets the parent DClass element reference.
    ///
    /// Panics if this field's parent element is not a DClass.
    pub fn get_dclass(&self) -> &'dc DClass {
        match self.parent_element {
            FieldParent::DClass(dclass_ref) => dclass_ref,
            FieldParent::Strukt(_) => panic!("Field parent is not a DClass."),
        }
    }

    #[inline(always)]
    pub fn set_field_id(&mut self, id: globals::FieldId) {
        self.field_id = id
    }

    #[inline(always)]
    pub fn set_field_name(&mut self, name: String) {
        self.field_name = name
    }

    pub fn set_field_type(&mut self, dtype: DCTypeDefinition) {
        self.field_type = Some(dtype);
        self.has_default_value = false;
        self.default_value = vec![];
    }

    pub fn set_field_keyword_list(&mut self, kw_list: DCKeywordList<'dc>) {
        self.keyword_list = kw_list;
    }

    pub fn set_default_value(&mut self, value: Vec<u8>) {
        self.default_value = value;
        self.has_default_value = true;
        self.default_value_stale = false;
    }

    #[inline(always)]
    pub fn set_bogus_field(&mut self, is_bogus: bool) {
        self.bogus_field = is_bogus
    }

    #[inline(always)]
    pub fn has_default_value(&self) -> bool {
        self.has_default_value
    }

    pub fn validate_ranges(&self, _packed_data: &Datagram) -> bool {
        todo!()
    }

    /// Given a blob that represents the packed data for this field, returns a
    /// string formatting it for human consumption.
    pub fn format_packed_data(
        &self,
        f: &mut std::fmt::Formatter<'_>,
        _data: &[u8],
        _show_field_names: bool,
    ) -> std::fmt::Result {
        f.write_str("TODO") // TODO
    }

    #[inline(always)]
    pub fn is_bogus_field(&self) -> bool {
        self.bogus_field
    }

    #[inline(always)]
    pub fn is_required(&self) -> bool {
        has_keyword!(self, "required")
    }

    #[inline(always)]
    pub fn is_broadcast(&self) -> bool {
        has_keyword!(self, "broadcast")
    }

    #[inline(always)]
    pub fn is_ram(&self) -> bool {
        has_keyword!(self, "ram")
    }

    #[inline(always)]
    pub fn is_db(&self) -> bool {
        has_keyword!(self, "db")
    }

    #[inline(always)]
    pub fn is_clsend(&self) -> bool {
        has_keyword!(self, "clsend")
    }

    #[inline(always)]
    pub fn is_clrecv(&self) -> bool {
        has_keyword!(self, "clrecv")
    }

    #[inline(always)]
    pub fn is_ownsend(&self) -> bool {
        has_keyword!(self, "ownsend")
    }

    #[inline(always)]
    pub fn is_ownrecv(&self) -> bool {
        has_keyword!(self, "ownrecv")
    }

    #[inline(always)]
    pub fn is_airecv(&self) -> bool {
        has_keyword!(self, "airecv")
    }

    fn _refresh_default_value(&self) {
        todo!()
    }
}