prns-core 0.3.4

Pure Reticulum engine and wire contract for Personal Reticulum
use alloc::vec::Vec;

use super::super::{HeldAnnounce, HeldAnnounceTable, HeldFull, MAX_HELD_ANNOUNCES_PER_INTERFACE};
use crate::interfaces::InterfaceId;
use crate::routing::announce::stored::AppDataHandle;
use crate::wire::DestinationHash;

#[derive(Debug, Default)]
pub struct HeapHeldAnnounceTable {
    interfaces: Vec<InterfaceHeld>,
}

#[derive(Debug)]
struct InterfaceHeld {
    interface: InterfaceId,
    held: Vec<HeldAnnounce>,
}

impl HeldAnnounceTable for HeapHeldAnnounceTable {
    type Slot = (usize, usize);

    fn find(&self, interface: InterfaceId, destination: DestinationHash) -> Option<(usize, usize)> {
        let iface = self
            .interfaces
            .iter()
            .position(|held| held.interface == interface)?;
        let slot = self.interfaces[iface]
            .held
            .iter()
            .position(|record| record.destination == destination)?;
        Some((iface, slot))
    }

    fn app_data_handle(&self, (iface, slot): (usize, usize)) -> Option<AppDataHandle> {
        self.interfaces[iface].held[slot]
            .announce
            .maybe_app_data_handle
    }

    fn overwrite(&mut self, (iface, slot): (usize, usize), record: HeldAnnounce) {
        self.interfaces[iface].held[slot] = record;
    }

    fn insert(&mut self, record: HeldAnnounce) -> Result<(), HeldFull> {
        match self
            .interfaces
            .iter_mut()
            .find(|held| held.interface == record.receiving_interface)
        {
            Some(held) => {
                if held.held.len() >= MAX_HELD_ANNOUNCES_PER_INTERFACE {
                    return Err(HeldFull::InterfaceAtCap);
                }
                held.held.push(record);
            }
            None => self.interfaces.push(InterfaceHeld {
                interface: record.receiving_interface,
                held: alloc::vec![record],
            }),
        }
        Ok(())
    }

    fn take_lowest_hop_for(&mut self, interface: InterfaceId) -> Option<HeldAnnounce> {
        let iface = self
            .interfaces
            .iter()
            .position(|held| held.interface == interface)?;
        let slot = self.interfaces[iface]
            .held
            .iter()
            .enumerate()
            .min_by_key(|(_, record)| record.hops)
            .map(|(i, _)| i)?;
        let record = self.interfaces[iface].held.swap_remove(slot);
        if self.interfaces[iface].held.is_empty() {
            self.interfaces.swap_remove(iface);
        }
        Some(record)
    }

    fn drop_interface(
        &mut self,
        interface: InterfaceId,
        mut on_removed: impl FnMut(Option<AppDataHandle>),
    ) {
        let Some(iface) = self
            .interfaces
            .iter()
            .position(|held| held.interface == interface)
        else {
            return;
        };
        for record in &self.interfaces[iface].held {
            on_removed(record.announce.maybe_app_data_handle);
        }
        self.interfaces.swap_remove(iface);
    }

    fn interfaces(&self) -> impl Iterator<Item = InterfaceId> + '_ {
        self.interfaces.iter().map(|held| held.interface)
    }

    fn len_for(&self, interface: InterfaceId) -> usize {
        self.interfaces
            .iter()
            .find(|held| held.interface == interface)
            .map_or(0, |held| held.held.len())
    }

    fn len(&self) -> usize {
        self.interfaces.iter().map(|held| held.held.len()).sum()
    }
}