pub static SOCKETS: spin::Mutex<SocketState> = spin::Mutex::new(vec![]);

pub type SocketState = Vec<SimpleSock>;
pub type Stream = Vec<u8>;

#[derive(Debug)]
pub struct SocketID {
    pub id: usize,
}

impl SocketID {
    pub fn protocol(self) -> Option<String> {
        SOCKETS.lock()[self.id].protocol.clone()
    }
}

impl Socket for SocketID {
    fn peek(&mut self) -> SocketReturns {
        SOCKETS.lock()[self.id].peek()
    }

    fn read(&mut self, length: usize) -> SocketReturns {
        SOCKETS.lock()[self.id].read(length)
    }

    fn write(&mut self, stream: Stream) -> SocketReturns {
        SOCKETS.lock()[self.id].write(stream)
    }
    fn register_protocol(&mut self, protocol_name: String) {
        SOCKETS.lock()[self.id].protocol = Some(protocol_name);
    }

    fn check_protocol(&mut self, protocol_name: String) -> bool {
        SOCKETS.lock()[self.id].check_protocol(protocol_name)
    }
}

pub trait Socket {
    fn peek(&mut self) -> SocketReturns;

    /// Reads from the socket and removes read bytes from the socket
    fn read(&mut self, length: usize) -> SocketReturns;

    fn write(&mut self, stream: Stream) -> SocketReturns;
    fn register_protocol(&mut self, protocol_name: String);
    fn check_protocol(&mut self, protocol_name: String) -> bool;

    fn close(&mut self) {}
}

#[derive(Debug)]
pub enum SocketReturns {
    ReadOk(Stream),
    ReadIndexOutOfBounds,
    WriteOk,
}

impl SocketReturns {
    pub fn unwrap(self) -> Stream {
        match self {
            SocketReturns::ReadOk(stream) => stream,
            SocketReturns::ReadIndexOutOfBounds => panic!("Read Index Out Of Bounds"),
            SocketReturns::WriteOk => vec![1],
        }
    }
}

pub struct SimpleSock {
    stream: Vec<u8>,
    protocol: Option<String>,
}

impl SimpleSock {
    pub fn new() -> SocketID {
        let sock = SimpleSock {
            stream: vec![],
            protocol: None,
        };
        let mut sock_lock = SOCKETS.lock();
        sock_lock.push(sock);

        SocketID {
            id: sock_lock.len() - 1,
        }
    }
    pub fn regrab_sock(id: usize) -> SocketID {
        SocketID { id }
    }

    pub fn grab_socket(protocol_name: String) -> Option<SocketID> {
        let socket_handle = SOCKETS.lock();
        let socket_count = socket_handle.len();
        drop(socket_handle);

        let return_sock = None;
        for socket_id in 0..socket_count {
            let mut id = SimpleSock::regrab_sock(socket_id);

            if id.check_protocol(protocol_name.clone()) {
                return Some(id);
            }
        }

        return_sock
    }
}

impl Socket for SimpleSock {
    fn peek(&mut self) -> SocketReturns {
        let mut return_vec = vec![];

        for x in &self.stream {
            return_vec.push(*x);
        }
        SocketReturns::ReadOk(return_vec)
    }

    fn read(&mut self, length: usize) -> SocketReturns {
        let mut return_vec = vec![];
        if length > self.stream.len() {
            SocketReturns::ReadIndexOutOfBounds
        } else {
            for _ in 0..length {
                return_vec.push(self.stream[0]);
                self.stream.remove(0);
            }
            SocketReturns::ReadOk(return_vec)
        }
    }

    fn register_protocol(&mut self, protocol_name: String) {
        self.protocol = Some(protocol_name);
    }

    fn check_protocol(&mut self, protocol_name: String) -> bool {
        if self.protocol == Some(protocol_name) {
            return true;
        }

        false
    }

    fn write(&mut self, stream: Stream) -> SocketReturns {
        for byte in stream {
            self.stream.push(byte);
        }
        SocketReturns::WriteOk
    }

    fn close(&mut self) {}
}
