/**
   * Hands a canvas to the chief.
   *
   * @param thiefId the thief id
   * @param teamId the team where that thief belongs
   * @param rolledCanvas true if a canvas was stolen, false otherwise
   */
  @Override
  public void handACanvas(int thiefId, int teamId, boolean rolledCanvas) {
    Team team = (Team) this.teamsHash.get(teamId);
    if (team == null) {
      throw new IllegalArgumentException("Unknown team with id #" + teamId);
    }

    MessageBroker broker = (MessageBroker) this.teamsBroker.get(teamId);
    synchronized (this.chiefBroker) {
      synchronized (broker) {
        this.logger.setThiefStatus(thiefId, Logger.THIEF_STATUS.OUTSIDE);
        team.removeThief(thiefId);
      }

      this.chiefBroker.writeMessage(new Message(THIEF_ARRIVE_ACTION, thiefId));
      this.chiefBroker.writeMessage(
          new HandCanvasMessage(THIEF_HAND_CANVAS_ACTION, thiefId, team.getId(), rolledCanvas));

      if (this.multipleMasters) {
        this.chiefBroker.notifyAll();
      } else {
        this.chiefBroker.notify();
      }
    }
  }
  /**
   * Collects the canvas of thief that arrived.
   *
   * @param chiefId the id of the chief
   * @param thiefId the id of the thief that handed the canvas
   */
  @Override
  public void collectCanvas(int chiefId, int thiefId) {
    while (true) {

      HandCanvasMessage message =
          (HandCanvasMessage) this.chiefBroker.readMessage(THIEF_HAND_CANVAS_ACTION, thiefId);
      if (message != null) {

        Team team = (Team) this.teamsHash.get(message.getTeamId());
        if (team == null) {
          throw new IllegalArgumentException("Unknown team with id #" + message.getTeamId());
        }

        synchronized (this.chiefBroker) {
          if (message.rolledCanvas()) this.nrCollectedCanvas++;

          Room room = team.getAssignedRoom();
          if ((boolean) this.roomsStatus.get(room.getId()) && !message.rolledCanvas()) {
            this.roomsStatus.put(room.getId(), false);
            this.nrRoomsToBeRobed--;
          }
        }

        break;
      }
    }
  }
  /**
   * Prepares the thief for the excursion. This method should signal the master that the thief is
   * ready and only depart when notified.
   *
   * @param thiefId the thief id
   * @param teamId the team in which the thief belongs
   * @return the room assigned to the team
   */
  @Override
  public ITargetRoom prepareExcursion(int thiefId, int teamId) {

    MessageBroker broker = (MessageBroker) this.teamsBroker.get(teamId);
    if (broker == null) {
      throw new IllegalArgumentException("Unknown team with id #" + teamId);
    }

    boolean sentMessage = false;
    while (true) {

      Team team;
      synchronized (broker) {
        if (!sentMessage) {
          sentMessage = true;
          broker.writeMessage(new Message(THIEF_READY_FOR_DEPARTURE_ACTION));
        }

        team = (Team) this.teamsHash.get(teamId);
        team.addThief(thiefId);

        try {
          broker.wait();
        } catch (InterruptedException ex) {
        }
      }

      Message message = broker.readMessage(SEND_ASSAULT_PARTY_ACTION);
      if (message != null) {
        return team.getAssignedRoom();
      }
    }
  }
  /**
   * Method that sends a previously prepared team to rob the designated room. This method guarantees
   * that all the thieves depart at the same time.
   *
   * @param chiefId the id of the chief
   * @param teamId the team to be sent
   */
  @Override
  public void sendAssaultParty(int chiefId, int teamId) {
    Team team = (Team) this.teamsHash.get(teamId);
    if (team == null) {
      throw new IllegalArgumentException("Unknown team with id #" + teamId);
    }

    synchronized (this.chiefBroker) {
      if (!team.isBeingPrepared()) {
        throw new IllegalStateException("Team with id #" + teamId + " is not being prepared.");
      }

      MessageBroker broker = (MessageBroker) this.teamsBroker.get(teamId);
      int nrThieves = team.getCapacity();
      for (int x = 0; x < nrThieves; x++) {

        while (broker.readMessage(THIEF_READY_FOR_DEPARTURE_ACTION) == null) {
          Thread.yield();
        }

        broker.writeMessage(new Message(SEND_ASSAULT_PARTY_ACTION));
      }

      // System.out.println("[Chief] Notifying all thieves of the party to start crawling to room #"
      // + team.getAssignedRoom().getId() + "..");

      synchronized (broker) {
        broker.notifyAll();
      }
    }
  }