use hashbrown::HashMap;

use crate::handle::{Handle, HandleResource};

use self::{
    channel::{Channel, ChannelMessage},
    socket::Socket,
};

pub mod channel;
pub mod socket;

lazy_static::lazy_static! {
    pub static ref IPC: spin::Mutex<IPCService> = spin::Mutex::new(IPCService {
        sockets: HashMap::new(),
        channels: HashMap::new(),
        public_board: vec![],
    });
}
pub struct IPCService {
    pub sockets: HashMap<Handle, Socket>,
    pub channels: HashMap<Handle, Channel>,
    // TODO: Add a public board of each down below which use some method of pointing to the above
    pub public_board: Vec<Handle>,
}

impl IPCService {
    pub fn examine_board(&self) -> Vec<Handle> {
        self.public_board.clone()
    }
}

// Socket Related Impl
impl IPCService {
    pub fn create_socket<S: Into<String>>(&mut self, name: S, public: bool) -> Handle {
        let handle = Handle::new(crate::handle::HandleResource::Socket);

        let mut sock = Socket::new(name.into());
        if public {
            sock.public = true;

            self.public_board.push(handle);
        }

        self.sockets.insert(handle.clone(), sock);

        handle
    }
    pub fn send_socket<S: Into<Vec<u8>>>(
        &mut self,
        handle: Handle,
        data: S,
    ) -> Result<(), IPCError> {
        trace!("Sending data to {}", handle);
        match handle {
            Handle {
                inner: _inner,
                res: HandleResource::Socket,
            } => {
                let sock = self.sockets.get_mut(&handle);

                match sock {
                    Some(socket) => {
                        return socket.write(data.into());
                    }
                    None => return Err(IPCError::NonexistantSocket),
                }
            }
            _ => return Err(IPCError::InvalidHandle),
        }
    }

    pub fn read_socket(&mut self, handle: Handle) -> Result<Vec<u8>, IPCError> {
        trace!("Reading data from {}", handle);

        match handle {
            Handle {
                inner: _inner,
                res: HandleResource::Socket,
            } => {
                let sock = self.sockets.get_mut(&handle);

                match sock {
                    Some(socket) => {
                        return socket.read();
                    }
                    None => return Err(IPCError::NonexistantSocket),
                }
            }
            _ => return Err(IPCError::InvalidHandle),
        }
    }
}

impl IPCService {
    pub fn create_channel<S: Into<String>>(&mut self, name: S, public: bool) -> Handle {
        let handle = Handle::new(crate::handle::HandleResource::Channel);

        let mut chan = Channel::new(name.into(), public);
        if public {
            chan.public = true;

            self.public_board.push(handle);
        }

        self.channels.insert(handle.clone(), chan);

        handle
    }

    pub fn read_channel(&mut self, handle: Handle) -> Result<ChannelMessage, IPCError> {
        trace!("Reading data from {}", handle);

        match handle {
            Handle {
                inner: _inner,
                res: HandleResource::Channel,
            } => {
                let sock = self.channels.get_mut(&handle);

                match sock {
                    Some(socket) => {
                        return socket.read();
                    }
                    None => return Err(IPCError::NonexistantSocket),
                }
            }
            _ => return Err(IPCError::InvalidHandle),
        }
    }

    pub fn send_channel(&mut self, handle: Handle, data: ChannelMessage) -> Result<(), IPCError> {
        trace!("Sending data to {}", handle);
        match handle {
            Handle {
                inner: _inner,
                res: HandleResource::Channel,
            } => {
                let sock = self.channels.get_mut(&handle);

                match sock {
                    Some(socket) => {
                        return socket.write(data);
                    }
                    None => return Err(IPCError::NonexistantSocket),
                }
            }
            _ => return Err(IPCError::InvalidHandle),
        }
    }
}

#[derive(Debug)]
#[non_exhaustive]
pub enum IPCError {
    InvalidHandle,
    NonexistantSocket,
    EmptySocket,
    EmptyChannel,
    InvalidMessage,
}
