/**
  * Adds a new user
  *
  * @param username if a user with the given username already exists, the existing user will be
  *     returned
  * @param connection the TCP connection which this user is associated with
  * @return newly created user instance
  */
 public synchronized User addUser(String username, TCPServerConnection connection) {
   for (User user : users) {
     if (user.getUsername().equals(username)) {
       return user;
     }
   }
   User user = new User(username, connection);
   users.add(user);
   return user;
 }
  /**
   * Sends the given message to the given user, using the UDPServer
   *
   * @param user
   * @param message
   */
  public synchronized void notifyUser(User user, String message) {
    if (testMode) {
      log.debug("i am running in test mode.");
      return;
    }

    if (user.getUdpPort() == 99999) {
      testMode = true;
      log.debug("i am running in test mode.");
      return;
    }

    DatagramPacket dp =
        new DatagramPacket(
            message.getBytes(), // message content
            0, // offset=0 => message starts at char 0
            message.getBytes().length, // entire message
            user.getTcpConnection().getClientSocket().getInetAddress(), // clients InetAddress
            user.getUdpPort()); // clients udp port

    UDPServer.send(dp);
  }
  // create events here
  public synchronized void processMessage(TCPServerConnection tcpConnection, String input) {
    if (input.startsWith("!login")) {
      String username;
      int udpPort = 0;
      try {
        String[] parts = input.split(" ");
        username = parts[1].trim();
        udpPort = Integer.parseInt(parts[2]);
        if (getUser(tcpConnection) == null) {
          User user = addUser(username, tcpConnection);
          analyticsServer.processEvent((EventInterface) UserEvent.createUserLogin(username));
          user.setUdpPort(udpPort);
          tcpConnection.sendMessage("Successfully logged in as " + user.getUsername());
        }
      } catch (IndexOutOfBoundsException e) {
        tcpConnection.sendMessage("You must provide a username!");
      } catch (NumberFormatException e) {
        tcpConnection.sendMessage("Login failed.");
      } catch (ConnectException e) {
        log.warn("could not connect to analytics server.");
      } catch (RemoteException e) {
        log.warn("could not process event: " + e.getMessage());
      }
    } else if (input.trim().equals("!logout")) {
      User user = getUser(tcpConnection);
      if (user == null) {
        tcpConnection.sendMessage("You have to login first!");
      } else {
        String username = user.getUsername();
        users.remove(user);
        try {
          analyticsServer.processEvent(UserEvent.createUserLogout(username));
          user.endSession();
          for (StatisticEvent e :
              StatisticEvent.createEventsOnUserLogout(user.getSessionDuration())) {
            analyticsServer.processEvent(e);
          }

        } catch (ConnectException e) {
          log.warn("could not connect to analytics server.");
        } catch (RemoteException e) {
          log.warn("could not process event: " + e.getMessage());
        }
        tcpConnection.sendMessage("You successfully logged out as " + user.getUsername());
      }
    } else if (input.trim().equals("!end")) {
      User user = getUser(tcpConnection);
      if (user != null)
        try {
          analyticsServer.processEvent(UserEvent.createUserDisconnected(user.getUsername()));
          user.endSession();
          for (StatisticEvent e :
              StatisticEvent.createEventsOnUserLogout(user.getSessionDuration())) {
            analyticsServer.processEvent(e);
          }
        } catch (ConnectException e) {
          log.warn("could not connect to analytics server.");
        } catch (RemoteException e) {
          log.warn("could not process event: " + e.getMessage());
        }
    } else if (input.trim().equals("!list")) {
      for (Auction auction : AuctionManager.getInstance().getAuctions()) {
        Bid heighestBid = auction.getHighestBid();

        String msg =
            String.format(
                "%d. '%s' %s %s %s",
                auction.getId(),
                auction.getName(),
                auction.getCreator().getUsername(),
                heighestBid == null ? "0.00" : heighestBid.getAmount(),
                heighestBid == null ? "none" : heighestBid.getBidder().getUsername());

        tcpConnection.sendMessage(msg);
      }
    } else if (input.startsWith("!create")) {
      if (getUser(tcpConnection) == null) {
        tcpConnection.sendMessage("login first!");
        return;
      }
      try {
        String[] parts = input.split(" ");
        StringBuilder sb = new StringBuilder();
        for (int i = 2; i < parts.length; i++) {
          sb.append(parts[i]).append(" ");
        }
        Auction auction =
            AuctionManager.getInstance()
                .createAuction(
                    sb.toString().trim(), Integer.valueOf(parts[1]), getUser(tcpConnection));

        analyticsServer.processEvent(AuctionEvent.createAuctionStarted(auction.getId()));

        tcpConnection.sendMessage(
            "An auction '"
                + auction.getName()
                + "' with id "
                + auction.getId()
                + " has been created and will end on "
                + auction.getEndDateFormatted());

      } catch (IndexOutOfBoundsException e) {
        tcpConnection.sendMessage("You must provide the duration and the name of the auction!");
      } catch (NumberFormatException e) {
        tcpConnection.sendMessage("After !create you must enter the seconds!");
      } catch (ConnectException e) {
        log.warn("could not connect to analytics server.");
      } catch (RemoteException e) {
        log.warn("could not process event: " + e.getMessage());
      }
    } else if (input.startsWith("!bid")) {
      try {
        String[] parts = input.split(" ");
        int auctionId = Integer.parseInt(parts[1]);
        double amount = Double.parseDouble(parts[2]);
        Bid heighestBid = AuctionManager.getInstance().getHeighestBid(auctionId);
        User user = getUser(tcpConnection);
        Auction auction = AuctionManager.getInstance().getAuctionById(auctionId);
        if (user == null) {
          tcpConnection.sendMessage("You have to login first!");
        } else if (auction != null && user.equals(auction.getCreator())) {
          tcpConnection.sendMessage("You may not bid on your own auction!");
        } else {
          Bid bid = AuctionManager.getInstance().newBid(amount, auctionId, user);
          if (bid != null) {

            try {
              analyticsServer.processEvent(
                  BidEvent.createBidPlacedEvent(
                      user.getUsername(), auction.getId(), bid.getAmount()));
              List<StatisticEvent> events = StatisticEvent.createEventsOnBidPlaced(bid.getAmount());
              for (StatisticEvent e : events) {
                analyticsServer.processEvent(e);
              }
            } catch (ConnectException e) {
              log.warn("could not connect to analytics server.");
            } catch (RemoteException e) {
              log.warn("could not process event: " + e.getMessage());
            }
            tcpConnection.sendMessage(
                "You successfully bid with "
                    + bid.getAmount()
                    + " on '"
                    + bid.getAuction().getName()
                    + "'");

            if (heighestBid != null && !user.equals(heighestBid.getBidder())) {
              try {
                analyticsServer.processEvent(
                    BidEvent.createBidOverbidEvent(
                        heighestBid.getBidder().getUsername(),
                        heighestBid.getAuction().getId(),
                        heighestBid.getAmount()));
              } catch (RemoteException e) {
                log.warn("could not process event: " + e.getMessage());
              }
              notifyUser(
                  heighestBid.getBidder(),
                  "You have been overbid on '" + heighestBid.getAuction().getName() + "'");
            }
          } else {
            tcpConnection.sendMessage("invalid bid");
          }
        }
      } catch (IndexOutOfBoundsException e) {
        tcpConnection.sendMessage("!bid usage: !bid auction.id bid.amount");
      } catch (NumberFormatException e) {
        tcpConnection.sendMessage("argument 2: int auction id, argument 3: double amount");
      }
    }
  }
 /**
  * Returns the user connected by the given tcp connection
  *
  * @param connection
  * @return
  */
 private synchronized User getUser(TCPServerConnection connection) {
   for (User user : users) {
     if (connection.equals(user.getTcpConnection())) return user;
   }
   return null;
 }