#![no_std]

use core::fmt::{self, Debug, Display};

#[macro_use]
extern crate alloc;

use alloc::string::{String, ToString};
use alloc::vec::Vec;

use serde::{Deserialize, Serialize};

#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]

pub struct Attribute {
    name: String,
    value: String,
}
impl fmt::Display for Attribute {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}={}", self.name, self.value)?;
        Ok(())
    }
}

impl Attribute {
    fn to_string(&self) -> String {
        format!("{:?}={:?}", self.name, self.value)
    }
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]

// TODO Add an autotype attribute that defaults to true
pub struct XMLElement {
    pub name: String,
    pub text: String,
    pub attributes: Vec<Attribute>,
    pub children: Vec<XMLElement>,
}

impl XMLElement {
    // pub fn to_bin(&self) -> Result<Vec<u8>, XMLError> {
    //     // match bincode::serialize(&self) {
    //     //     Ok(bin) => return Ok(bin),
    //     //     Err(err) => return Err(XMLError::EncodingError(err)),
    //     // }
    // }
    pub fn from_bin(bin: Vec<u8>) -> Self {
        todo!()
    }
}

impl XMLElement {
    pub fn new<T: Display + Debug>(name: T) -> Self {
        Self {
            name: name.to_string(),
            text: String::new(),
            attributes: vec![],
            children: vec![],
        }
    }

    pub fn to_string(self) -> String {
        self.into()
    }

    pub fn set_child(&mut self, child: XMLElement) {
        // TODO: handle replacing preexisting children

        if child.name == "".to_string() {
            panic!("empty element name")
        }
        self.children.push(child);
    }

    pub fn set_attribute<T1: Display + Debug, T2: Display + Debug>(&mut self, name: T1, value: T2) {
        let new_attr = Attribute {
            name: name.to_string(),
            value: value.to_string(),
        };
        let mut duplicate_attribute = (false, 0);
        for (index, attr) in self.attributes.clone().into_iter().enumerate() {
            if attr.name == name.to_string() {
                duplicate_attribute = (true, index);
                break;
            }
        }

        match duplicate_attribute {
            (false, _) => {
                self.attributes.push(new_attr.clone());
            }
            (true, index) => {
                self.attributes[index] = new_attr.clone();
            }
        }
    }
}

impl Into<String> for XMLElement {
    fn into(self) -> String {
        let mut string = "".to_string();
        let element_open: String = format!("<{}", self.name);

        string.push_str(&element_open);
        if self.attributes.len() > 0 {
            string.push(' ');
        }
        let mut attr_count = 0;
        for attr in &self.attributes {
            string.push_str(&attr.to_string());
            attr_count += 1;

            if &attr_count != &self.attributes.len() {
                string.push(' ');
            }
        }
        string.push('>');
        string.push_str(&self.text);
        for children in &self.children.clone() {
            let chl: String = (*children).clone().into();
            string.push_str(&chl);
        }

        let element_close: String = format!("</{}>", self.name);
        string.push_str(&element_close);
        string
    }
}

#[test]
fn element_to_string_test() {
    let mut xml_ele = XMLElement::new("mouse");
    xml_ele.set_attribute("x", 0);
    xml_ele.set_attribute("x", 1);
    xml_ele.set_child(XMLElement::new("last_update"));

    let xml_str: String = xml_ele.into();
    let rhs = r##"<mouse "x"="1"><last_update></last_update></mouse>"##;

    assert_eq!(xml_str, rhs.to_string());
}
#[repr(C)]
#[derive(Debug)]
pub enum Types {
    // Used to represent an empty attribute
    None,
    Integer(Int),
    UnsignedInteger(UInt),
    Fixed(FixedPoint),
    Float(FloatingPoint),

    String(String),
}

#[derive(Debug)]
pub enum FixedPoint {
    Fixed16(bool, u8, u8),
    Fixed32(bool, u16, u16),
    Fixed64(bool, u32, u32),
    Fixed128(bool, u64, u64),
    Fixed256(bool, u128, u128),
}
#[derive(Debug)]
pub enum FloatingPoint {
    Float32(f32),
    Float64(f64),
}
#[derive(Debug)]
pub enum Int {
    I8(i8),
    I16(i16),
    I32(i32),
    I64(i64),
    I128(i128),
}
#[derive(Debug)]
pub enum UInt {
    U8(u8),
    U16(u16),
    U32(u32),
    U64(u64),
    U128(u128),
}

impl From<u64> for UInt {
    fn from(value: u64) -> Self {
        Self::U64(value)
    }
}

// #[test]
// pub fn test_string_to_num() {
//     string_to_num("-256.0")
// }

pub fn string_to_num<T: Debug + Display>(string: T) -> Result<Types, XMLError> {
    // let string = "-27".to_string(); //  `parse()` works with `&str` and `String`!
    let string: String = string.to_string();

    use FloatingPoint::*;
    use Int::*;
    use Types::{Fixed, Float, Integer, UnsignedInteger};
    use UInt::*;

    match string.parse::<u8>() {
        Ok(num) => return Ok(UnsignedInteger(U8(num))),
        Err(_) => {}
    }
    match string.parse::<u16>() {
        Ok(num) => return Ok(UnsignedInteger(U16(num))),
        Err(_) => {}
    }
    match string.parse::<u32>() {
        Ok(num) => return Ok(UnsignedInteger(U32(num))),
        Err(_) => {}
    }
    match string.parse::<u64>() {
        Ok(num) => return Ok(UnsignedInteger(U64(num))),
        Err(_) => {}
    }
    match string.parse::<u128>() {
        Ok(num) => return Ok(UnsignedInteger(U128(num))),
        Err(_) => {}
    }
    match string.parse::<i8>() {
        Ok(num) => return Ok(Integer(I8(num))),
        Err(_) => {}
    }
    match string.parse::<i16>() {
        Ok(num) => return Ok(Integer(I16(num))),
        Err(_) => {}
    }
    match string.parse::<i32>() {
        Ok(num) => return Ok(Integer(I32(num))),
        Err(_) => {}
    }
    match string.parse::<i64>() {
        Ok(num) => return Ok(Integer(I64(num))),
        Err(_) => {}
    }
    match string.parse::<i128>() {
        Ok(num) => return Ok(Integer(I128(num))),
        Err(_) => {}
    }
    match string.parse::<f32>() {
        Ok(num) => return Ok(Float(Float32(num))),
        Err(_) => {}
    }
    match string.parse::<f64>() {
        Ok(num) => return Ok(Float(Float64(num))),
        Err(_) => {}
    }

    // TODO: Decimal handler
    {
        let mut split_string = string.split(".");
        let positive;
        let mut lhs = split_string.next().unwrap();

        // println!("{:?}", lhs.chars().next());
        let mut lhs_median: Vec<char> = lhs.chars().collect();

        if lhs.chars().next() == Some('-') {
            // lhs
            positive = false;
            lhs_median.remove(0);
            // println!("A {:?}", lhs_median);
        } else {
            positive = true;
        }

        // println!("{:?}", lhs);
        let lhs_median_2 = lhs_median.iter().cloned().collect::<String>();
        let mut lhs_final: Types = Types::None;
        let abc = string_to_num(lhs_median_2);

        // println!("{:?}", lhs_final);
        let rhs = split_string.next().unwrap();
        // println!("{:?}", rhs);
        let xyz = string_to_num(rhs);
        // println!("{:?}", abc);
        // println!("{:?}", xyz);

        // return Types::Fixed();
    }

    Err(XMLError::TypeNotANumber)
}
#[derive(Debug)]
pub enum XMLError {
    TypeNotANumber,
    // EncodingError(bincode::Error),
    // DecodingError(bincode::Error),
}