/**
   * Handles server invite message
   *
   * @param msg - invite message
   */
  public void handleIncomingInvite(final Request msg) throws DialogStateException {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();

    assert !done.get();
    assert msg != null && MessageType.SIP_INVITE == MessageType.parse(msg.getMethod());

    if (!done.get()) {
      Logger.log("Remote party has sent invite");
      // ClientIdentity localParty =
      // getStackContext().getStackClientRegistry().findAddressee(msg.getTo().getUriBuilder().getShortURI());
      ClientIdentity localParty = getStackContext().getClientRouter().findAddressee(msg);
      if (localParty != null) {
        assert getStackContext().getDialogStorage().findDialogForMessage(msg) == null;
        final Dialog dialog =
            getStackContext().getDialogStorage().getDialogForIncomingMessage(localParty, msg);
        TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction>
            transactionType = SIP_INVITE_SERVER;

        doHandleIncomingInvite(msg, dialog, transactionType);
      } else {
        throw new DialogStateException(null, DialogStateException.Error.ADDRESSEE_NOT_FOUND, msg);
      }
    }
  }
  public void handleIncomingUpdate(Request msg) throws DialogStateException {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (!done.get()) {
      Logger.log("Remote party has sent update");

      final Dialog dialog = getStackContext().getDialogStorage().findDialogForMessage(msg);
      assert dialog != null;

      checkUpdatePreconditions(dialog, msg);

      Logger.log(TAG, "mark dialog as update in progress");
      dialog.markUpdateInProgress(InitiateParty.REMOTE);

      // TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction>
      // transactionType = SIP_REINVITE_SERVER;
      TransactionType<UpdateSrvTransaction, UpdateServerTransaction> transactionType =
          SIP_UPDATE_SERVER;

      doHandleIncomingUpdate(msg, dialog, transactionType);
    }
  }
  /**
   * Handles server noninvite message
   *
   * @param msg - noninvite message
   */
  public void handleIncomingBye(final Request msg) {

    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    assert msg != null && MessageType.SIP_BYE == MessageType.parse(msg.getMethod());

    Logger.log("Remote party has sent noninvite");
    if (!done.get()) {

      final Dialog dialog = getStackContext().getDialogStorage().findDialogForMessage(msg);
      assert dialog != null;
      assert STATED == dialog.getState();

      dialog.getMessageHistory().addMessage(msg, true);

      final TransactionManager transactionManager = getTransactionManager();
      transactionManager.addListener(
          new FirstMessageResolver(SIP_BYE_SERVER.getName(), dialog, msg, transactionManager));

      final Transaction transaction =
          transactionManager.lookUpTransaction(dialog, null, SIP_BYE_SERVER);
      runAsynchronously(transaction, TRANSACTION_TIMEOUT);
    }
  }
  public InviteServiceImpl(
      final StackContext stackContext, final TransactionManager transactionManager) {
    super(stackContext, transactionManager);

    transactionSafeView = TransactionUtils.wrap(this, InviteService.class);

    subscribeToTransactionManager();
  }
  protected void onTransactionInited() {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();

    final TransactionStateChangeEvent<BaseSipMessage> event =
        DefaultTransactionStateChangeEvent.createInitEvent(this, getInitialMessage());

    transitToState(new TryingState(this), event);
  }
  private void preAccept(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    Logger.log(TAG, "preAccept");

    if (!done.get()) {
      doPreAccept(dialog);
    }
  }
  private void doBye(final Dialog dialog, final TimeoutUnit timeoutUnit) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    final Transaction transaction =
        getTransactionManager().lookUpTransaction(dialog, null, SIP_BYE_CLIENT);

    runAsynchronously((Transaction<Boolean, BaseSipMessage>) transaction, timeoutUnit);
  }
  /**
   * Cancels establishing DIALOG
   *
   * @param dialog - DIALOG to cancel
   */
  public void cancel(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    Logger.log("Canceling call");

    if (!done.get()) {
      doCancel(dialog);
    }
  }
  private void doUpdate(Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (dialog.getInitiateParty() == InitiateParty.LOCAL) {
      getTransactionManager().findTransaction(dialog, SIP_INVITE_CLIENT).update();
    } else {
      getTransactionManager().findTransaction(dialog, SIP_INVITE_SERVER).update();
    }
  }
  /**
   * This method initiates SIP_BYE for established DIALOG
   *
   * @param dialog - DIALOG to terminate
   */
  public void bye(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (!done.get()) {
      assert STATED == dialog.getState()
          : "Wrong dialog state. Must be " + STATED + " now is " + dialog.getState();
      doBye(dialog, TRANSACTION_TIMEOUT);
    }
  }
  private void doPreAccept(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction> transactionType;

    if (dialog.isReInviteInProgress()) {
      transactionType = SIP_REINVITE_SERVER;
    } else {
      transactionType = SIP_INVITE_SERVER;
    }

    getTransactionManager().findTransaction(dialog, transactionType).preAccept();
  }
  public void update(Dialog dialog) throws DialogStateException {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    assert getStackContext().getDialogStorage().findDialogByCallId(dialog.getCallId()) != null
        : "DIALOG being updated is already  terminated";

    if (!done.get()) {

      checkUpdatePreconditions(dialog, null);
      dialog
          .getOutgoingSdpMessage()
          .setSessionVersion(dialog.getOutgoingSdpMessage().getSessionVersion() + 1);
      doUpdate(dialog);
    }
  }
  public void reInvite(final Dialog dialog) throws DialogStateException {
    Logger.log(TAG, "reInvite#started");
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    assert getStackContext().getDialogStorage().findDialogByCallId(dialog.getCallId()) != null
        : "DIALOG being re-invited is already  terminated";

    if (!done.get()) {

      checkReInvitePreconditions(dialog, null);
      dialog
          .getOutgoingSdpMessage()
          .setSessionVersion(dialog.getOutgoingSdpMessage().getSessionVersion() + 1);
      doReInvite(dialog, LONG_TRANSACTION_TIMEOUT);
    }
    Logger.log(TAG, "reInvite#finished");
  }
  /**
   * Rejects server invite
   *
   * @param dialog - associated DIALOG
   * @param statusCode - status code to send to remote party
   * @param alternativeUserAddress - alternative USER address for status code 302(Moved)
   */
  private void doReject(
      final Dialog dialog, final int statusCode, final String alternativeUserAddress) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction> transactionType;
    if (dialog.isReInviteInProgress()) {
      transactionType = SIP_REINVITE_SERVER;
    } else {
      transactionType = SIP_INVITE_SERVER;
    }

    assert transactionType == SIP_INVITE_SERVER || statusCode == 488;

    getTransactionManager()
        .findTransaction(dialog, transactionType)
        .reject(statusCode, alternativeUserAddress);
  }
  private void doRejectUpdate(
      final Dialog dialog, final int statusCode, final String alternativeUserAddress) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (dialog.getInitiateParty() == InitiateParty.LOCAL) {
      getTransactionManager()
          .findTransaction(dialog, SIP_INVITE_CLIENT)
          .rejectUpdate(statusCode, alternativeUserAddress);
    } else {
      // getTransactionManager().findTransaction(dialog, SIP_INVITE_SERVER).rejectUpdate(statusCode,
      // alternativeUserAddress);
      TransactionType<UpdateSrvTransaction, UpdateServerTransaction> transactionType =
          SIP_UPDATE_SERVER;
      getTransactionManager()
          .findTransaction(dialog, transactionType)
          .reject(statusCode, alternativeUserAddress);
    }
  }
  private void doCancel(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    final InviteClntTransaction transaction =
        getTransactionManager().findTransaction(dialog, SIP_INVITE_CLIENT);

    final DialogStateEvent<BaseSipMessage> stateEvent =
        new DefaultDialogStateEvent<BaseSipMessage>(dialog, SessionState.SESSION_TERMINATED, null);

    dialogStateListenerHolder.getNotifier().onSessionTerminated(stateEvent);
    // dialogStateListenerHolder.getNotifier().onSessionEnded(stateEvent);
    // DIALOG.putCustomParameter(ParamKey.INITIAL_MESSAGE, ((Transaction)
    // transaction).getInitialMessage());

    if (transaction != null) {
      transaction.cancel();
    }
  }
  private SteppedAcceptable createSafeInviteAcceptable(final Dialog dialog) {
    return TransactionUtils.wrap(
        new SteppedAcceptable<Dialog>() {

          private final Dialog dlg = dialog;

          public void reject(Dialog parameter, int statusCode, String alternativeUserAddress) {
            if (dialog.isUpdateInProgress()) {
              InviteServiceImpl.this.doRejectUpdate(dlg, statusCode, alternativeUserAddress);
              dialog.unmarkUpdateInProgress();
            } else {
              InviteServiceImpl.this.doReject(dlg, statusCode, alternativeUserAddress);
            }
          }

          public void accept(Dialog parameter) {
            Logger.log(
                "SteppedAcceptable",
                "accept#dialog.isUpdateInProgress() = " + dialog.isUpdateInProgress());

            if (dialog.isUpdateInProgress()) {
              Logger.log("SteppedAcceptable", "doAcceptUpdate");
              InviteServiceImpl.this.doAcceptUpdate(dialog);
              dialog.unmarkUpdateInProgress();
            } else {
              Logger.log("SteppedAcceptable", "doAccept");
              InviteServiceImpl.this.doAccept(dlg);
            }
          }

          public void preAccept() {
            if (!dialog.isUpdateInProgress()) {
              InviteServiceImpl.this.preAccept(dlg);
            } else {
              assert false : "Method not supported";
            }
          }
        },
        SteppedAcceptable.class);
  }
  public void handleIncomingReInvite(final Request msg) throws DialogStateException {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    assert msg != null && MessageType.SIP_INVITE == MessageType.parse(msg.getMethod());

    if (!done.get()) {
      Logger.log("Remote party has sent ReInvite");

      final Dialog dialog = getStackContext().getDialogStorage().findDialogForMessage(msg);
      assert dialog != null;

      checkReInvitePreconditions(dialog, msg);
      dialog.markReInviteInProgress(InitiateParty.REMOTE);

      TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction>
          transactionType = SIP_REINVITE_SERVER;

      doHandleIncomingInvite(msg, dialog, transactionType);
    }
  }
  /** This method tries to invite remote party */
  public void invite(final Dialog dialog) throws DialogStateException {
    Logger.log(TAG, "invite#started");
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (!done.get()) {
      if (STATED == dialog.getState()) {
        throw new DialogStateException(
            dialog,
            INVITE_FOR_STATED_DIALOG,
            null,
            "Can not invite remote party. Dialog is already stated.");
      }

      if (getStackContext().getConfig().useInviteRefresh()) {
        addDialogStateListener(dialog, listenerForInviteRefresh);
      }
      doInvite(dialog, LONG_TRANSACTION_TIMEOUT);
    }
    Logger.log(TAG, "invite#finished");
  }
  private void doAcceptUpdate(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (dialog.isReInviteInProgress() || dialog.isUpdateInProgress()) {
      // transactionType = SIP_REINVITE_SERVER;
      TransactionType<UpdateSrvTransaction, UpdateServerTransaction> transactionType =
          SIP_UPDATE_SERVER;
      getTransactionManager().findTransaction(dialog, transactionType).accept();
    } else {
      TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction>
          transactionType = SIP_INVITE_SERVER;
      getTransactionManager().findTransaction(dialog, transactionType).acceptUpdate();
    }

    /*        if (dialog.getInitiateParty() == InitiateParty.LOCAL) {
                getTransactionManager().findTransaction(dialog, SIP_INVITE_CLIENT).acceptUpdate();
            }
            else {
                getTransactionManager().findTransaction(dialog, SIP_INVITE_SERVER).acceptUpdate();
            }
    */ }