//! Implementation of [coap_message::ReadableMessage] based on a serialized message
//!
//! [Message] is the main struct of this module.

use coap_message::*;

use crate::option_iteration::*;

/// An iterator producing [MessageOption]s by running along an encoded CoAP message options stream
/// by using a [OptPayloadReader] and discarding the payload.
// FIXME make argument non-pub once coapwsmessage figures out a better way to use Message
pub struct OptionsIter<'a>(pub OptPayloadReader<'a>);

impl<'a> Iterator for OptionsIter<'a> {
    type Item = MessageOption<'a>;

    fn next(&mut self) -> Option<MessageOption<'a>> {
        match self.0.next() {
            Some(OptItem::Option { number, data }) => Some(MessageOption {
                number,
                value: data,
            }),
            Some(OptItem::Error { .. }) => panic!("coap-message has no error handling"),
            // No need to recurse or loop -- it's always the last one
            Some(OptItem::Payload(_)) => None,
            None => None,
        }
    }
}

/// A simple [coap_message::MessageOption] implementation for memory-mapped CoAP messages
pub struct MessageOption<'a> {
    number: u16,
    value: &'a [u8],
}

impl<'a> coap_message::MessageOption for MessageOption<'a> {
    fn number(&self) -> u16 {
        self.number
    }
    fn value(&self) -> &[u8] {
        self.value
    }
}

/// A CoAP message that resides in contiguous readable memory
///
/// This implementation does not attempt to do any early validation. On encoding errors discovered
/// at runtime, it simply emits the critical-and-not-safe-to-forward CoAP option 65535, which to
/// the indication indicates that something went wrong FIXME but so far it panics.
#[derive(Copy, Clone)]
pub struct Message<'a> {
    code: u8,
    options_and_payload: &'a [u8],
}

impl<'a> Message<'a> {
    pub fn new(code: u8, options_and_payload: &'a [u8]) -> Self {
        Self {
            code,
            options_and_payload,
        }
    }
}

impl<'m> ReadableMessage for Message<'m> {
    type Code = u8;
    // We *can* make them live longer -- should we? Probably doesn't change anything, but at least
    // it's teasing the compiler a bit.
    type MessageOption<'a> = MessageOption<'m>;
    type OptionsIter<'a> = OptionsIter<'m>;

    fn code(&self) -> u8 {
        self.code
    }

    // This is one of the not-most-efficient things mentioned in the module introduction: It's
    // iterating through the complete options stream on every payload call, rather than memoizing
    // its location when the options are iterated over.
    fn payload(&self) -> &[u8] {
        let empty: &[u8] = &[];

        // ... into which we'll index
        let optpayload = self.options_and_payload;

        OptPayloadReader::new(optpayload)
            .filter(|x| !matches!(x, OptItem::Option { .. }))
            .next()
            .map(|x| {
                if let OptItem::Payload(data) = x {
                    // Can't return data itself because the iterator doesn't outlive this function
                    // To be replaced when ptr_wrapping_offset_from is stabilized
                    let offset = data.as_ptr() as usize - optpayload.as_ptr() as usize;
                    &optpayload[offset..]
                } else {
                    panic!("Error before payload")
                }
            })
            .unwrap_or(&empty)
    }

    fn options(&self) -> OptionsIter<'m> {
        OptionsIter(OptPayloadReader::new(self.options_and_payload))
    }
}
