pub trait BodyMessage {
    fn build() {}
    fn body_to_iter<'a>(&'a self, rawbody: &'a Vec<u8>) -> std::slice::Iter<'a, u8> {
        rawbody.into_iter()
    }
    fn print(&mut self, rawbody: &Vec<u8>) {
        println!("{:?}", rawbody);
    }
}

macro_rules! generate_impl {
    ($($t:ty),*) => {
        $(
        impl BodyMessage for $t {
            fn build() {}
        }
        impl $t {
            pub fn new(rawbody: &Vec<u8>) -> Self {
                let mut res = Self::default();
                res.parse(rawbody);
                res
            }
        })*
    };
}

#[derive(Default, Debug)]
pub struct TerminalUniversalResponse {
    pub answer_serial_no: u16,
    pub answer_id: u16,
    pub result: u8,
}

impl TerminalUniversalResponse {
    pub const ID: u16 = 0x0001;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
    pub fn debug() {}
}

#[derive(Default, Debug)]
pub struct PlatformUniversalResponse {
    pub answer_serial_no: u16,
    pub answer_id: u16,
    pub result: u8,
}

impl PlatformUniversalResponse {
    pub const ID: u16 = 0x8001;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct TerminalHeartbeat {}

impl TerminalHeartbeat {
    pub const ID: u16 = 0x0002;
    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct TerminalRegistration {
    pub provincial_id: u16,
    pub city_and_prefecture_id: u16,
    pub manufacturer_id: [u8; 5],
    pub terminal_models: [u8; 20], //8 or 20 bytes
    pub terminal_id: [u8; 7],
    pub license_plate_color: u8,
    pub license_plate: String,
}

impl TerminalRegistration {
    pub const ID: u16 = 0x0100;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {
        let mut bd = rawbody.into_iter(); //self.body_to_iter(rawbody);
        self.provincial_id = u16::from_be_bytes(
            vec![*bd.next().unwrap(), *bd.next().unwrap()]
                .try_into()
                .unwrap(),
        );
        self.city_and_prefecture_id = u16::from_be_bytes(
            vec![*bd.next().unwrap(), *bd.next().unwrap()]
                .try_into()
                .unwrap(),
        );
        for i in 0..self.manufacturer_id.len() {
            self.manufacturer_id[i] = *bd.next().unwrap();
        }
        for i in 0..self.terminal_models.len() {
            self.terminal_models[i] = *bd.next().unwrap();
        }
        for i in 0..self.terminal_id.len() {
            self.terminal_id[i] = *bd.next().unwrap();
        }
        self.license_plate_color = *bd.next().unwrap();

        while let Some(i) = bd.next() {
            let c = char::from(*i);
            self.license_plate.push(c);
        }
    }
    pub fn generate_reply(&self, terminal_id: Vec<u8>, serial: u16) -> TerminalRegistrationReply {
        let mut res = TerminalRegistrationReply::default();
        res.answer_serial_no = serial;
        res.result = TerminalRegistrationResult::Success as u8;
        res.authentication_code = String::from_utf8(terminal_id).unwrap();
        res
    }
}

impl std::fmt::Display for TerminalRegistration {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "provincial: {}, city/pref: {}, manufacturer: {:?}, model: {:?}, terminal id: {}, license_plate_color: {}, license_plate: {}",
            self.provincial_id,
            self.city_and_prefecture_id,
            self.manufacturer_id,
            self.terminal_models,
            String::from_utf8(self.terminal_id.try_into().unwrap()).unwrap(),
            self.license_plate_color,
            self.license_plate,
        )
    }
}

enum TerminalRegistrationResult {
    Success = 0x00,
    VehicleRegistered,
    VehicleNotInDatabase,
    TerminalRegistered,
    VehicleNotInDatabase2,
}

#[derive(Default, Debug)]
pub struct TerminalRegistrationReply {
    pub answer_serial_no: u16,
    pub result: u8,
    pub authentication_code: String,
}

impl TerminalRegistrationReply {
    pub const ID: u16 = 0x8100;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
    pub fn body_to_vec(&self) -> Vec<u8> {
        let mut res: Vec<u8> = vec![];
        for b in self.answer_serial_no.to_be_bytes() {
            res.push(b);
        }
        res.push(self.result);
        for b in self.authentication_code.as_bytes() {
            res.push(*b);
        }
        res
    }
}

#[derive(Default, Debug)]
pub struct TerminalLogout {}

impl TerminalLogout {
    pub const ID: u16 = 0x0003;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

impl std::fmt::Display for TerminalLogout {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}

#[derive(Default, Debug)]
pub struct TerminalAuthentication {
    pub authentication_code: String,
}

impl TerminalAuthentication {
    pub const ID: u16 = 0x0102;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct TerminalParameterSetting {
    pub total_parameters: u8,
    pub parameters: Vec<TerminalParameterData>,
}

impl TerminalParameterSetting {
    pub const ID: u16 = 0x8103;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct TerminalParameterData {
    pub parameter_id: u32,
    pub parameter_length: u8,
    pub parameter_value: u8,
}

impl TerminalParameterData {
    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct QueryTerminalParameter {}
impl QueryTerminalParameter {
    pub const ID: u16 = 0x8104;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct QueryTerminalParameterResponse {
    pub response_serial_no: u16,
    pub response_parameter_quantity: u16,
    pub parameters: Vec<TerminalParameterData>,
}

impl QueryTerminalParameterResponse {
    pub const ID: u16 = 0x0104;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct TerminalControl {
    pub command_word: u8,
    pub command_parameter: String,
}

impl TerminalControl {
    pub const ID: u16 = 0x8105;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct LocationInformationReport {
    pub alert_mark: u32,
    pub status: u32,
    pub latitude: u32,
    pub longitude: u32,
    pub height: u32,
    pub speed: u32,
    pub direction: u32,
    pub time: u32,
}

impl LocationInformationReport {
    pub const ID: u16 = 0x0200;
    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct StartOfTrip {}
impl StartOfTrip {
    pub const ID: u16 = 0x0202;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

#[derive(Default, Debug)]
pub struct EndOfTrip {}
impl EndOfTrip {
    pub const ID: u16 = 0x0203;

    pub fn parse(&mut self, rawbody: &Vec<u8>) {}
}

generate_impl!(
    TerminalUniversalResponse,
    PlatformUniversalResponse,
    TerminalHeartbeat,
    TerminalRegistration,
    TerminalRegistrationReply,
    TerminalLogout,
    TerminalAuthentication,
    TerminalParameterSetting,
    TerminalParameterData,
    QueryTerminalParameter,
    QueryTerminalParameterResponse,
    TerminalControl,
    LocationInformationReport,
    StartOfTrip,
    EndOfTrip
);