/**
   * Ends the call with for this <tt>CallPeer</tt>. Depending on the state of the peer the method
   * would send a CANCEL, BYE, or BUSY_HERE message and set the new state to DISCONNECTED.
   *
   * @param failed indicates if the hangup is following to a call failure or simply a disconnect
   * @param reasonText the text, if any, to be set on the <tt>ReasonPacketExtension</tt> as the
   *     value of its
   * @param reasonOtherExtension the <tt>PacketExtension</tt>, if any, to be set on the
   *     <tt>ReasonPacketExtension</tt> as the value of its <tt>otherExtension</tt> property
   */
  public void hangup(boolean failed, String reasonText, PacketExtension reasonOtherExtension) {
    // do nothing if the call is already ended
    if (CallPeerState.DISCONNECTED.equals(getState()) || CallPeerState.FAILED.equals(getState())) {
      if (logger.isDebugEnabled())
        logger.debug("Ignoring a request to hangup a call peer " + "that is already DISCONNECTED");
      return;
    }

    CallPeerState prevPeerState = getState();
    getMediaHandler().getTransportManager().close();

    if (failed) setState(CallPeerState.FAILED, reasonText);
    else setState(CallPeerState.DISCONNECTED, reasonText);

    SessionIQ responseIQ = null;

    if (prevPeerState.equals(CallPeerState.CONNECTED) || CallPeerState.isOnHold(prevPeerState)) {
      responseIQ =
          GTalkPacketFactory.createBye(getProtocolProvider().getOurJID(), peerJID, getSID());
      responseIQ.setInitiator(isInitiator() ? getAddress() : getProtocolProvider().getOurJID());
    } else if (CallPeerState.CONNECTING.equals(prevPeerState)
        || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(prevPeerState)
        || CallPeerState.ALERTING_REMOTE_SIDE.equals(prevPeerState)) {
      responseIQ =
          GTalkPacketFactory.createCancel(getProtocolProvider().getOurJID(), peerJID, getSID());
      responseIQ.setInitiator(isInitiator() ? getAddress() : getProtocolProvider().getOurJID());
    } else if (prevPeerState.equals(CallPeerState.INCOMING_CALL)) {
      responseIQ =
          GTalkPacketFactory.createBusy(getProtocolProvider().getOurJID(), peerJID, getSID());
      responseIQ.setInitiator(isInitiator() ? getAddress() : getProtocolProvider().getOurJID());
    } else if (prevPeerState.equals(CallPeerState.BUSY)
        || prevPeerState.equals(CallPeerState.FAILED)) {
      // For FAILED and BUSY we only need to update CALL_STATUS
      // as everything else has been done already.
    } else {
      logger.info("Could not determine call peer state!");
    }

    if (responseIQ != null) {
      if (reasonOtherExtension != null) {
        ReasonPacketExtension reason =
            (ReasonPacketExtension)
                responseIQ.getExtension(
                    ReasonPacketExtension.ELEMENT_NAME, ReasonPacketExtension.NAMESPACE);

        if (reason == null) {
          if (reasonOtherExtension instanceof ReasonPacketExtension) {
            responseIQ.setReason((ReasonPacketExtension) reasonOtherExtension);
          }
        } else reason.setOtherExtension(reasonOtherExtension);
      }

      getProtocolProvider().getConnection().sendPacket(responseIQ);
    }
  }
  /**
   * Sets the state of the contained call peer by specifying the state name.
   *
   * @param oldState the previous state of the peer
   * @param newState the new state of the peer
   * @param stateString the state of the contained call peer
   */
  public void setPeerState(
      final CallPeerState oldState, final CallPeerState newState, final String stateString) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setPeerState(oldState, newState, stateString);
            }
          });
      return;
    }

    this.callStatusLabel.setText(stateString);

    if (newState == CallPeerState.CONNECTED
        && !CallPeerState.isOnHold(oldState)
        && !securityStatusLabel.isSecurityStatusSet()) {
      securityStatusLabel.setSecurityOff();
    }
  }