/*
This file is part of Fantom.
Fantom is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fantom is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fantom. If not, see <http://www.gnu.org/licenses/>.
*/
package cz.matfyz.aai.fantom.message;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import cz.matfyz.aai.fantom.game.Actor;
import cz.matfyz.aai.fantom.game.Graph;
import cz.matfyz.aai.fantom.game.TransportType;
import cz.matfyz.aai.fantom.server.ProtocolException;
/**
* Message sent by server to clients informing about moves of the
* other player.
*/
public class MessageUpdate extends MessageMoveBase {
/**
* The name of the property that contains the ID of the captured phantoms.
*/
public static final String PROPERTY_CAPTURED = "captured";
/**
* The name of the property that contains the ID of the actor for the
* record with the number of tickets.
*/
public static final String PROPERTY_TICKETS = "tickets";
/**
* The name of the property that contains the type of the winning client.
*/
public static final String PROPERTY_WINNER = "winner";
/**
* Contains information about the numbers of tickets of a single actor.
*/
public class ActorTickets {
/**
* The actor, for which this object records the number of tickets.
*/
protected Actor actor;
/**
* The numbers of tickets, indexed by transport types.
*/
protected Map<TransportType, Integer> tickets;
/**
* The number of double tickets owned by the actor.
*/
protected int doubleTickets;
/**
* Returns the actor, for which this objects records the number of tickets.
* @return the actor object.
*/
public Actor getActor() {
return this.actor;
}
/**
* Returns the number of double tickets owned by the actor.
* @return the number of double tickets owned by the actor.
*/
public int getDoubleTickets() {
return this.doubleTickets;
}
/**
* Returns the numbers of tickets.
* @return the numbers of tickets.
*/
public Map<TransportType, Integer> getTickets() {
return this.tickets;
}
/**
* Loads the serialized information about the numbers of tickets.
* @param graph the game graph, for which the information is loaded.
* @param record the serialized data.
* @throws ProtocolException if there is a problem when loading the data.
*/
protected void loadProperties(Graph graph, Properties record) throws ProtocolException {
String actorId = record.getProperty(PROPERTY_TICKETS);
if(actorId == null || actorId.isEmpty())
throw new ProtocolException("No actor ID was specified", null);
this.actor = graph.getActor(actorId);
if(this.actor == null)
throw new ProtocolException("Unknown actor ID: " + actorId, null);
if(record.containsKey(Graph.PROPERTY_DOUBLE_TICKET)) {
String countStr = record.getProperty(Graph.PROPERTY_DOUBLE_TICKET);
try {
this.doubleTickets = Integer.parseInt(countStr);
}
catch(NumberFormatException e) {
throw new ProtocolException("Number of double tickets is not a valid number", null);
}
}
this.tickets = new HashMap<TransportType, Integer>();
for(Entry<Object, Object> prop : record.entrySet()) {
String key = (String)prop.getKey();
String countStr = (String)prop.getValue();
TransportType ttype = graph.getTransportType(key);
if(ttype == null)
continue;
try {
int count = Integer.parseInt(countStr);
tickets.put(ttype, count);
}
catch(NumberFormatException e) {
throw new ProtocolException("The number of tickets is not a valid number", null);
}
}
}
/**
* Serializes the information about the numbers of tickets of the actor.
* @return serialized information about the numbers of tickets.
*/
public Properties serialize() {
Properties res = new Properties();
res.setProperty(PROPERTY_TICKETS, actor.getId());
res.setProperty(Graph.PROPERTY_DOUBLE_TICKET, Integer.toString(doubleTickets));
for(Entry<TransportType, Integer> ttype : tickets.entrySet())
res.setProperty(ttype.getKey().getName(), ttype.getValue().toString());
return res;
}
/**
* Initializes the information about the number of tickets from the given
* actor object.
* @param actor the actor object, from which the number of tickets is initialized.
*/
public ActorTickets(Actor actor) {
if(actor == null)
throw new IllegalArgumentException("actor must not be null");
this.actor = actor;
this.doubleTickets = actor.getDoubleMoves();
this.tickets = new HashMap<TransportType, Integer>(actor.getTickets());
}
public ActorTickets(Graph graph, Properties properties) throws ProtocolException {
loadProperties(graph, properties);
}
}
/**
* The list of captured phantoms.
*/
protected Set<Actor> capturedPhantoms;
/**
* The list of information about the numbers of tickets.
*/
protected ActorTickets[] actorTickets;
/**
* The client that won the game. Set to <code>null</code>, if no client
* won the game (and the game continues).
*/
protected ClientType winner;
/**
* Returns the information about the numbers of tickets of all actors.
* @return the information about the numbers of tickets.
*/
public ActorTickets[] getActorTickets() {
return this.actorTickets;
}
/**
* Returns the list of captured phantoms.
* @return the list of captured phantoms.
*/
public Set<Actor> getCapturedPhantoms() {
return this.capturedPhantoms;
}
@Override
public String getMessageType() {
return PROPERTY_MESSAGE_TYPE_UPDATE;
}
/**
* Returns the winner of the game.
* @return the type of client that won the game. If no client won the game
* in this round, retrurns <code>null</code>.
*/
public ClientType getWinner() {
return this.winner;
}
@Override
protected void loadProperties(Graph graph, Properties[] messageData)
throws ProtocolException {
super.loadProperties(graph, messageData);
ArrayList<ActorTickets> tickets = new ArrayList<ActorTickets>();
for(Properties record : messageData) {
if(record.containsKey(PROPERTY_CAPTURED)) {
String actorId = record.getProperty(PROPERTY_CAPTURED);
if(actorId == null || actorId.isEmpty())
throw new ProtocolException("Actor ID was not specified", null);
Actor capturedActor = graph.getActor(actorId);
if(capturedActor == null)
throw new ProtocolException("Unknown actor ID: " + actorId, null);
capturedPhantoms.add(capturedActor);
}
else if(record.containsKey(PROPERTY_TICKETS)) {
ActorTickets aticket = new ActorTickets(graph, record);
tickets.add(aticket);
}
else if(record.containsKey(PROPERTY_MESSAGE_TYPE) && record.containsKey(PROPERTY_WINNER)) {
String winnerStr = record.getProperty(PROPERTY_WINNER);
try {
this.winner = ClientType.valueOf(winnerStr);
}
catch(IllegalArgumentException e) {
throw new ProtocolException(e.getMessage(), null);
}
}
}
this.actorTickets = tickets.toArray(new ActorTickets[tickets.size()]);
}
@Override
public Properties[] serialize() {
ArrayList<Properties> res = new ArrayList<Properties>(Arrays.asList(super.serialize()));
if(getWinner() != null) {
Properties header = res.get(0);
header.setProperty(PROPERTY_WINNER, getWinner().toString());
}
for(Actor actor : capturedPhantoms) {
Properties cphantom = new Properties();
cphantom.setProperty(PROPERTY_CAPTURED, actor.getId());
res.add(cphantom);
}
for(ActorTickets tickets : actorTickets)
res.add(tickets.serialize());
return res.toArray(new Properties[res.size()]);
}
/**
* Initializes a new instance of the message from serialized data.
*
* @param graph the graph, for which the message is initialized.
* @param messageData the raw data of the message.
* @throws ProtocolException if there is a problem with loading the
* serialized data.
*/
public MessageUpdate(Graph graph, Properties[] messageData) throws ProtocolException {
super(graph, messageData);
}
/**
* Initializes a new instance of the message for the given graph and
* actor moves.
*
* @param graph the graph, for which the message is initialized.
* @param moves the moves of the actors.
* @param winner the client that won the game. <code>null</code> if
* no client won the game.
*/
public MessageUpdate(Graph graph, ActorMove[] moves, ClientType winner) {
super(moves);
if(graph == null)
throw new IllegalArgumentException("graph must not be null");
initialize();
ArrayList<ActorTickets> tickets = new ArrayList<ActorTickets>();
for(Actor actor : graph.getActors()) {
if(actor.isCaptured())
capturedPhantoms.add(actor);
tickets.add(new ActorTickets(actor));
}
this.actorTickets = tickets.toArray(new ActorTickets[tickets.size()]);
this.winner = winner;
}
@Override
protected void initialize() {
this.capturedPhantoms = new HashSet<Actor>();
}
}