package com.rayo.server.verb;
import javax.validation.ConstraintValidatorContext;
import com.rayo.core.validation.ValidationException;
import com.rayo.core.verb.Output;
import com.rayo.core.verb.OutputCompleteEvent;
import com.rayo.core.verb.PauseCommand;
import com.rayo.core.verb.ResumeCommand;
import com.rayo.core.verb.SayCompleteEvent.Reason;
import com.rayo.core.verb.SeekCommand;
import com.rayo.core.verb.SpeedDownCommand;
import com.rayo.core.verb.SpeedUpCommand;
import com.rayo.core.verb.Ssml;
import com.rayo.core.verb.VerbCommand;
import com.rayo.core.verb.VerbCompleteEvent;
import com.rayo.core.verb.VolumeDownCommand;
import com.rayo.core.verb.VolumeUpCommand;
import com.rayo.server.CallActor;
import com.rayo.server.exception.ExceptionMapper;
import com.rayo.server.validation.SsmlValidator;
import com.voxeo.logging.Loggerf;
import com.voxeo.moho.MediaService;
import com.voxeo.moho.Participant;
import com.voxeo.moho.State;
import com.voxeo.moho.media.output.AudibleResource;
import com.voxeo.moho.media.output.OutputCommand;
import com.voxeo.moho.media.output.OutputCommand.BehaviorIfBusy;
import com.voxeo.servlet.xmpp.StanzaError;
public class OutputHandler extends AbstractLocalVerbHandler<Output, Participant> {
private static final Loggerf logger = Loggerf.getLogger(OutputHandler.class);
private com.voxeo.moho.media.Output<Participant> output;
private SsmlValidator ssmlValidator;
// Verb Lifecycle
// ================================================================================
@Override
public void start() {
Ssml prompt = model.getPrompt();
AudibleResource audibleResource = resolveAudio(prompt);
OutputCommand outcommand = new OutputCommand(audibleResource);
outcommand.setBahavior(BehaviorIfBusy.STOP);
if (model.getBargeinType() != null) {
outcommand.setBargeinType(model.getBargeinType());
}
if (model.getStartOffset() != null) {
outcommand.setStartingOffset(model.getStartOffset().getMillis());
}
if (model.isStartPaused() != null) {
outcommand.setStartInPausedMode(model.isStartPaused());
}
if (model.getRepeatInterval() != null) {
outcommand.setRepeatInterval(model.getRepeatInterval().getMillis());
}
if (model.getRepeatTimes() != null) {
outcommand.setRepeatTimes(model.getRepeatTimes());
}
if (model.getMaxTime() != null) {
outcommand.setMaxtime(model.getMaxTime().getMillis());
}
if (prompt.getVoice() != null) {
outcommand.setVoiceName(prompt.getVoice());
}
output = getMediaService().output(outcommand);
if (model.getBroadcast() != null && model.getBroadcast()) {
if (getActor() instanceof CallActor) {
CallActor actor = (CallActor)getActor();
for(Object o: actor.getJoinees()) {
MediaService<Participant> service = (MediaService<Participant>)o;
service.output(outcommand);
}
}
}
}
@Override
public boolean isStateValid(ConstraintValidatorContext context) {
if (!isReady(participant)) {
context.buildConstraintViolationWithTemplate("Call is not ready yet.")
.addNode(ExceptionMapper.toString(StanzaError.Condition.RESOURCE_CONSTRAINT))
.addConstraintViolation();
return false;
}
if (isOnHold(participant)) {
context.buildConstraintViolationWithTemplate("Call is currently on hold.")
.addNode(ExceptionMapper.toString(StanzaError.Condition.RESOURCE_CONSTRAINT))
.addConstraintViolation();
return false;
}
if (!canManipulateMedia()) {
context.buildConstraintViolationWithTemplate("Media operations are not allowed in the current call status.")
.addNode(ExceptionMapper.toString(StanzaError.Condition.RESOURCE_CONSTRAINT))
.addConstraintViolation();
return false;
}
return true;
}
// Commands
// ================================================================================
public void stop(boolean hangup) {
if (hangup) {
complete(new OutputCompleteEvent(model, VerbCompleteEvent.Reason.HANGUP));
} else {
if (output != null) {
output.stop();
}
}
}
@Override
public void onCommand(VerbCommand command) {
if (command instanceof PauseCommand) {
pause();
} else if (command instanceof ResumeCommand) {
resume();
} else if (command instanceof SeekCommand) {
seek(((SeekCommand) command));
} else if (command instanceof SpeedUpCommand) {
speedUp((SpeedUpCommand) command);
} else if (command instanceof SpeedDownCommand) {
speedDown((SpeedDownCommand) command);
} else if (command instanceof VolumeUpCommand) {
volumeUp((VolumeUpCommand) command);
} else if (command instanceof VolumeDownCommand) {
volumeDown((VolumeDownCommand) command);
}
}
@State
public void pause() {
output.pause();
}
@State
public void resume() {
output.resume();
}
public void seek(SeekCommand command) {
if (command.getDirection() == SeekCommand.Direction.FORWARD) {
output.move(true, command.getAmount());
} else {
output.move(false, command.getAmount());
}
}
public void speedUp(SpeedUpCommand command) {
output.speed(true);
}
public void speedDown(SpeedDownCommand command) {
output.speed(false);
}
public void volumeUp(VolumeUpCommand command) {
output.volume(true);
}
public void volumeDown(VolumeDownCommand command) {
output.volume(false);
}
// Moho Events
// ================================================================================
@State
public void onSpeakComplete(com.voxeo.moho.event.OutputCompleteEvent<Participant> event) {
if (!event.getMediaOperation().equals(output)) {
logger.debug("Ignoring complete event as it is targeted to a different media operation");
return;
}
switch (event.getCause()) {
case BARGEIN:
case END:
complete(new OutputCompleteEvent(model, Reason.SUCCESS));
break;
case DISCONNECT:
complete(new OutputCompleteEvent(model, VerbCompleteEvent.Reason.HANGUP));
break;
case CANCEL:
complete(new OutputCompleteEvent(model, VerbCompleteEvent.Reason.STOP));
break;
case ERROR:
case UNKNOWN:
complete(new OutputCompleteEvent(model, VerbCompleteEvent.Reason.ERROR, findErrorCause(event)));
case TIMEOUT:
complete(new OutputCompleteEvent(model, VerbCompleteEvent.Reason.ERROR, "Timeout"));
break;
}
}
private String findErrorCause(com.voxeo.moho.event.OutputCompleteEvent<Participant> event) {
try {
ssmlValidator.validateSsml(model.getPrompt().getText());
} catch (ValidationException ve) {
return ve.getMessage();
}
String cause = event.getErrorText();
if (cause != null) {
if (cause.startsWith("NOT_FOUND")) {
cause = "Could not find the Resource's URI";
}
} else {
cause = "Unknown cause";
}
return cause;
}
public void setSsmlValidator(SsmlValidator ssmlValidator) {
this.ssmlValidator = ssmlValidator;
}
}