/**
   * Sends a notification to the highest bidder and the auction owner
   *
   * @param auction ended auction
   */
  public synchronized void notifyAuctionEnded(Auction auction) {
    if (auction.getHighestBid() != null) {
      notifyUser(
          auction.getCreator(),
          "The auction '"
              + auction.getName()
              + "' has ended. "
              + auction.getHighestBid().getBidder().getUsername()
              + " has won with "
              + auction.getHighestBid().getAmount());

      notifyUser(
          auction.getHighestBid().getBidder(),
          "The auction '"
              + auction.getName()
              + "' has ended. "
              + "You won with "
              + auction.getHighestBid().getAmount());
    } else {
      notifyUser(
          auction.getCreator(),
          "The auction '" + auction.getName() + "' has ended. " + "No one bid on it.");
    }
  }
  /**
   * Ends the given auction and sends the notifications
   *
   * @param auction
   */
  public synchronized void endAuction(Auction auction) {
    if (null != AuctionManager.getInstance().getAuctionById(auction.getId())) {
      AuctionManager.getInstance().removeAuction(auction);

      try {
        Bid highestBid = auction.getHighestBid();
        if (highestBid != null) {
          try {
            billingServer.billAuction(
                auction.getCreator().getUsername(),
                auction.getId(),
                auction.getHighestBid().getAmount());
          } catch (RemoteException e) {
            log.warn("could not bill auction: " + e.getMessage());
          }
        }

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

        List<StatisticEvent> events;

        if (highestBid != null) {
          analyticsServer.processEvent(
              BidEvent.createBidWonEvent(
                  highestBid.getBidder().getUsername(),
                  highestBid.getAuction().getId(),
                  highestBid.getAmount()));

          // create successful auction_ended events
          events = StatisticEvent.createEventsOnAuctionEnded((long) auction.getDuration(), true);
        } else {
          // unsuccessful auction_ended events, because no bid
          events = StatisticEvent.createEventsOnAuctionEnded((long) auction.getDuration(), false);
        }

        for (StatisticEvent e : events) analyticsServer.processEvent(e);

      } catch (RemoteException e) {
        log.warn("could not process event: " + e.getMessage());
      }

      notifyAuctionEnded(auction);
    }
  }
  // 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");
      }
    }
  }