use std::sync::mpsc;
use mpsc::{ Sender, Receiver, SendError, RecvError };

use std::collections::VecDeque;

pub struct Controller<M> {
    server: Option<Server<M>>,
    clients: VecDeque<Client<M>>,
}

pub struct Server<M> {
    txs: Vec<Sender<M>>,
    rxs: Vec<Receiver<M>>,
    info: Receiver<(usize, M)>,
}

pub struct Client<M> {
    idx: usize,
    tx: Sender<M>,
    rx: Receiver<M>,
    info: Sender<(usize, M)>,
}

impl<M> Controller<M> {
    pub fn new(n: usize) -> Self {
        let (info_tx, info_rx) = mpsc::channel::<(usize, M)>();
        let mut c = Controller {
            server: Some(Server {
                txs: Vec::new(),
                rxs: Vec::new(),
                info: info_rx,
            }),
            clients: VecDeque::new(),
        };
        
        for idx in 0..n {
            let (server_tx, client_rx) = mpsc::channel();
            let (client_tx, server_rx) = mpsc::channel();
            match c.server.as_mut() {
                Some(s) => {
                    s.txs.push(server_tx);
                    s.rxs.push(server_rx);
                },
                None => unreachable!(),
            }
            c.clients.push_back(Client {
                idx,
                tx: client_tx,
                rx: client_rx,
                info: info_tx.clone(),
            });
        }

        c
    }

    pub fn server(&mut self) -> Option<Server<M>> {
        self.server.take()
    }

    pub fn client(&mut self) -> Option<Client<M>> {
        self.clients.pop_front()
    }
}

impl<M> Server<M> {
    pub fn send(&self, idx: usize, msg: M) -> Result<(), SendError<M>> {
        self.txs[idx].send(msg)
    }
    pub fn recv(&self, idx: usize) -> Result<M, RecvError> {
        self.rxs[idx].recv()
    }
    pub fn info(&self) -> Result<(usize, M), RecvError> {
        self.info.recv()
    }
}

impl <M> Server<M>
where M: Clone {
    pub fn broadcast(&self, msg: M) -> Result<(), SendError<M>> {
        for tx in &self.txs {
            tx.send(M::clone(&msg))?;
        }

        Ok(())
    }
}

impl<M> Client<M> {
    pub fn send(&self, m: M) -> Result<(), SendError<M>> {
        self.tx.send(m)
    }
    pub fn recv(&self) -> Result<M, RecvError> {
        self.rx.recv()
    }
    pub fn info(&self, msg: M) -> Result<(), SendError<(usize, M)>> {
        self.info.send((self.idx, msg))
    }
}
