package geniusweb.protocol.session.mopac2;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import geniusweb.actions.Action;
import geniusweb.actions.EndNegotiation;
import geniusweb.actions.PartyId;
import geniusweb.inform.Agreements;
import geniusweb.protocol.ProtocolException;
/**
* Invariant: contains the current state of all participating parties. A party
* either notYetActed, did action, reached an agreement, walked away or made a
* {@link ProtocolException}.
*
* After a phase completes, the actions are filtered/handled to collect
* agreements and move remaining parties back to notYetActed.
*
*/
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
public class PartyStates {
// all parties must be in exactly one state at all times.
private final Set notYetActed;
private final List actions; // chrono order
private final Agreements agreements;
private final List walkedAway;
private final Map exceptions;
private final Map powers;
@JsonCreator
protected PartyStates(@JsonProperty("notYetActed") Set notYetActed,
@JsonProperty("actions") List actions,
@JsonProperty("agreements") Agreements agreements,
@JsonProperty("walkedAway") List walkedAway,
@JsonProperty("exceptions") Map exceptions,
@JsonProperty("powers") Map powers) {
this.notYetActed = notYetActed;
this.actions = actions;
this.agreements = agreements;
this.walkedAway = walkedAway;
this.exceptions = exceptions;
this.powers = powers;
}
/**
* Initial constructor.
*
* @param powers the powers of all parties.
*/
public PartyStates(Map powers) {
this.notYetActed = new HashSet<>(powers.keySet());
this.actions = Collections.emptyList();
this.agreements = new Agreements();
this.walkedAway = Collections.emptyList();
this.exceptions = Collections.emptyMap();
this.powers = new HashMap<>(powers);
}
/**
*
* @param action the action done by some party. The correctness of action,
* particularly of {@link Action#getActor()}, must be correct.
* @return new state with party done given action. This just accepts any
* given action and is not considering number of rounds, time or
* whether an action is allowed.
*
* @throws IllegalArgumentException if party doing action already acted.
* This is just a safety check as legality
* of actions should be checked before
* calling this.
*/
public PartyStates with(Action action) {
if (!(notYetActed.contains(action.getActor())))
throw new IllegalArgumentException(
"actor already acted: " + action);
if (action instanceof EndNegotiation)
return withWalkAway(action.getActor());
List newActions = new LinkedList(actions);
newActions.add(action);
return new PartyStates(removeParty(action.getActor()), newActions,
agreements, walkedAway, exceptions, powers);
}
/**
*
* @param newAgree a new agreements to be merged with existing agreements.
* The parties in the agreement must have acted and thus in
* actions.
* @return new PartyStates with agreeing parties removed from actions and
* added to Agreements.
*/
public PartyStates with(Agreements newAgree) {
Set parties = newAgree.getMap().keySet();
List newActions = actions.stream()
.filter(act -> !(parties.contains(act.getActor())))
.collect(Collectors.toList());
return new PartyStates(notYetActed, newActions,
agreements.with(newAgree), walkedAway, exceptions, powers);
}
public PartyStates withWalkAway(PartyId actor) {
if (!(notYetActed.contains(actor)))
throw new IllegalArgumentException("actor already acted: " + actor);
LinkedList newWalkAway = new LinkedList<>(walkedAway);
newWalkAway.add(actor);
return new PartyStates(removeParty(actor), actions, agreements,
newWalkAway, exceptions, powers);
}
/**
* Move party from active to exceptions.
*
* @param e the exception that the party caused
* @return new PartyStates with party that caused the exception in the
* exceptions list and removed from the active list. Nothing happens
* if party is not active.
*/
public PartyStates with(ProtocolException e) {
if (!notYetActed.contains(e.getParty())) {
// complex case. Party did valid action but now is messing around.
// Easiest seems to completely ignore it. CHECK.
return this;
}
Map newExc = new HashMap<>(exceptions);
newExc.put(e.getParty(), e);
return new PartyStates(removeParty(e.getParty()), actions, agreements,
walkedAway, newExc, powers);
}
/**
*
* @return parties that have not yet acted
*/
public Set getNotYetActed() {
return notYetActed;
}
/**
*
* @return parties that are still in the negotiation. These are the parties
* that not yet acted plus the ones that did an action.
*/
public Set getNegotiatingParties() {
Set parties = actions.stream().map(act -> act.getActor())
.collect(Collectors.toSet());
parties.addAll(notYetActed);
return parties;
}
/**
* @param actor
* @return {@link #notYetActed} with actor removed
* @throws IllegalArgumentException if party already acted (not in
* {@link #notYetActed}).
*/
private Set removeParty(PartyId party) {
Set newActiveParties = new HashSet<>(notYetActed);
if (!newActiveParties.remove(party))
throw new IllegalArgumentException(
"Party " + party + " is not active, can't be removed");
return newActiveParties;
}
public Agreements getAgreements() {
return agreements;
}
public Map getPowers() {
return Collections.unmodifiableMap(powers);
}
public Map getExceptions() {
return Collections.unmodifiableMap(exceptions);
}
/**
* @return new state where all {@link #notYetActed} are moved to the
* exceptions list.
*/
public PartyStates finish() {
PartyStates newstate = this;
for (PartyId party : notYetActed) {
newstate = newstate
.with(new ProtocolException("Party did not act", party));
}
return newstate;
}
public List getActions() {
return Collections.unmodifiableList(actions);
}
/**
*
* @return all parties that walked away
*/
public List getWalkedAway() {
return walkedAway;
}
/**
* @param the type of objects requested
* @param type the type of actions to extract. Must be of type T, needed
* because of java's type erasure.
*
* @return all actions of type done in this phase
*/
public List getActions(Class type) {
return actions.stream().filter(act -> type.isInstance(act))
.map(act -> (T) act).collect(Collectors.toList());
}
@Override
public String toString() {
return "PartyStates[" + notYetActed + "," + actions + "," + agreements
+ "," + walkedAway + "," + exceptions + "]";
}
/**
*
* @return states with all parties that are in {@link #actions} moved back
* to {@link #notYetActed}. This state is then ready for a next
* phase.
* @throws IllegalStateException if {@link #notYetActed} is not empty
*/
public PartyStates flush() {
if (!notYetActed.isEmpty())
throw new IllegalStateException(
"Some parties did not yet act:" + notYetActed);
return new PartyStates(getNegotiatingParties(), Collections.emptyList(),
agreements, walkedAway, exceptions, powers);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((actions == null) ? 0 : actions.hashCode());
result = prime * result
+ ((agreements == null) ? 0 : agreements.hashCode());
result = prime * result
+ ((exceptions == null) ? 0 : exceptions.hashCode());
result = prime * result
+ ((notYetActed == null) ? 0 : notYetActed.hashCode());
result = prime * result + ((powers == null) ? 0 : powers.hashCode());
result = prime * result
+ ((walkedAway == null) ? 0 : walkedAway.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PartyStates other = (PartyStates) obj;
if (actions == null) {
if (other.actions != null)
return false;
} else if (!actions.equals(other.actions))
return false;
if (agreements == null) {
if (other.agreements != null)
return false;
} else if (!agreements.equals(other.agreements))
return false;
if (exceptions == null) {
if (other.exceptions != null)
return false;
} else if (!exceptions.equals(other.exceptions))
return false;
if (notYetActed == null) {
if (other.notYetActed != null)
return false;
} else if (!notYetActed.equals(other.notYetActed))
return false;
if (powers == null) {
if (other.powers != null)
return false;
} else if (!powers.equals(other.powers))
return false;
if (walkedAway == null) {
if (other.walkedAway != null)
return false;
} else if (!walkedAway.equals(other.walkedAway))
return false;
return true;
}
}