// Copyright 2021-2022 Ian Jackson and contributors to Hippotat
// SPDX-License-Identifier: GPL-3.0-or-later WITH LicenseRef-Hippotat-OpenSSL-Exception
// There is NO WARRANTY.

use super::*;

pub async fn run(global: Arc<Global>,
                 mut rx: mpsc::Receiver<RoutedPacket>,
                 mut ipif: Ipif) -> Result<Void,AE> {
  let r = async {
    let mut goodness: i32 = 0;
    const GOODNESS_SHIFT: u8 = 8;
    const GOODNESS_MIN: i32 = -16;

    loop {
      select!{
        biased;

        data = rx.recv() =>
        {
          let data = data.ok_or_else(|| anyhow!("rx stream end!"))?;
          let mut data = &*data.data;
          let mut slip_end = &[SLIP_END][..];
          let mut buf = Buf::chain(&mut data, &mut slip_end);
          ipif.rx.write_all_buf(&mut buf).await
            .context("write to ipif")?;
        },

        data = Ipif::next_frame(&mut ipif.tx) =>
        {
          let data = data?;
          let may_route = MayRoute::came_from_outside_hippotatd();

          goodness -= goodness >> GOODNESS_SHIFT;

          match process1(SlipNoConv, global.config.mtu, &data, |header|{
            let saddr = ip_packet_addr::<false>(header)?;
            let daddr = ip_packet_addr::<true>(header)?;
            Ok((saddr,daddr))
          }) {
            Err(PE::Empty) => { },

            Err(pe) => {
              goodness -= 1;
              error!("[good={}] invalid data from local tx ipif {}",
                     goodness, pe);
              if goodness < GOODNESS_MIN {
                throw!(anyhow!("too many bad packets, too few good ones!"))
              }
            },

            Ok((ref data, (ref saddr, ref daddr)))
            if ! global.config.vnetwork.iter().any(|n| n.contains(saddr)) => {
              // pretent as if this came from route
              trace!(
                target: "hippotatd",
 "discard to={:?} came=ipif user=local len={} outside-vnets: from={:?}",
                daddr, saddr, data.len());
            },

            Ok((data, (_saddr, daddr))) => {
              goodness += 1;
              route_packet(
                &global, "ipif", None,
                data, daddr, may_route.clone()
              ).await;
            }
          }
        },
      }
    }
  }.await;

  ipif.quitting(None).await;
  r
}
