class ChatServer implements Runnable {
    private int port = 0
    private List<Client> clientList = new ArrayList<>()

    ChatServer(int port) {
        this.port = port
    }

    @SuppressWarnings("GroovyInfiniteLoopStatement")
    @Override
    void run() {
        try {
            ServerSocket serverSocket = new ServerSocket(port)
            while (true) {
                Socket socket = serverSocket.accept()
                new Thread(new Client(socket)).start()
            }
        } catch (Exception e) {
            e.printStackTrace()
        }
    }

    private synchronized boolean registerClient(Client client) {
        for (Client other: clientList) {
            if (other.clientName.equalsIgnoreCase(client.clientName)) {
                return false
            }
        }
        clientList.add(client)
        return true
    }

    private void deRegisterClient(Client client) {
        boolean wasRegistered
        synchronized (this) {
            wasRegistered = clientList.remove(client)
        }
        if (wasRegistered) {
            broadcast(client, "--- " + client.clientName + " left ---")
        }
    }

    private synchronized String getOnlineListCSV() {
        StringBuilder sb = new StringBuilder()
        sb.append(clientList.size()).append(" user(s) online: ")
        def it = clientList.iterator()
        if (it.hasNext()) {
            sb.append(it.next().clientName)
        }
        while (it.hasNext()) {
            sb.append(", ")
            sb.append(it.next().clientName)
        }
        return sb.toString()
    }

    private void broadcast(Client fromClient, String msg) {