mod arbitrary;
mod comment;
mod destination;
mod gps;
mod identification;
mod node_info;
mod route;
mod timestamp;
mod unknown;

use crate::error::DecodeError;

pub use self::{
    arbitrary::Arbitrary,
    comment::Comment,
    destination::Destination,
    gps::Gps,
    identification::Identification,
    node_info::{NodeInfo, NodeInfoBuilder},
    route::{PastHop, Route, RouteHop, RouteIter},
    timestamp::Timestamp,
    unknown::Unknown,
};

pub(crate) const IDENTIFICATION_TYPE: u8 = 0x00;
pub(crate) const TIMESTAMP_TYPE: u8 = 0x01;
pub(crate) const GPS_TYPE: u8 = 0x02;
pub(crate) const COMMENT_TYPE: u8 = 0x03;
pub(crate) const ROUTE_TYPE: u8 = 0x04;
pub(crate) const DESTINATION_TYPE: u8 = 0x05;
pub(crate) const ARBITRARY_TYPE: u8 = 0x06;
pub(crate) const NODE_INFO_TYPE: u8 = 0x09;

#[derive(Debug)]
pub enum Whisker {
    Identification(Identification),
    Timestamp(Timestamp),
    Gps(Gps),
    Comment(Comment),
    Route(Route),
    Destination(Destination),
    Arbitrary(Arbitrary),
    NodeInfo(NodeInfo),
    Unknown(Unknown),
}

pub struct WhiskerIter<'a> {
    data: &'a [u8],
    i: usize,

    // for detecting duplicates
    has_ident: bool,
    has_timestamp: bool,
    has_gps: bool,
    has_route: bool,
}

impl<'a> WhiskerIter<'a> {
    pub fn new(data: &'a [u8]) -> Self {
        Self {
            data,
            i: 0,
            has_ident: false,
            has_timestamp: false,
            has_gps: false,
            has_route: false,
        }
    }

    fn decode(&mut self) -> Result<Whisker, DecodeError> {
        let position = self.i;

        let t = *self
            .data
            .get(self.i)
            .ok_or(DecodeError::UnexpectedEndOfInput)?;
        let len = usize::from(
            *self
                .data
                .get(self.i + 1)
                .ok_or(DecodeError::UnexpectedEndOfInput)?,
        );

        let data = self
            .data
            .get((self.i + 1)..(self.i + 2 + len))
            .ok_or(DecodeError::UnexpectedEndOfInput)?;

        self.i += len + 2;

        let out = match t {
            IDENTIFICATION_TYPE => {
                if self.has_ident {
                    return Err(DecodeError::DuplicateWhisker { position });
                }

                self.has_ident = true;

                Whisker::Identification(
                    Identification::decode(data)
                        .ok_or(DecodeError::MalformedWhisker { position })?,
                )
            }
            TIMESTAMP_TYPE => {
                if self.has_timestamp {
                    return Err(DecodeError::DuplicateWhisker { position });
                }

                self.has_timestamp = true;

                Whisker::Timestamp(
                    Timestamp::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
                )
            }
            GPS_TYPE => {
                if self.has_gps {
                    return Err(DecodeError::DuplicateWhisker { position });
                }

                self.has_gps = true;

                Whisker::Gps(Gps::decode(data).ok_or(DecodeError::MalformedWhisker { position })?)
            }
            COMMENT_TYPE => Whisker::Comment(
                Comment::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
            ),
            ROUTE_TYPE => {
                if self.has_route {
                    return Err(DecodeError::DuplicateWhisker { position });
                }

                self.has_route = true;

                Whisker::Route(
                    Route::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
                )
            }
            DESTINATION_TYPE => Whisker::Destination(
                Destination::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
            ),
            ARBITRARY_TYPE => Whisker::Arbitrary(
                Arbitrary::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
            ),
            NODE_INFO_TYPE => Whisker::NodeInfo(
                NodeInfo::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
            ),

            // safe to unwrap because we know len has to be 255 or less, since it's a u8
            whisker_type => {
                Whisker::Unknown(Unknown::new(whisker_type, data[1..].try_into().unwrap()))
            }
        };

        Ok(out)
    }
}

impl Iterator for WhiskerIter<'_> {
    type Item = Result<Whisker, DecodeError>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i == self.data.len() {
            return None;
        }

        Some(self.decode())
    }
}

pub struct ValidatedWhiskerIter<'a> {
    iter: WhiskerIter<'a>,
}

impl<'a> ValidatedWhiskerIter<'a> {
    pub(crate) fn new(data: &'a [u8]) -> Self {
        Self {
            iter: WhiskerIter::new(data),
        }
    }
}

impl Iterator for ValidatedWhiskerIter<'_> {
    type Item = Whisker;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|w| w.unwrap())
    }
}

#[cfg(test)]
mod tests {
    use half::f16;

    use crate::{
        identity::Identity,
        whisker::route::{PastHop, RouteHop},
    };

    use super::*;

    #[test]
    fn ident_e2e() {
        let icon = 2738;
        let call = "VE9ABCDEFGZZ4839-???";
        let ssid = 17;

        let ident = Identification::new(call, ssid, icon).unwrap();
        let mut buf = [0; 256];

        let encoded = ident.encode(&mut buf).unwrap();
        let decoded = Identification::decode(encoded).unwrap();
        assert_eq!(ident, decoded);
        assert_eq!(call, &decoded.callsign);
        assert_eq!(ssid, decoded.ssid);
    }

    #[test]
    fn new_timestamp() {
        let t = 34894;
        assert_eq!(t, Timestamp::new(t).unwrap().unix_time());

        let t = 1 << (5 * 8);
        assert_eq!(None, Timestamp::new(t));
    }

    #[test]
    fn timestamp_e2e() {
        let t = 34894;
        let timestamp = Timestamp::new(t).unwrap();

        let mut buf = [0; 256];
        let encoded = timestamp.encode(&mut buf).unwrap();
        let decoded = Timestamp::decode(encoded).unwrap();
        assert_eq!(timestamp, decoded);
        assert_eq!(t, timestamp.unix_time());
    }

    #[test]
    fn new_gps() {
        let gps = Gps::new(
            90.0,
            -180.0,
            f16::from_f32(45.02),
            24,
            123.45,
            f16::from_f32(12.3),
        );

        assert_eq!(89.99899999704212, gps.latitude());
        assert_eq!(-179.9989999551326, gps.longitude());
        assert_eq!(123.75, gps.heading());
    }

    #[test]
    fn gps_max_heading() {
        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            359.0,
            f16::from_f32(12.3),
        );

        assert_eq!(358.59375, gps.heading());

        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            957.47,
            f16::from_f32(12.3),
        );

        assert_eq!(957.65625 - 360.0 * 2.0, gps.heading());
    }

    #[test]
    fn gps_min_heading() {
        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            0.0,
            f16::from_f32(12.3),
        );

        assert_eq!(0.0, gps.heading());

        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            -22.0,
            f16::from_f32(12.3),
        );

        assert_eq!(337.5, gps.heading());

        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            -1206.0,
            f16::from_f32(12.3),
        );

        assert_eq!(233.4375, gps.heading());
    }

    #[test]
    fn gps_e2e() {
        let gps = Gps::new(
            90.0,
            -180.0,
            f16::from_f32(45.02),
            24,
            123.45,
            f16::from_f32(12.3),
        );

        let mut buf = [0; 256];
        let encoded = gps.encode(&mut buf).unwrap();
        let decoded = Gps::decode(encoded).unwrap();
        assert_eq!(gps, decoded);
    }

    #[test]
    fn comment_e2e() {
        let data = b"Hello world! This is an example comment";
        let comment = Comment::new(data).unwrap();
        let mut buf = [0; 256];
        let encoded = comment.encode(&mut buf).unwrap();
        let decoded = Comment::decode(encoded).unwrap();
        let decoded2 = Comment::decode(&buf).unwrap();
        assert_eq!(data[..], decoded.internal_data()[..]);
        assert_eq!(data[..], decoded2.internal_data()[..]);
    }

    #[test]
    fn route_push_and_iter() {
        let mut route = Route::new(34);
        route
            .push_past(PastHop::new(Identity::new("VE2XYZ", 23), None))
            .unwrap();
        route.push_internet().unwrap();
        route.push_internet().unwrap();
        route.push_internet().unwrap();
        route.push_future(Identity::new("VE9AAAAA", 94)).unwrap();
        // past after future - not allowed
        assert!(route
            .push_past(PastHop::new(Identity::new("VE9AAAAA", 94), None))
            .is_none());

        // too long
        assert!(route
            .push_future(Identity::new(
                "lsdfjslkdfjlksdjflksfjsdklfjsdklfjsdklfjsdklfjsklfsef;jklsdfjkl;sdf;klsdf;klsjdfJSDJFSKL:DFJDSL:KFskldfj;slkdfjsdkl;fjdskl;fjsdfl;kjsdfl;ksdjfkl;ssdfl;kjsdfl;ksdjf;sdklsd;lfkjsdlfk;jsdl;fkjsd;klfjsd;fljsf;oidfjgwper0tujdfgndfjkl;gjnergjol;kehfgo;dijge;oghdfkl;gjdfkl;gjdeior;lgjedr;ioghjdorighndeklo;grjiop[",
                20,
            )).is_none());

        route
            .push_future(Identity::new("This is the last callsign", 0))
            .unwrap();
        route.push_internet().unwrap();

        let mut iter = route.iter();
        assert_eq!(
            RouteHop::Past(PastHop::new(Identity::new("VE2XYZ", 23), None)),
            iter.next().unwrap()
        );
        assert_eq!(RouteHop::Internet, iter.next().unwrap());
        assert_eq!(RouteHop::Internet, iter.next().unwrap());
        assert_eq!(RouteHop::Internet, iter.next().unwrap());
        assert_eq!(
            RouteHop::Future(Identity::new("VE9AAAAA", 94)),
            iter.next().unwrap()
        );
        assert_eq!(
            RouteHop::Future(Identity::new("This is the last callsign", 0)),
            iter.next().unwrap()
        );
        assert_eq!(RouteHop::Internet, iter.next().unwrap());
        assert_eq!(None, iter.next());

        assert_eq!(34, route.max_hops);
    }

    #[test]
    fn route_e2e() {
        let mut route = Route::new(34);
        route
            .push_past(PastHop::new(Identity::new("VE2XYZ", 23), Some(0.0)))
            .unwrap();
        route.push_internet().unwrap();
        route.push_internet().unwrap();
        route.push_internet().unwrap();
        route.push_future(Identity::new("VE9AAAAA", 94)).unwrap();
        route
            .push_future(Identity::new("This is the last callsign", 0))
            .unwrap();
        route.push_internet().unwrap();

        let mut buf = [0; 256];
        let encoded = route.encode(&mut buf).unwrap();
        let decoded = Route::decode(encoded).unwrap();
        assert_eq!(route, decoded);
    }

    // verify examples in the standard doc
    #[test]
    fn route_doc_examples() {
        let mut ex1 = Route::new(4);
        ex1.push_past(PastHop::new(Identity::new("VE1ABC", 0), Some(-96.0)))
            .unwrap();
        ex1.push_past(PastHop::new(Identity::new("VE2DEF", 234), Some(-13.0)))
            .unwrap();
        ex1.push_past(PastHop::new(Identity::new("VE3XYZ", 14), Some(-106.0)))
            .unwrap();

        let mut buf = [0; 256];
        let encoded = ex1.encode(&mut buf).unwrap();
        assert_eq!(
            &[
                0x1C, 0x04, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x60, 0x56, 0x45, 0x32,
                0x44, 0x45, 0x46, 0xFF, 0xEA, 0xDC, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, 0xFF, 0x0E,
                0x51
            ],
            &encoded
        );

        let mut ex1_5 = Route::new(4);
        ex1_5
            .push_past(PastHop::new(Identity::new("VE1ABC", 0), Some(-96.0)))
            .unwrap();
        ex1_5.push_future(Identity::new("VE2DEF", 234)).unwrap();
        ex1_5.push_future(Identity::new("VE3XYZ", 14)).unwrap();

        let mut buf = [0; 256];
        let encoded = ex1_5.encode(&mut buf).unwrap();
        assert_eq!(
            &[
                0x1A, 0x04, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x60, 0x56, 0x45, 0x32,
                0x44, 0x45, 0x46, 0xFD, 0xEA, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, 0xFD, 0x0E,
            ],
            &encoded
        );

        let mut ex2 = Route::new(3);
        ex2.push_past(PastHop::new(Identity::new("VE1ABC", 0), None))
            .unwrap();
        ex2.push_internet();
        ex2.push_past(PastHop::new(Identity::new("VE2DEF", 234), Some(-86.5)))
            .unwrap();
        ex2.push_internet();
        ex2.push_past(PastHop::new(Identity::new("VE3XYZ", 14), Some(-65.0)))
            .unwrap();

        let encoded = ex2.encode(&mut buf).unwrap();
        assert_eq!(
            &[
                0x1E, 0x03, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x00, 0xFE, 0x56, 0x45,
                0x32, 0x44, 0x45, 0x46, 0xFF, 0xEA, 0x6E, 0xFE, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A,
                0xFF, 0x0E, 0x8E
            ],
            &encoded
        );

        let mut ex3 = Route::new(0);
        ex3.push_past(PastHop::new(Identity::new("VE1ABC", 0), Some(-42.5)))
            .unwrap();
        ex3.push_internet();
        ex3.push_internet();

        let encoded = ex3.encode(&mut buf).unwrap();
        assert_eq!(
            &[0x0C, 0x00, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0xB0, 0xFE, 0xFE],
            &encoded
        );
    }

    #[test]
    fn dest_e2e() {
        assert_eq!(None, Destination::new(true, 0, "dest", 36));
        assert_eq!(None, Destination::new(false, 128, "abc", 0));
        assert!(Destination::new(false, 0, "anc", 8).is_some());

        let dest = Destination::new(true, 64, "mrow", 31).unwrap();
        let mut buf = [0; 256];
        let encoded = dest.encode(&mut buf).unwrap();
        let decoded = Destination::decode(encoded).unwrap();
        assert_eq!(dest, decoded);
    }

    #[test]
    fn dest_ack() {
        let dest = Destination::new(true, 84, "abc", 17).unwrap();
        assert_eq!(84, dest.ack_num());
        assert!(dest.is_ack());
        assert_eq!("abc", dest.callsign());
        assert_eq!(17, dest.ssid());
    }

    #[test]
    fn arbitrary_e2e() {
        let data = b"Hello world! This is an example comment";
        let arbitrary = Arbitrary::new(data).unwrap();
        let mut buf = [0; 256];
        let encoded = arbitrary.encode(&mut buf).unwrap();
        let decoded = Arbitrary::decode(encoded).unwrap();
        let decoded2 = Arbitrary::decode(&buf).unwrap();
        assert_eq!(data[..], decoded.0[..]);
        assert_eq!(data[..], decoded2.0[..]);
    }
}