pub mod body;
pub mod error;

use body::*;
use error::*;

use std::collections::VecDeque;

use bcd_convert::BcdNumber;
use rand::prelude::*;

const FLAGDELIMITER: u8 = 0x7E;

#[allow(dead_code)]
const PROPS_SIZE_MASK: u16 = 0x01FF;
#[allow(dead_code)]
const PROPS_SUBCONTRACT_MASK: u16 = 0x3000;
#[allow(dead_code)]
const PROPS_RESERVED_MASK: u16 = 0xC000;
#[allow(dead_code)]
const PROPS_DATAENCRYPTION_MASK: u16 = 0x0E00;

pub fn parse_inbound_msg(rawdata: &mut DataWrapper) -> std::result::Result<Message, MessageError> {
    let mut msg: Message = Message::default();

    match msg.parse_header(rawdata) {
        Ok(_) => {
            msg.check(rawdata);
        }
        Err(e) => {
            println!("error parsing header {e}");
            return Err(MessageError);
        }
    };

    Ok(msg)
}

#[derive(Default, Debug)]
pub struct DataWrapper {
    pub data: VecDeque<u8>,
    pub count: usize,
}

impl DataWrapper {
    pub fn new(data: Vec<u8>) -> DataWrapper {
        DataWrapper {
            data: data.into(),
            count: 0,
        }
    }
}

impl Iterator for DataWrapper {
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> {
        let res: Option<u8>;

        if self.count < 6 {
            res = self.data.pop_front()
        } else {
            res = None
        }
        self.count += 1;

        res
    }
}

#[derive(Default, Debug)]
pub struct Message {
    pub header: MessageHeader,
    pub content: MessageType,
    pub body: Vec<u8>,
    pub checksum: u8,
    valid: bool,
}

impl std::fmt::Display for Message {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "Message: (header: {}), content: {}, checksum: {}, valid: {}",
            self.header, self.content, self.checksum, self.valid
        )
    }
}

impl Message {
    pub fn parse_header(
        &mut self,
        rawdata: &mut DataWrapper,
    ) -> std::result::Result<(), NotOurProtocolError> {
        let mut data = rawdata.data.clone().into_iter();
        let first_byte = data.next().unwrap();
        if first_byte != FLAGDELIMITER {
            return Err(NotOurProtocolError);
        };
        let mut tmpid = vec![];
        tmpid.push(data.next().unwrap());
        tmpid.push(data.next().unwrap());
        self.header.id = u16::from_be_bytes(tmpid.try_into().unwrap());
        //self.header.id = ((data.next().unwrap() as u16) << 8) + data.next().unwrap() as u16;

        self.header.properties = ((data.next().unwrap() as u16) << 8) + data.next().unwrap() as u16;
        self.parse_properties();

        for i in 0..self.header.raw_terminal_id.len() {
            self.header.raw_terminal_id[i] = data.next().unwrap();
        }
        self.parse_terminal_id();

        self.header.serial_number =
            ((data.next().unwrap() as u16) << 8) + data.next().unwrap() as u16;

        for _ in 0..self.header.bodylength as usize {
            self.body.push(data.next().unwrap());
        }

        self.checksum = data.next().unwrap();

        if data.next().unwrap() != FLAGDELIMITER {
            return Err(NotOurProtocolError);
        }
        self.parse_body();
        Ok(())
    }

    pub fn parse_properties(&mut self) {
        self.header.bodylength = (self.header.properties & PROPS_SIZE_MASK) as u8;
        self.header.encryption =
            ((self.header.properties & PROPS_DATAENCRYPTION_MASK) as u16 >> 10) as u8;
        self.header.encrypted = self.header.encryption != 0;
        self.header.subcontract =
            ((self.header.properties & PROPS_SUBCONTRACT_MASK) as u16 >> 13) == 1;
        self.header.reserved = ((self.header.properties & PROPS_RESERVED_MASK) as u16 >> 14) as u16;
    }

    pub fn parse_terminal_id(&mut self) {
        let code = BcdNumber::try_from(&self.header.raw_terminal_id as &[u8]).unwrap();
        self.header.terminal_id = code.to_u64().unwrap();
    }

    pub fn check(&mut self, rawdata: &mut DataWrapper) {
        let mut data = rawdata.data.iter();
        data.next().unwrap();
        let mut check: u8 = 0;

        loop {
            let b = data.next().unwrap();
            if *b == FLAGDELIMITER {
                break;
            }
            check = *b ^ check;
        }

        self.validate(check);
    }

    fn validate(&mut self, check: u8) {
        self.valid = check == 0;
    }

    pub fn is_valid(&self) -> bool {
        self.valid
    }

    pub fn parse_body(&mut self) {
        match self.header.id {
            TerminalUniversalResponse::ID => {
                self.content = MessageType::TerminalUniversalResponse(
                    TerminalUniversalResponse::new(&self.body),
                )
            }
            PlatformUniversalResponse::ID => {
                self.content = MessageType::PlatformUniversalResponse(
                    PlatformUniversalResponse::new(&self.body),
                )
            }
            TerminalHeartbeat::ID => {
                self.content = MessageType::TerminalHeartbeat(TerminalHeartbeat::new(&self.body))
            }
            TerminalRegistration::ID => {
                self.content =
                    MessageType::TerminalRegistration(TerminalRegistration::new(&self.body))
            }
            TerminalRegistrationReply::ID => {
                self.content = MessageType::TerminalRegistrationReply(
                    TerminalRegistrationReply::new(&self.body),
                )
            }
            TerminalLogout::ID => {
                self.content = MessageType::TerminalLogout(TerminalLogout::new(&self.body))
            }
            TerminalAuthentication::ID => {
                self.content =
                    MessageType::TerminalAuthentication(TerminalAuthentication::new(&self.body))
            }
            TerminalParameterSetting::ID => {
                self.content =
                    MessageType::TerminalParameterSetting(TerminalParameterSetting::new(&self.body))
            }
            QueryTerminalParameter::ID => {
                self.content =
                    MessageType::QueryTerminalParameter(QueryTerminalParameter::new(&self.body))
            }
            QueryTerminalParameterResponse::ID => {
                self.content = MessageType::QueryTerminalParameterResponse(
                    QueryTerminalParameterResponse::new(&self.body),
                )
            }
            TerminalControl::ID => {
                self.content = MessageType::TerminalControl(TerminalControl::new(&self.body))
            }
            LocationInformationReport::ID => {
                self.content = MessageType::LocationInformationReport(
                    LocationInformationReport::new(&self.body),
                )
            }
            StartOfTrip::ID => {
                self.content = MessageType::StartOfTrip(StartOfTrip::new(&self.body))
            }
            EndOfTrip::ID => self.content = MessageType::EndOfTrip(EndOfTrip::new(&self.body)),
            _ => {
                self.content = MessageType::TerminalUniversalResponse(
                    TerminalUniversalResponse::new(&self.body),
                )
            }
        }
    }

    pub fn set_reply(inmsg: Message) -> Option<Message> {
        let mut reply: Message = Message::default();
        let mut rng = rand::thread_rng();
        match inmsg.content {
            MessageType::TerminalRegistration(t) => {
                let cnt = t.generate_reply(
                    inmsg.header.raw_terminal_id.into(),
                    inmsg.header.serial_number,
                );
                reply.header.id = TerminalRegistrationReply::ID;
                reply.header.properties = cnt.to_raw().len() as u16;
                reply.header.raw_terminal_id = inmsg.header.raw_terminal_id;
                reply.header.serial_number = rng.gen();
                reply.content = MessageType::TerminalRegistrationReply(cnt);
            }
            MessageType::TerminalAuthentication(t) => {
                let cnt = t.generate_reply(TerminalAuthentication::ID, inmsg.header.serial_number);
                reply.header.id = PlatformUniversalResponse::ID;
                reply.header.raw_terminal_id = inmsg.header.raw_terminal_id;
                reply.header.properties = cnt.to_raw().len() as u16;
                reply.header.serial_number = rng.gen();
                reply.content = MessageType::PlatformUniversalResponse(cnt);
            }
            _ => {
                println!("no type");
                return None;
            }
        }
        Some(reply)
    }

    pub fn to_raw(&self) -> VecDeque<u8> {
        let mut resp: VecDeque<u8> = vec![].into();

        let mut checksum: u8 = 0;

        for b in self.header.to_raw() {
            resp.push_back(b);
        }

        for b in self.content.to_raw() {
            resp.push_back(b);
        }

        for b in resp.iter() {
            checksum = checksum ^ b;
        }

        resp.push_back(checksum);

        resp.push_front(FLAGDELIMITER);
        resp.push_back(FLAGDELIMITER);
        resp
    }
}

#[derive(Default, Debug)]
pub struct MessageHeader {
    id: u16,
    properties: u16,
    pub bodylength: u8,
    encryption: u8,
    pub encrypted: bool,
    pub subcontract: bool,
    reserved: u16,
    raw_terminal_id: [u8; 6],
    pub terminal_id: u64,
    pub serial_number: u16,
}

impl MessageHeader {
    fn to_raw(&self) -> Vec<u8> {
        let mut r: Vec<u8> = vec![];
        for b in self.id.to_be_bytes() {
            r.push(b);
        }
        for b in self.properties.to_be_bytes() {
            r.push(b);
        }
        for b in self.raw_terminal_id.into_iter() {
            r.push(b);
        }
        for b in self.serial_number.to_be_bytes() {
            r.push(b);
        }
        r
    }
}

impl std::fmt::Display for MessageHeader {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "message header: id: {:X?}, length: {}, terminal id: {}, serial: {:X?}",
            self.id, self.bodylength, self.terminal_id, self.serial_number
        )
    }
}

#[derive(Debug, Clone)]
pub enum MessageType {
    None,
    TerminalUniversalResponse(TerminalUniversalResponse),
    PlatformUniversalResponse(PlatformUniversalResponse),
    TerminalHeartbeat(TerminalHeartbeat),
    TerminalRegistration(TerminalRegistration),
    TerminalRegistrationReply(TerminalRegistrationReply),
    TerminalLogout(TerminalLogout),
    TerminalAuthentication(TerminalAuthentication),
    TerminalParameterSetting(TerminalParameterSetting),
    QueryTerminalParameter(QueryTerminalParameter),
    QueryTerminalParameterResponse(QueryTerminalParameterResponse),
    TerminalControl(TerminalControl),
    LocationInformationReport(LocationInformationReport),
    StartOfTrip(StartOfTrip),
    EndOfTrip(EndOfTrip),
}

impl Default for MessageType {
    fn default() -> Self {
        Self::None
    }
}

impl BodyMessage for MessageType {
    fn to_raw(&self) -> Vec<u8> {
        let res = match self {
            MessageType::TerminalRegistrationReply(o) => o.to_raw(),
            MessageType::PlatformUniversalResponse(o) => o.to_raw(),
            //MessageType::TerminalHeartbeat(o) => o.to_raw(),
            _ => vec![],
        };
        res
    }
}

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

/*
macro_rules! generate_switch {
    ($($t:ty),*) => {
        $(
        //impl BodyMessage for $t {
        //    fn build() {}
        //    fn body_to_iter<'a>(&'a self, rawbody: &'a Vec<u8>) -> std::slice::Iter<'a, u8> {
        //        rawbody.into_iter()
        //    }
        //}
        impl $t {
            pub fn new(rawbody: &Vec<u8>) -> Self {
                let mut res = Self::default();
                res.parse(rawbody);
                res
            }
        })*
    };
}*/