Documentation
//really rust compiler?? really?
#![allow(static_mut_refs)]
use crate::fs::file::File;
use crate::linux;
use crate::sync::once::OnceLock;
use core::cell::OnceCell;
use core::pin::Pin;
use core::task::Waker;
use core::{cell::RefCell, marker::PhantomData};

use crate::fs::*;
use crate::net::*;
use alloc::boxed::Box;

#[thread_local]
static mut RT: OnceCell<OptimalRuntime> = OnceCell::new();

type OptimalRuntime = linux::runtime::Runtime;

pub trait Share {
    fn spawn<F, R>(&self, future: F) -> Join<R>
    where
        F: Future<Output = R> + Send + 'static,
        R: Send + 'static;
}

pub trait Runtime<S: Share>: 'static + Sized {
    type TcpStream: tcp::Stream<Self, S>;
    type TcpListener: tcp::Listener<Self, S>;
    type UdpSocket: udp::Socket<Self, S>;
    type UdpEndpoint: udp::Endpoint<Self, S>;
    type File: File<Self, S>;
    type FileSystem: FileSystem<Self, S>;

    fn for_platform() -> Self
    where
        Self: Sized;
    fn share(&self) -> &S;

    fn spawn_local<F, R>(&self, future: F) -> Join<R>
    where
        F: Future<Output = R> + 'static,
        R: 'static;
    fn block_on<F>(&self, future: F) -> F::Output
    where
        F: Future;
}

pub struct Join<Ret>(u64, PhantomData<Ret>);

pub(crate) fn rt() -> &'static OptimalRuntime {
    /*
    SAFETY:
    1) the static mut is thread local thus,
    (meaning only one caller can exist in time)

    2) only one reference may be called with at once thus,
    (meaning you cant have two like a mutable and immutable at same time)

    3) if you never allow the api to create mutable references henceforth,
    (which can cause problems of its own)

    4) and you cant mutate it from multiple threads 
    (no need to worry about Send or Sync at all here)
    ((which can cause race conditions and all sorts of voodoo problems))


    conclusion) it is safe to immutably change the runtime through static immutable references
    */
    unsafe { &*RT.get_or_init(OptimalRuntime::for_platform) }
}

pub(crate) fn spawn<R: Send + 'static>(fut: impl Future<Output = R> + Send + 'static) -> Join<R> {
    rt().share().spawn(fut)
}

pub(crate) fn spawn_local<R: 'static>(fut: impl Future<Output = R> + 'static) -> Join<R> {
    rt().spawn_local(fut)
}

pub(crate) fn block_on<F: Future>(f: F) -> F::Output {
    rt().block_on(f)
}

pub struct LocalTask {
    future: Pin<Box<dyn Future<Output = ()>>>,
    waker: Waker,
}

pub struct SharedTask {
    future: Pin<Box<dyn Future<Output = ()> + Send>>,
    waker: Waker,
}