use crc::{Crc, CRC_16_IBM_3740};

use crate::ldpc::ldpc_encode;

const TEXT_MESSAGE_LEN: usize = 252;
const FEC_PACKET_LEN: usize = 256 + 2 + 65;

pub enum Packet {
    TextMessage(u8, [u8; TEXT_MESSAGE_LEN]),
}

impl Packet {
    // Cuts off messages that are too long
    pub fn new_text_message(msg: &str) -> Self {
        let mut out = [0x55; TEXT_MESSAGE_LEN];

        // this technically would allow part of a character to be sent
        // (unicode multi-byte character that gets cut off at the end)
        // ideally, we would iterate over characters and add them, so that
        // we could abort before adding a partial character
        for (i, b) in msg.as_bytes().iter().enumerate().take(TEXT_MESSAGE_LEN) {
            out[i] = *b;
        }

        let len = msg
            .as_bytes()
            .len()
            .min(TEXT_MESSAGE_LEN)
            .try_into()
            .unwrap();

        Self::TextMessage(len, out)
    }
}

impl Packet {
    // increments text_id if we're sending a text message packet
    pub fn into_raw(self, text_id: &mut u16) -> RawPacket {
        match self {
            Packet::TextMessage(len, txt) => {
                let id = *text_id;
                *text_id = text_id.wrapping_add(1);

                let mut out = [0; 256];

                out[0] = 0x00; // packet type
                out[1] = len;
                out[2..4].clone_from_slice(&id.to_be_bytes());
                out[4..].clone_from_slice(&txt);

                RawPacket(out)
            }
        }
    }
}

pub struct RawPacket(pub [u8; 256]);

pub struct FecPacket(pub [u8; FEC_PACKET_LEN]);

impl From<RawPacket> for FecPacket {
    fn from(value: RawPacket) -> Self {
        let crc = Crc::<u16>::new(&CRC_16_IBM_3740);

        let checksum = crc.checksum(&value.0);

        let mut data = [0; 256 + 2 + 65];
        data[0..256].copy_from_slice(&value.0);
        data[256..258].copy_from_slice(&checksum.to_le_bytes());

        ldpc_encode(&mut data);

        FecPacket(data)
    }
}