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
use serde::{Deserialize, Serialize};

use crate::data::electron::orbital;
use crate::inner::InnerElement;

use super::data::prelude::*;

#[allow(dead_code)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RawElement {
    pub name: &'static str,
    pub appearance: Option<&'static str>,
    pub atomic_mass: f64,
    pub boil: Option<f64>,
    pub category: &'static str,
    pub color: Option<&'static str>,
    pub density: Option<f64>,
    pub melt: Option<f64>,
    pub molar_heat: Option<f64>,
    pub named_by: Option<&'static str>,
    pub number: u8,
    pub period: u8,
    pub phase: &'static str,
    pub source: &'static str,
    pub spectral_img: Option<&'static str>,
    pub summary: String,
    pub symbol: &'static str,
    pub xpos: u8,
    pub ypos: u8,
    pub shells: Vec<u8>,
    pub electron_configuration: &'static str,
    pub electron_configuration_semantic: &'static str,
    pub electron_affinity: Option<f64>,
    pub electronegativity_pauling: Option<f64>,
    pub ionization_energies: Vec<f64>,
    pub cpk_hex: Option<&'static str>,
}

impl RawElement {
    pub fn into_inner(self) -> InnerElement {
        let sub_orbitals = self.electron_configuration.split(" ").map(parse_suborbital).collect::<Vec<_>>();

        let mut electron_configuration = [1u8, 2, 3, 4, 5, 6]
            .map(|i| Orbital(
                SOrbital(i, 0),
                POrbital(i, 0),
                DOrbital(i, 0),
                FOrbital(i, 0),
            ));

        sub_orbitals.iter().for_each(|so| match so.capacity() {
            2  => electron_configuration[so.orbital_number() as usize -  1].0.1 = so.electrons(),
            6  => electron_configuration[so.orbital_number() as usize -  1].1.1 = so.electrons(),
            10 => electron_configuration[so.orbital_number() as usize -  1].2.1 = so.electrons(),
            14 => electron_configuration[so.orbital_number() as usize -  1].3.1 = so.electrons(),
            cap => panic!("Invalid suborbital capacity [{cap}]"),
        });

        let electron_configuration = ElectronConfiguration(electron_configuration);

        let mut ionisation_energies = [0.0f64; 30];

        let s = ionisation_energies.as_mut_slice();

        for (idx, &item) in self.ionization_energies.iter().enumerate() {
            s[idx] = item;
        }

        let mut shells = [0u8; 8];

        let s = shells.as_mut_slice();

        for (idx, &item) in self.shells.iter().enumerate() {
            s[idx] = item;
        }

        InnerElement {
            name: self.name,
            symbol: self.symbol,
            description: Box::leak(self.summary.into_boxed_str()),
            atomic_data: AtomicData {
                atomic_number: self.number,
                nucleon_number: self.atomic_mass.round() as u16,
                atomic_mass: self.atomic_mass,
            },
            state_data: StateData {
                boiling_point: self.boil,
                melting_point: self.melt,
            },
            electron_data: ElectronData {
                electron_configuration,
                ionisation_energies,
                shells,
                electron_affinity: self.electron_affinity,
                electronegativity: self.electronegativity_pauling,
            },
        }
    }
}

pub fn parse_suborbital(s: &str) -> Box<dyn orbital::SubOrbital> {
    let mut chars = s.chars();

    let mut orbital_number = None;
    let mut suborbital_letter = None;
    let mut suborbital_fullness = 0u8;
    
    while let Some(c) = chars.next() {
        if c.is_digit(10) && suborbital_letter.is_none() {
            orbital_number = Some(c);
        } else if c.is_alphabetic() {
            suborbital_letter = Some(c);
        } else if c.is_digit(10) {
            suborbital_fullness *= 10;
            suborbital_fullness += c.to_digit(10).unwrap() as u8;
        }
    }

    if let (Some(number), Some(letter), quantity) = (orbital_number, suborbital_letter, suborbital_fullness) {
        match letter {
            's' => Box::new(orbital::SOrbital(number.to_digit(10).unwrap() as u8, quantity)),
            'p' => Box::new(orbital::POrbital(number.to_digit(10).unwrap() as u8, quantity)),
            'd' => Box::new(orbital::DOrbital(number.to_digit(10).unwrap() as u8, quantity)),
            'f' => Box::new(orbital::FOrbital(number.to_digit(10).unwrap() as u8, quantity)),
            _ => panic!("Invalid suborbital letter")
        }
    } else {
        panic!("Invalid orbital number, suborbital letter, or electron quantity");
    }
}