use std::{
	sync::Arc,
};

use futures::StreamExt;
use tokio::sync::Mutex;

use opengl_rust::{
	network_protocol::*,
	quinn_common::make_server_endpoint,
};

struct ConnectedPlayer {
	connection: quinn::Connection,
	cmd: NetworkCommand,
	position: (f32, f32),
}

#[tokio::main]
async fn main () -> anyhow::Result <()> {
	let server_addr = "127.0.0.1:5000".parse().unwrap();
	let (mut incoming, server_cert) = make_server_endpoint (server_addr)?;
	tokio::fs::write ("quic_server.crt", &server_cert).await?;
	
	let connected_players = Arc::new (Mutex::new (vec! []));
	
	{
		let connected_players = Arc::clone (&connected_players);
		
		tokio::spawn (async move {
			let mut player_seq = 0;
			
			while let Some (incoming_conn) = incoming.next ().await {
				let new_conn = incoming_conn.await?;
				println! (
					"[server] connection accepted: addr={}",
					new_conn.connection.remote_address ()
				);
				
				let quinn::NewConnection {
					connection,
					mut datagrams,
					..
				} = new_conn;
				
				{
					let mut guard = connected_players.lock ().await;
					guard.push (ConnectedPlayer {
						connection,
						cmd: NetworkCommand::default (),
						position: (32.0, 32.0),
					});
				}
				
				// Read player commands
				{
					let player_seq = player_seq;
					let connected_players = Arc::clone (&connected_players);
					
					tokio::spawn (async move {
						while let Some (Ok (datagram)) = datagrams.next ().await {
							let cmd: NetworkCommand = rmp_serde::from_slice (&datagram)?;
							let mut guard = connected_players.lock ().await;
							guard [player_seq].cmd = cmd;
						}
						
						Ok::<_, anyhow::Error> (())
					});
				}
				
				player_seq += 1;
			}
			
			Ok::<_, anyhow::Error> (())
		});
	}
	
	let mut time_step = opengl_rust::timestep::TimeStep::new (120, 1000);
	let mut state = NetworkedState {
		positions: vec! [
			(32.0, 32.0),
		],
	};
	
	let mut i = 0_u64;
	
	loop {
		let frames_to_do = time_step.step ();
		
		let state;
		{
			let mut guard = connected_players.lock ().await;
			for _ in 0..frames_to_do {
				let speed = 0.25;
				
				for player in &mut guard [..] {
					let cmd = &player.cmd;
					let mut pos = &mut player.position; 
					
					if cmd.left {
						pos.0 -= speed;
					}
					if cmd.right {
						pos.0 += speed;
					}
					if cmd.up {
						pos.1 += speed;
					}
					if cmd.down {
						pos.1 -= speed;
					}
				}
			}
			
			state = NetworkedState {
				positions: guard.iter ()
				.map (|player| player.position)
				.collect (),
			};
			
			let bytes = bytes::Bytes::from (rmp_serde::to_vec (&state)?);
			
			for player in &guard [..] {
				player.connection.send_datagram (bytes.clone ())?;
			}
			
			i += 1;
		}
		
		tokio::time::sleep (std::time::Duration::from_millis(16)).await;
	}
	
    Ok(())
}
