mod connection;
mod handler;
use bstr::BString;
use num_traits::FromPrimitive;
use std::{
    net::{SocketAddr, SocketAddrV4},
    time::Instant,
};
use tokio::net::UdpSocket;
use tracing::{debug, error, info, warn};
use crate::{RakPeerConfig, ID};
pub use connection::RemoteSystem;
pub use handler::PacketHandler;
pub struct RakPeer<H> {
    handler: H,
    socket: UdpSocket,
    local: SocketAddrV4,
}
impl<H: PacketHandler> RakPeer<H> {
    pub async fn new(local: SocketAddrV4, handler: H) -> Result<Self, tokio::io::Error> {
        let socket = UdpSocket::bind(local).await?;
        info!("Server started on {:?}", local);
        Ok(Self {
            socket,
            local,
            handler,
        })
    }
    pub async fn run(&mut self, password: String) -> Result<(), tokio::io::Error> {
        let start = Instant::now();
        let mut buf = vec![0; 2048];
        let mut connections: Vec<RemoteSystem> = Vec::new();
        let peer_config = RakPeerConfig {
            max_incoming_connections: 10,
            incoming_password: BString::from(password),
        };
        loop {
            for conn in &mut connections {
                conn.update(&self.socket).await?;
            }
            let (length, remote) = self.socket.recv_from(&mut buf).await?;
            let origin = match remote {
                SocketAddr::V4(v4) => v4,
                SocketAddr::V6(_v6) => {
                    eprintln!("IPv6 not supported");
                    continue;
                }
            };
            let bytes = &buf[..length];
            let conn = connections.iter_mut().find(|x| x.addr == origin);
            debug!("{} bytes from {}", bytes.len(), origin);
            if let Some(connection) = conn {
                connection.on_packet(
                    bytes,
                    self.local,
                    Instant::now().duration_since(start),
                    &peer_config,
                    &mut self.handler,
                );
                connection.update(&self.socket).await?;
                if connection.queue.is_empty() && connection.pending_disconnect() {
                    if let Some(index) = connections.iter_mut().position(|x| x.addr == origin) {
                        connections.remove(index);
                    } else {
                        warn!("Failed to find connection {} to remove!", origin);
                    }
                }
            } else {
                let id_byte = match ID::of_packet(bytes) {
                    Ok(b) => b,
                    Err(e) => {
                        error!("{}", e);
                        continue;
                    }
                };
                match ID::from_u8(id_byte) {
                    Some(id) => {
                        debug!("raw: {:?}", id);
                        match id {
                            ID::OpenConnectionRequest => {
                                let reply = if true {
                                    connections.push(RemoteSystem::new(origin));
                                    ID::OpenConnectionReply
                                } else {
                                    ID::NoFreeIncomingConnections
                                };
                                self.socket.send_to(&[reply as u8, 0], origin).await?;
                            }
                            _ => debug!("bytes: {:?}", &bytes[1..]),
                        }
                    }
                    None => error!("Missing or invalid first byte from {}: {:?}", origin, bytes),
                }
            }
        }
    }
}