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.core.AcceptCommand;
import com.rayo.core.AnswerCommand;
import com.rayo.core.RedirectCommand;
import com.rayo.core.RejectCommand;
import com.voxeo.moho.Call;
import com.voxeo.moho.CallableEndpoint;
import com.voxeo.moho.Endpoint;
import com.voxeo.moho.IncomingCall;
import com.voxeo.moho.Joint;
import com.voxeo.moho.MediaException;
import com.voxeo.moho.SignalException;
import com.voxeo.moho.common.event.MohoJoinCompleteEvent;
import com.voxeo.moho.event.JoinCompleteEvent;
import com.voxeo.moho.event.Observer;
import com.voxeo.moho.remote.MohoRemoteException;
import com.voxeo.rayo.client.XmppException;
import com.voxeo.rayo.client.xmpp.stanza.IQ;
public class IncomingCallImpl extends CallImpl implements IncomingCall {
private static final Logger LOG = Logger.getLogger(IncomingCallImpl.class);
protected boolean isRejected;
protected boolean isAccepted;
protected boolean isRedirected;
protected boolean isAcceptedWithEarlyMedia;
protected JointImpl waitAnswerJoint = null;
public IncomingCallImpl(MohoRemoteImpl mohoRemote, String callID, CallableEndpoint caller, CallableEndpoint callee,
Map<String, String> headers) {
super(mohoRemote, callID, caller, callee, headers);
this.setCallState(Call.State.INITIALIZED);
}
@Override
public boolean isAccepted() {
return isAccepted;
}
@Override
public boolean isRejected() {
return isRejected;
}
@Override
public void reject(Reason reason) throws SignalException {
reject(reason, null);
}
@Override
public void reject(Reason reason, Map<String, String> headers) throws SignalException {
try {
RejectCommand command = new RejectCommand();
command.setHeaders(headers);
command.setCallId(this.getId());
command.setReason(getRayoCallRejectReasonByMohoReason(reason));
IQ iq = _mohoRemote.getRayoClient().command(command, this.getId());
if (iq.isError()) {
this.setCallState(Call.State.FAILED);
com.voxeo.rayo.client.xmpp.stanza.Error error = iq.getError();
throw new SignalException(error.getCondition() + error.getText());
}
else {
isRejected = true;
this.setCallState(Call.State.DISCONNECTED);
}
}
catch (XmppException e) {
LOG.error("", e);
throw new MohoRemoteException(e);
}
}
@Override
public boolean isRedirected() {
return isRedirected;
}
@Override
public void redirect(Endpoint other) throws SignalException {
redirect(other, null);
}
@Override
public void redirect(Endpoint other, Map<String, String> headers) throws SignalException {
RedirectCommand redirect = new RedirectCommand();
redirect.setTo(other.getURI());
redirect.setCallId(this.getId());
redirect.setHeaders(headers);
try {
IQ iq = _mohoRemote.getRayoClient().command(redirect, this.getId());
if (iq.isError()) {
this.setCallState(Call.State.FAILED);
com.voxeo.rayo.client.xmpp.stanza.Error error = iq.getError();
throw new SignalException(error.getCondition() + error.getText());
}
else {
isRedirected = true;
this.setCallState(Call.State.DISCONNECTED);
}
}
catch (XmppException e) {
LOG.error("", e);
throw new MohoRemoteException(e);
}
}
@Override
public boolean isAcceptedWithEarlyMedia() {
return isAcceptedWithEarlyMedia;
}
@Override
public void acceptWithEarlyMedia() throws SignalException, MediaException {
acceptWithEarlyMedia((Map<String, String>) null);
}
@Override
public void acceptWithEarlyMedia(Observer... observer) throws SignalException, MediaException {
addObserver(observer);
acceptWithEarlyMedia();
}
@Override
public void acceptWithEarlyMedia(Map<String, String> headers) throws SignalException, MediaException {
try {
AcceptCommand command = new AcceptCommand();
command.setHeaders(headers);
command.setCallId(this.getId());
command.setEarlyMedia(true);
IQ iq = _mohoRemote.getRayoClient().command(command, this.getId());
if (iq.isError()) {
this.setCallState(Call.State.FAILED);
com.voxeo.rayo.client.xmpp.stanza.Error error = iq.getError();
throw new SignalException(error.getCondition() + error.getText());
}
else {
isAcceptedWithEarlyMedia = true;
this.setCallState(Call.State.INPROGRESS);
}
}
catch (XmppException e) {
LOG.error("", e);
throw new MohoRemoteException(e);
}
}
@Override
public void accept() throws SignalException {
accept((Map<String, String>) null);
}
@Override
public void accept(Map<String, String> headers) throws SignalException {
try {
AcceptCommand command = new AcceptCommand();
command.setHeaders(headers);
command.setCallId(this.getId());
IQ iq = _mohoRemote.getRayoClient().command(command, this.getId());
if (iq.isError()) {
this.setCallState(Call.State.FAILED);
com.voxeo.rayo.client.xmpp.stanza.Error error = iq.getError();
throw new SignalException(error.getCondition() + error.getText());
}
else {
isAccepted = true;
this.setCallState(Call.State.ACCEPTED);
}
}
catch (XmppException e) {
LOG.error("", e);
throw new MohoRemoteException(e);
}
}
@Override
public void accept(Observer... observer) throws SignalException {
addObserver(observer);
accept();
}
@Override
public void answer() throws SignalException, MediaException {
this.answer((Map<String, String>) null);
}
@Override
public void answer(Observer... observer) throws SignalException, MediaException {
addObserver(observer);
answer();
}
@Override
public void answer(Map<String, String> headers) throws SignalException, MediaException {
final Joint joint = this.internalAnswer(null, headers, false);
while (!joint.isDone()) {
try {
joint.get();
}
catch (final InterruptedException e) {
// ignore
}
catch (final ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof SignalException) {
throw (SignalException) cause;
}
throw new SignalException(cause);
}
}
}
@Override
public void proxyTo(boolean recordRoute, boolean parallel, Endpoint... destinations) throws SignalException {
// TODO Auto-generated method stub
}
@Override
public boolean isProxied() {
// TODO Auto-generated method stub
return false;
}
@Override
public void proxyTo(boolean recordRoute, boolean parallel, Map<String, String> headers, Endpoint... destinations) {
// does rayo support proxyTo?
// TODO Auto-generated method stub
}
@Override
public Call getSource() {
return this;
}
@Override
public String startJoin() throws MohoRemoteException {
return _id;
}
// TODO this method is sync now.
// should make _mohoRemote.getRayoClient().answer() asynchronous, and make
// this method just send out command, the joint and event stuff should be
// processed in the join() method, wait for the result or error IQ.
private Joint internalAnswer(Direction direction, Map<String, String> headers, boolean dispatchJoinToMediaEvent) {
if (waitAnswerJoint == null) {
waitAnswerJoint = new JointImpl(this, direction);
MohoJoinCompleteEvent joinCompleteEvent = null;
try {
AnswerCommand command = new AnswerCommand();
command.setHeaders(headers);
command.setCallId(this.getId());
IQ iq = _mohoRemote.getRayoClient().answer(this.getId(), command);
if (iq.isError()) {
this.setCallState(Call.State.FAILED);
com.voxeo.rayo.client.xmpp.stanza.Error error = iq.getError();
SignalException exception = new SignalException(error.getCondition() + error.getText());
joinCompleteEvent = new MohoJoinCompleteEvent(this, null, JoinCompleteEvent.Cause.ERROR, exception, true);
if (dispatchJoinToMediaEvent) {
dispatch(joinCompleteEvent);
}
waitAnswerJoint.done(exception);
}
else {
isAccepted = true;
this.setCallState(Call.State.CONNECTED);
joinCompleteEvent = new MohoJoinCompleteEvent(this, null, JoinCompleteEvent.Cause.JOINED, true);
if (dispatchJoinToMediaEvent) {
dispatch(joinCompleteEvent);
}
waitAnswerJoint.done(joinCompleteEvent);
}
}
catch (XmppException e) {
LOG.error("", e);
throw new MohoRemoteException(e);
}
}
return waitAnswerJoint;
}
@Override
public Joint join(Direction direction) {
return internalAnswer(direction, null, true);
}
@Override
public Endpoint getAddress() {
return _caller;
}
@Override
public String getRemoteAddress() {
return _callee.getURI().toString();
}
@Override
public void setAsync(boolean async) {
}
@Override
public boolean isAsync() {
return false;
}
}