package iamrescue.communication;
import iamrescue.communication.failuredetection.SentMessageMemory;
import iamrescue.communication.messages.Message;
import iamrescue.communication.messages.MessageChannel;
import iamrescue.communication.messages.MessageChannelType;
import iamrescue.communication.messages.filter.IsSentMessageFilter;
import iamrescue.communication.util.ByteArray;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javolution.util.FastList;
import javolution.util.FastMap;
import org.apache.commons.collections15.CollectionUtils;
import org.apache.commons.collections15.functors.NotPredicate;
import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
public abstract class AOutgoingMessageSelector implements
IOutgoingMessageSelector {
private IOutgoingMessageService outgoingMessageService;
private IEncoder encoder;
private SentMessageMemory memory;
private static final boolean CONCATENATE_RADIO_MESSAGES = false;
private static final Logger LOGGER = Logger
.getLogger(AOutgoingMessageSelector.class);
public AOutgoingMessageSelector(
IOutgoingMessageService outgoingMessageService, IEncoder encoder,
SentMessageMemory memory) {
this.outgoingMessageService = outgoingMessageService;
this.encoder = encoder;
this.memory = memory;
}
/**
* Will send max 'maximimumMessageCount' messages in the collection to
* specified channel
*
* @param messages
* @param channel
* @param maximumMessageCount
*/
protected void sendMessages(Collection<Message> messages,
MessageChannel channel, int maximumMessageCount,
int maximumMessageSize, int maximumTotalBandwidth,
int maximumRepetitions, int timeStep) {
Validate.notNull(messages, "List of messages cannot be null");
// log.debug("Sending Message: " + messages);
int messageCount = 0;
int bandwidthLeft = maximumTotalBandwidth;
if (messages.size() == 0) {
return;
}
// Always concatenate on voice channel
boolean voice = channel.getType().equals(MessageChannelType.VOICE);
boolean concatenate = CONCATENATE_RADIO_MESSAGES || voice;
// Improve this later!
boolean failuresPossible = true;// channel.getOverallFailureProbability()
// > 0;
// LOGGER.debug("Entering send");
List<Message> repeated = new FastList<Message>();
if (failuresPossible && maximumMessageCount > 1) {
for (Message m : messages) {
repeated.add(m.copy());
}
}
// List<Message> messageList = messages;
Map<ByteArray, List<Message>> sentBytes = new FastMap<ByteArray, List<Message>>();
int repetition = 0;
LOGGER.info("Repetitions: " + maximumRepetitions + "failures: "
+ failuresPossible);
do {
if (messageCount > 0) {
// Repeating? Copy from repeated list
messages = new FastList<Message>();
for (Message m : repeated) {
messages.add(m.copy());
}
}
// LOGGER.debug("After do");
int messageNumber = 0;
int totalSize = 0;
StringBuffer sb = null;
if (LOGGER.isInfoEnabled()) {
sb = new StringBuffer();
}
while (!messages.isEmpty() && messageCount < maximumMessageCount
&& bandwidthLeft > 0) {
// LOGGER.debug("In while");
int available = Math.min(bandwidthLeft, maximumMessageSize);
// LOGGER.debug("Encode " + messages);
byte[] encodedMessage;
Message sentMessage = null;
if (concatenate) {
encodedMessage = getEncoder().encodeMessages(messages,
available);
} else {
sentMessage = messages.iterator().next();
encodedMessage = getEncoder().encodeMessages(
Collections.singleton(sentMessage), available);
}
// LOGGER.debug("Done " + Arrays.toString(encodedMessage));
if (encodedMessage.length == 0) {
// no more space
bandwidthLeft = 0;
} else {
getOutgoingMessageService().sendMessage(encodedMessage,
channel);
// Remember sent messages
// if (messageCount == 0) {
List<Message> sent = new FastList<Message>();
if (sentMessage != null) {
sent.add(sentMessage);
messages.remove(sentMessage);
} else {
Iterator<Message> iterator = messages.iterator();
while (iterator.hasNext()) {
Message message = iterator.next();
if (message.isSent()) {
sent.add(message);
iterator.remove();
}
}
}
if (LOGGER.isInfoEnabled()) {
messageNumber += sent.size();
if (sb.length() > 0) {
sb.append(',');
sb.append(' ');
}
sb.append(encodedMessage.length);
totalSize += encodedMessage.length;
}
if (repetition == 0) {
sentBytes.put(new ByteArray(encodedMessage), sent);
}
// } else {
// filterSentMessages(messages);
// }
bandwidthLeft -= encodedMessage.length;
messageCount++;
}
}
repetition++;
if (LOGGER.isInfoEnabled()) {
String prefix = (repetition == 1) ? "" : "(Repeating) ";
LOGGER.info(prefix + "Encoded " + messageNumber
+ " messages in " + totalSize + " bytes. Sizes: "
+ sb.toString());
}
// LOGGER.debug("End of loop 1");
} while (failuresPossible && messageCount < maximumMessageCount
&& bandwidthLeft > 0 && repetition <= maximumRepetitions);
if (!voice) {
memory.setSentMessages(channel, timeStep, sentBytes);
}
// LOGGER.debug("End of loop 2");
}
protected void filterSentMessages(Collection<Message> messages) {
CollectionUtils.filter(messages, NotPredicate
.getInstance(new IsSentMessageFilter()));
}
public final void setOutgoingMessageService(
IOutgoingMessageService outgoingMessageService) {
this.outgoingMessageService = outgoingMessageService;
}
protected IEncoder getEncoder() {
return encoder;
}
protected IOutgoingMessageService getOutgoingMessageService() {
return outgoingMessageService;
}
}