@Override
  public void onReceive(final Object message) throws Exception {
    final Class<?> klass = message.getClass();
    final State state = fsm.state();
    final ActorRef sender = sender();
    final ActorRef source = self();

    if (logger.isInfoEnabled()) {
      logger.info(" ********** UssdInterpreter's Current State: " + state.toString());
      logger.info(" ********** UssdInterpreter's Processing Message: " + klass.getName());
    }

    if (StartInterpreter.class.equals(klass)) {
      ussdCall = ((StartInterpreter) message).resource();
      fsm.transition(message, acquiringCallInfo);
    } else if (message instanceof SipServletRequest) {
      SipServletRequest request = (SipServletRequest) message;
      String method = request.getMethod();
      if ("INFO".equalsIgnoreCase(method)) {
        fsm.transition(message, processingInfoRequest);
      } else if ("ACK".equalsIgnoreCase(method)) {
        fsm.transition(message, downloadingRcml);
      } else if ("BYE".equalsIgnoreCase(method)) {
        fsm.transition(message, disconnecting);
      } else if ("CANCEL".equalsIgnoreCase(method)) {
        fsm.transition(message, cancelling);
      }
    } else if (CallStateChanged.class.equals(klass)) {
      final CallStateChanged event = (CallStateChanged) message;
      callState = event.state();
      if (CallStateChanged.State.RINGING == event.state()) {
        if (logger.isInfoEnabled()) {
          logger.info("CallStateChanged.State.RINGING");
        }
      } else if (CallStateChanged.State.IN_PROGRESS == event.state()) {
        if (logger.isInfoEnabled()) {
          logger.info("CallStateChanged.State.IN_PROGRESS");
        }
      } else if (CallStateChanged.State.NO_ANSWER == event.state()
          || CallStateChanged.State.COMPLETED == event.state()
          || CallStateChanged.State.FAILED == event.state()
          || CallStateChanged.State.CANCELED == event.state()) {
        if (logger.isInfoEnabled()) {
          logger.info(
              "CallStateChanged.State.NO_ANSWER OR  CallStateChanged.State.COMPLETED OR CallStateChanged.State.FAILED or CallStateChanged.State.CANCELED");
        }
        fsm.transition(message, finished);
      } else if (CallStateChanged.State.BUSY == event.state()) {
        if (logger.isInfoEnabled()) {
          logger.info("CallStateChanged.State.BUSY");
        }
      }
      //            else if (CallStateChanged.State.COMPLETED == event.state()) {
      //                logger.info("CallStateChanged.State.Completed");
      //                fsm.transition(message, finished);
      //            }
    } else if (CallResponse.class.equals(klass)) {
      if (acquiringCallInfo.equals(state)) {
        @SuppressWarnings("unchecked")
        final CallResponse<CallInfo> response = (CallResponse<CallInfo>) message;
        // Check from whom is the message (initial call or outbound call) and update info
        // accordingly
        if (sender == ussdCall) {
          callInfo = response.get();
        } else {
          outboundCallInfo = response.get();
        }
        final String direction = callInfo.direction();
        if ("inbound".equals(direction)) {
          ussdCall.tell(new Answer(callInfo.sid()), source);
          // fsm.transition(message, downloadingRcml);
        } else {
          fsm.transition(message, downloadingRcml);
          //                     fsm.transition(message, initializingCall);
        }
      }
    } else if (DownloaderResponse.class.equals(klass)) {
      final DownloaderResponse response = (DownloaderResponse) message;
      if (response.succeeded() && HttpStatus.SC_OK == response.get().getStatusCode()) {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Rcml URI : "
                  + response.get().getURI()
                  + "response succeeded "
                  + response.succeeded()
                  + ", statusCode "
                  + response.get().getStatusCode());
        }
        fsm.transition(message, ready);
      } else if (response.succeeded()
          && HttpStatus.SC_NOT_FOUND == response.get().getStatusCode()) {
        fsm.transition(message, notFound);
      } else {
        if (downloadingRcml.equals(state)) {
          if (fallbackUrl != null) {
            fsm.transition(message, downloadingFallbackRcml);
          } else {
            fsm.transition(message, finished);
          }
        } else {
          fsm.transition(message, finished);
        }
      }
    } else if (ParserFailed.class.equals(klass)) {
      if (logger.isInfoEnabled()) {
        logger.info("ParserFailed received. Will stop the call");
      }
      fsm.transition(message, cancelling);
    } else if (Tag.class.equals(klass)) {
      final Tag verb = (Tag) message;
      if (ussdLanguage.equals(verb.name())) {
        if (ussdLanguageTag == null) {
          ussdLanguageTag = verb;
          final GetNextVerb next = GetNextVerb.instance();
          parser.tell(next, source);
        } else {
          // We support only one Language element
          invalidVerb(verb);
        }
        return;
      } else if (ussdMessage.equals(verb.name())) {
        ussdMessageTags.add(verb);
        final GetNextVerb next = GetNextVerb.instance();
        parser.tell(next, source);
        return;
      } else if (ussdCollect.equals(verb.name())) {
        if (ussdCollectTag == null) {
          ussdCollectTag = verb;
          final GetNextVerb next = GetNextVerb.instance();
          parser.tell(next, source);
        } else {
          // We support only one Collect element
          invalidVerb(verb);
        }
        return;
      } else {
        invalidVerb(verb);
      }
    } else if (End.class.equals(klass)) {
      fsm.transition(message, preparingMessage);
    }
  }
  // FSM logic.
  @Override
  public void onReceive(final Object message) throws Exception {
    final Class<?> klass = message.getClass();
    final State state = fsm.state();
    final ActorRef sender = sender();

    if (logger.isInfoEnabled()) {
      logger.info(
          "********** Media Group " + self().path() + " Current State: \"" + state.toString());
      logger.info(
          "********** Media Group "
              + self().path()
              + " Processing Message: \""
              + klass.getName()
              + " sender : "
              + sender.getClass());
    }

    if (Observe.class.equals(klass)) {
      observe(message);
    } else if (StopObserving.class.equals(klass)) {
      stopObserving(message);
    } else if (MediaGroupStatus.class.equals(klass)) {
      if (active.equals(state)) {
        sender().tell(new MediaGroupStateChanged(MediaGroupStateChanged.State.ACTIVE), self());
      } else {
        sender().tell(new MediaGroupStateChanged(MediaGroupStateChanged.State.INACTIVE), self());
      }
    } else if (StartMediaGroup.class.equals(klass)) {
      if (logger.isInfoEnabled()) {
        logger.info(
            "MediaGroup: "
                + self().path()
                + " got StartMediaGroup from: "
                + sender().path()
                + " endpoint: "
                + endpoint.path()
                + " isTerminated: "
                + endpoint.isTerminated());
      }
      fsm.transition(message, acquiringIvr);
    } else if (Join.class.equals(klass)) {
      fsm.transition(message, acquiringInternalLink);
    } else if (MediaGatewayResponse.class.equals(klass)) {
      if (acquiringIvr.equals(state)) {
        fsm.transition(message, acquiringLink);
      } else if (acquiringLink.equals(state)) {
        fsm.transition(message, initializingLink);
      } else if (acquiringInternalLink.equals(state)) {
        fsm.transition(message, initializingInternalLink);
      }
    } else if (LinkStateChanged.class.equals(klass)) {
      final LinkStateChanged response = (LinkStateChanged) message;
      if (LinkStateChanged.State.CLOSED == response.state()) {
        if (initializingLink.equals(state)) {
          fsm.transition(message, openingLink);
        } else if (openingLink.equals(state)
            || deactivating.equals(state)
            || updatingLink.equals(state)) {
          fsm.transition(message, inactive);
        }
        if (initializingInternalLink.equals(state)) {
          fsm.transition(message, openingInternalLink);
        }
      } else if (LinkStateChanged.State.OPEN == response.state()) {
        if (openingLink.equals(state)) {
          fsm.transition(message, updatingLink);
        } else if (updatingLink.equals(state)) {
          fsm.transition(message, active);
        }
        if (openingInternalLink.equals(state)) {
          fsm.transition(message, updatingInternalLink);
        }
        if (updatingInternalLink.equals(state)) {
          fsm.transition(message, active);
        }
      }
    } else if (StopMediaGroup.class.equals(klass)) {
      if (acquiringLink.equals(state) || initializingLink.equals(state)) {
        fsm.transition(message, inactive);
      } else if (active.equals(state) || openingLink.equals(state) || updatingLink.equals(state)) {
        fsm.transition(message, deactivating);
      }
    } else if (EndpointStateChanged.class.equals(klass)) {
      onEndpointStateChanged((EndpointStateChanged) message, self(), sender);
    } else if (active.equals(state)) {
      if (Play.class.equals(klass)) {
        play(message);
      } else if (Collect.class.equals(klass)) {
        collect(message);
      } else if (Record.class.equals(klass)) {
        record(message);
      } else if (Stop.class.equals(klass)) {
        stop(lastEvent);
        // Send message to originator telling media group has been stopped
        // Needed for call bridging scenario, where inbound call must stop
        // ringing before attempting to perform join operation.
        sender().tell(new MediaGroupResponse<String>("stopped"), self());
      } else if (IvrEndpointResponse.class.equals(klass)) {
        notification(message);
      }
    } else if (ivrInUse) {
      if (Stop.class.equals(klass)) {
        stop(lastEvent);
      }
    }
  }