// This method is called when a request has been made to close a client connection
  public synchronized void disconnectClient(ClientConnection clientConnection) {

    // Ensures this connection exists (a necessary check when this is being called from outside of a
    // clientConnection)
    if (clientConnection != null
        && clientConnection.isConnected()
        && clientSocketList.get(clientSocketList.indexOf(clientConnection)) != null) {

      // Send the client a message telling it to close its socket
      clientConnection.xmitMessage(ClientConnection.QUIT);

      // Then close that socket...the user really doesn't have a choice.  We're just asking to be
      // nice.
      clientSocketList.get(clientSocketList.indexOf(clientConnection)).closeSocket();
      parent.updateStatusBox(
          clientSocketList.indexOf(clientConnection)
              + " "
              + "Forcing disconnection of client "
              + clientConnection.id
              + " on port "
              + port
              + "...");
    } else {

      // If this thread is NOT connected, it may be stuck, so let's send an interrupt to wake it up
      clientThreadList.get(clientSocketList.indexOf(clientConnection)).interrupt();
      parent.updateStatusBox("Closing listen socket on port " + port + "...");
    }
  }
  // This method sets a boolean value that tells the server to leave the socket generation loop and
  // begin cleanup
  public void notifyStopPressed() {
    stopPressed = true;
    parent.getServerThread().interrupt();

    try {
      if (serverSocket != null) {
        serverSocket.close();
      }
    } catch (IOException e) {
      parent.updateStatusBox(e.getMessage());
    }
  }
  // This method is called when a client has disconnected from the server and its thread is about to
  // end
  // It just presents a status message and removes the given client and thread from our lists
  public synchronized void clientThreadEnding(ClientConnection clientConnection) {

    parent.updateStatusBox(
        clientSocketList.indexOf(clientConnection)
            + " "
            + "Client "
            + clientConnection.id
            + " disconnected, removing from connection pool");

    clientThreadList.remove(clientSocketList.indexOf(clientConnection));
    clientSocketList.remove(clientSocketList.indexOf(clientConnection));
    numClients--;

    parent.updateStatusBox("There are " + numClients + " clients connected");
  }
  // Once a client is connected to the server, their connection object is handed off to this method
  // for handling
  public synchronized void clientConnected(Socket socket) {

    // Create a ClientConnection to contain this new client, pass off the socket, and start the new
    // thread
    ClientConnection clientConnection = new ClientConnection(this, socket, idCounter);
    Thread clientSocketThread = new Thread(clientConnection);
    clientSocketThread.start();

    // Add this connection to our socket and thread lists so we can keep track of who is alive
    clientSocketList.add(clientConnection);
    clientThreadList.add(clientSocketList.indexOf(clientConnection), clientSocketThread);

    numClients++; // numClients just keeps track of how many clients are connected
    idCounter++; // idCounter NEVER decrements, so we will never have two clientConnections trying
                 // to occupy
    //      the same space in the vector

    // Let the server status box know that someone has connected
    parent.updateStatusBox("Client " + clientConnection.id + " has connected on port " + port);
    parent.updateStatusBox("There are " + numClients + " clients connected\n");
  }
  // This method starts the voting process
  public void startVote() {

    this.voting = true; // Status boolean

    broadcastMsg("/numq" + numQuestions); // Let all clients know how many questions are coming

    for (int x = 0; x <= (numQuestions - 1); x++) {
      broadcastMsg("/ques" + x + questions[x]); // Send each question in turn to the clients
    }

    // Tell the clients that voting has started (doubles as a "no more questions" notification
    broadcastMsg("/stvt");
    parent.updateStatusBox("Voting started!  " + numQuestions + " questions presented to clients.");
  }
  // This method stops the voting process
  public void stopVote() {

    this.voting = false; // Status boolean

    for (int x = 0; x <= (numQuestions - 1); x++) {
      broadcastMsg(
          "/ansr" + x + votes[x]); // Tells all clients how many votes were cast for each question
    }
    broadcastMsg("/novo"); // Tells all users that voting has ended

    parent.updateStatusBox(
        "Voting has ended!  Voting Results: \n1: "
            + votes[0]
            + " \n2: "
            + votes[1]
            + " \n3: "
            + votes[2]
            + " \n4: "
            + votes[3]);

    // Perform some simple math to calculate the vote winner, then let the server know which option
    // won
    int largest = 0;
    int winner = 0;
    for (int x = 0; x <= 3; x++) {
      if (votes[x] > largest) {
        largest = votes[x];
        winner = (x + 1);
      }
    }
    parent.updateStatusBox("Option " + winner + " was reported as the winner.");

    for (int x = 0; x <= 3; x++) {
      this.votes[x] = 0;
    }
  }
  // This method keeps track of how many votes have been cast, useful for letting the server manager
  // know when to stop a vote
  public void setVote(int voteNo) {

    this.votes[voteNo]++;

    int totalVotes = votes[0] + votes[1] + votes[2] + votes[3];
    int votesRemaining = numClients - totalVotes;
    broadcastMsg("/rem" + votesRemaining);
    parent.updateStatusBox(
        totalVotes
            + " votes cast out of "
            + numClients
            + " clients.  "
            + votesRemaining
            + " votes remain.");
  }
  // RUN METHOD
  public void run() {

    for (int x = 0; x <= 3; x++) {
      selected[x] = false;
      questions[x] = "";
    }

    // Create new lists for the clients and threads
    if (clientSocketList == null || !clientSocketList.isEmpty()) {
      clientSocketList = new Vector<ClientConnection>();
    }

    if (clientThreadList == null || !clientThreadList.isEmpty()) {
      clientThreadList = new Vector<Thread>();
    }

    // Now we're ready to go
    isRunning = true;
    stopPressed = false;

    // Server initialization
    parent.updateStatusBox("SERVER ONLINE");
    parent.setStatus(new Color(32, 160, 32), "ONLINE");
    parent.setButtonEnabled(parent.STOP_BUTTON, true);

    Socket clientSocket = null;

    // SERVER CONNECTION LOOP AND CLIENT THREAD GENERATION
    while (!stopPressed) {
      try {
        // Create a new socket and wait for a client connection
        parent.updateStatusBox("Listening for clients on port: " + String.valueOf(port));
        serverSocket = new ServerSocket(port);
        clientSocket = serverSocket.accept();

        // Once a connection is established, we pass it off to clientConnected, which creates a new
        // thread
        //      to manage this client
        clientConnected(clientSocket);

      } catch (IOException e) {
        // No need to implement anything here -- cleanup is taken care of outside the loop
        // TODO: Move cleanup to a FINALLY block
        parent.updateStatusBox(e.getMessage());
      } finally {

        try {
          if (serverSocket != null) {
            serverSocket.close();
          }
        } catch (IOException e) {
          parent.updateStatusBox(e.getMessage());
        }
      }
    }

    // We're responsible adults...once we leave the server connection loop, we need to clean up
    // (close all
    //      connections) before we can officially stop the server.
    parent.updateStatusBox("Stopping server...");

    // Instead of locking clientSocketList inside of a huge synchronized block, just make a copy of
    // it
    //      This is a good idea because as clients disconnect, they will be asking to remove
    // themselves from
    //      the clientSocketList, and we don't want them to wait
    Vector<ClientConnection> snapshot = new Vector<ClientConnection>();
    snapshot.addAll(clientSocketList);
    Iterator<ClientConnection> clientIterator = snapshot.iterator();
    ClientConnection clientConnection;

    // This loop iterates through all of the currently connected clients and disconnects them
    while (clientIterator.hasNext()) {
      clientConnection = clientIterator.next();
      parent.updateStatusBox("Stopping client from ServerRunnable Loop");
      disconnectClient(clientConnection);
    }
    snapshot.clear();

    // Once we've cleaned up, we let the user know that the server is down and re-enable the Start
    // Server button
    parent.updateStatusBox("\nSERVER SHUTDOWN COMPLETE");
    parent.setStatus(Color.RED, "OFFLINE");
    isRunning = false;
    parent.setButtonEnabled(parent.GO_BUTTON, true);
  }