package com.voxeo.moho.remote.impl;

import java.util.Map;
import java.util.concurrent.ExecutionException;

import javax.media.mscontrol.join.Joinable.Direction;

import org.apache.log4j.Logger;

import com.rayo.client.XmppException;
import com.rayo.client.xmpp.stanza.Presence;
import com.rayo.core.AnsweredEvent;
import com.rayo.core.DialCommand;
import com.rayo.core.EndEvent;
import com.rayo.core.verb.VerbRef;
import com.voxeo.moho.Call;
import com.voxeo.moho.CallableEndpoint;
import com.voxeo.moho.Joint;
import com.voxeo.moho.OutgoingCall;
import com.voxeo.moho.SignalException;
import com.voxeo.moho.event.JoinCompleteEvent;
import com.voxeo.moho.event.RingEvent;
import com.voxeo.moho.remote.impl.event.MohoAnsweredEvent;
import com.voxeo.moho.remote.impl.event.MohoCallCompleteEvent;
import com.voxeo.moho.remote.impl.event.MohoHangupEvent;
import com.voxeo.moho.remote.impl.event.MohoJoinCompleteEvent;
import com.voxeo.moho.remote.impl.event.MohoRingEvent;

public class OutgoingCallImpl extends CallImpl implements OutgoingCall {
  private static final Logger LOG = Logger.getLogger(OutgoingCallImpl.class);

  protected JointImpl waitAnswerJoint = null;

  protected OutgoingCallImpl(MohoRemoteImpl mohoRemote, String callID, CallableEndpoint caller,
      CallableEndpoint callee, Map<String, String> headers) {
    super(mohoRemote, callID, caller, callee, headers);
  }

  protected synchronized void call() throws XmppException {
    if (getId() == null) {
      DialCommand command = new DialCommand();
      command.setFrom(_caller.getURI());
      command.setTo(_callee.getURI());
      command.setHeaders(_headers);
      VerbRef verbRef = _mohoRemote.getRayoClient().dial(command);
      setID(verbRef.getVerbId());
      _state = State.INITIALIZED;
    }
  }

  @Override
  public synchronized Joint join(Direction direction) {
    if (waitAnswerJoint == null) {
      waitAnswerJoint = new JointImpl(this, direction);
      try {
        call();
      }
      catch (XmppException e) {
        LOG.error("", e);
        waitAnswerJoint = null;
        throw new SignalException(e);
      }
    }
    else {
      if (waitAnswerJoint.isDone()) {
        try {
          JoinCompleteEvent event = waitAnswerJoint.get();
          dispatch(event);
        }
        catch (InterruptedException e) {
          // can't happen
        }
        catch (ExecutionException e) {
          // can't happen
        }
      }
    }

    return waitAnswerJoint;
  }

  private void setID(String id) {
    _id = id;
    if (_id != null) {
      _mohoRemote.addCall(this);
    }
  }

  @Override
  public void onRayoEvent(JID from, Presence presence) {
    Object object = presence.getExtension().getObject();
    if (object instanceof AnsweredEvent) {
      AnsweredEvent event = (AnsweredEvent) object;
      MohoAnsweredEvent<Call> mohoEvent = new MohoAnsweredEvent<Call>(this, event.getHeaders());
      _state = State.CONNECTED;
      this.dispatch(mohoEvent);

      if (waitAnswerJoint != null) {
        MohoJoinCompleteEvent joinComplete = new MohoJoinCompleteEvent(this, null, JoinCompleteEvent.Cause.JOINED, true);
        this.dispatch(joinComplete);
        waitAnswerJoint.done(joinComplete);
        waitAnswerJoint = null;
      }
    }
    else if (object instanceof RingEvent) {
      RingEvent event = (RingEvent) object;
      MohoRingEvent mohoEvent = new MohoRingEvent(this, event.getHeaders());
      this.dispatch(mohoEvent);
    }
    else if (object instanceof EndEvent) {
      EndEvent event = (EndEvent) object;
      EndEvent.Reason rayoReason = event.getReason();
      if (rayoReason == EndEvent.Reason.HANGUP) {
        MohoHangupEvent mohoEvent = new MohoHangupEvent(this);
        this.dispatch(mohoEvent);
      }

      if (_state == State.CONNECTED) {
        _state = State.DISCONNECTED;
      }
      else {
        _state = State.FAILED;
      }
      MohoCallCompleteEvent mohoEvent = new MohoCallCompleteEvent(this,
          getMohoReasonByRayoEndEventReason(event.getReason()), null, event.getHeaders());
      this.dispatch(mohoEvent);

      if (waitAnswerJoint != null) {
        MohoJoinCompleteEvent joinComplete = new MohoJoinCompleteEvent(this, null,
            getMohoJoinCompleteReasonByRayoEndEventReason(rayoReason), true);
        waitAnswerJoint.done(joinComplete);
        waitAnswerJoint = null;
      }

      cleanUp();
    }
    else {
      super.onRayoEvent(from, presence);
    }
  }

  public void startJoin() throws XmppException {
    call();
  }

  protected JoinCompleteEvent.Cause getMohoJoinCompleteReasonByRayoEndEventReason(EndEvent.Reason reason) {
    switch (reason) {
      case TIMEOUT:
        return JoinCompleteEvent.Cause.TIMEOUT;
      case BUSY:
        return JoinCompleteEvent.Cause.BUSY;
      case REJECT:
        return JoinCompleteEvent.Cause.REJECT;
      case REDIRECT:
        return JoinCompleteEvent.Cause.REDIRECT;
      case ERROR:
        return JoinCompleteEvent.Cause.ERROR;
      default:
        return JoinCompleteEvent.Cause.ERROR;
    }
  }
}

@@ -365,32 +365,67 @@
 .core.EndEvent;%0A
+import com.rayo.core.RingingEvent;%0A
 import com.rayo.
@@ -666,47 +666,8 @@
 nt;%0A
-import com.voxeo.moho.event.RingEvent;%0A
 impo
@@ -3285,24 +3285,27 @@
 tanceof Ring
+ing
 Event) %7B%0A   
@@ -3307,24 +3307,27 @@
 %7B%0A      Ring
+ing
 Event event 
@@ -3329,24 +3329,27 @@
 vent = (Ring
+ing
 Event) objec

fix #1563761 - Ringing Event on moho-remote.