package org.scribble.model.global;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.scribble.model.endpoint.EState;
import org.scribble.model.endpoint.actions.EAccept;
import org.scribble.model.endpoint.actions.EConnect;
import org.scribble.model.endpoint.actions.EDisconnect;
import org.scribble.model.endpoint.actions.EAction;
import org.scribble.model.endpoint.actions.EReceive;
import org.scribble.model.endpoint.actions.ESend;
import org.scribble.model.endpoint.actions.EWrapClient;
import org.scribble.model.endpoint.actions.EWrapServer;
import org.scribble.sesstype.name.Role;
public class SBuffers
{
private final Map<Role, Map<Role, Boolean>> connected = new HashMap<>();
private final Map<Role, Map<Role, ESend>> buffs = new HashMap<>(); // dest -> src -> msg
public SBuffers(Set<Role> roles, boolean implicit)
{
this(roles);
if (implicit)
{
roles.forEach((k) ->
{
HashMap<Role, Boolean> tmp = new HashMap<>();
this.connected.put(k, tmp);
roles.forEach((k2) ->
{
if (!k.equals(k2))
{
tmp.put(k2, true);
}
});
});
}
}
public SBuffers(Set<Role> roles)
{
// FIXME: do the same for connected
roles.forEach((k) ->
{
HashMap<Role, ESend> tmp = new HashMap<>();
this.buffs.put(k, tmp);
roles.forEach((k2) ->
{
if (!k.equals(k2))
{
tmp.put(k2, null);
}
});
});
}
public SBuffers(SBuffers buffs)
{
Set<Role> roles = buffs.buffs.keySet();
roles.forEach((k) ->
{
Map<Role, Boolean> tmp = buffs.connected.get(k);
if (tmp != null)
{
this.connected.put(k, new HashMap<>(tmp));
}
});
roles.forEach((k) ->
{
this.buffs.put(k, new HashMap<>(buffs.buffs.get(k)));
});
}
// FIXME factor out properly with constructor
public Map<Role, Map<Role, ESend>> getBuffers()
{
//return this.buffs;
return new SBuffers(this).buffs;
}
public Map<Role, ESend> get(Role r)
{
return Collections.unmodifiableMap(this.buffs.get(r));
}
/*public boolean isEmpty()
{
return this.buffs.values().stream().flatMap((m) -> m.values().stream()).allMatch((v) -> v == null);
}*/
public boolean isEmpty(Role r)
{
return this.buffs.get(r).values().stream().allMatch((v) -> v == null);
}
public boolean isConnected(Role self, Role peer)
{
Map<Role, Boolean> tmp = this.connected.get(self);
if (tmp == null)
{
return false;
}
Boolean b = tmp.get(peer);
return b != null && b;
}
public boolean canAccept(Role self, EAccept a)
//public boolean canAccept(Role r1, Role r2)
{
return !isConnected(self, a.peer);
//return canConnect(r2, r1);
}
public boolean canConnect(Role self, EConnect c)
//public boolean canConnect(Role r1, Role r2)
{
return !isConnected(self, c.peer);
//return !isConnected(r1, r2);
}
public boolean canDisconnect(Role self, EDisconnect d)
{
return isConnected(self, d.peer);
}
public boolean canWrapClient(Role self, EWrapClient wc)
{
return isConnected(self, wc.peer);
}
public boolean canWrapServer(Role self, EWrapServer ws)
{
return isConnected(self, ws.peer);
}
//public WFBuffers connect(Role src, Connect c, Role dest)
public SBuffers connect(Role src, Role dest)
{
SBuffers copy = new SBuffers(this);
Map<Role, Boolean> tmp1 = copy.connected.get(src);
if (tmp1 == null)
{
tmp1 = new HashMap<>();
copy.connected.put(src, tmp1);
}
tmp1.put(dest, true);
Map<Role, Boolean> tmp2 = copy.connected.get(dest);
if (tmp2 == null)
{
tmp2 = new HashMap<>();
copy.connected.put(dest, tmp2);
}
tmp2.put(src, true);
//copy.buffs.get(c.peer).put(src, new Send(c.peer, c.mid, c.payload));
return copy;
}
public SBuffers disconnect(Role self, EDisconnect d)
{
SBuffers copy = new SBuffers(this);
copy.connected.get(self).put(d.peer, false);
return copy;
}
public boolean canSend(Role self, ESend a)
{
return isConnected(self, a.peer) && (this.buffs.get(a.peer).get(self) == null);
}
public SBuffers send(Role self, ESend a)
{
SBuffers copy = new SBuffers(this);
copy.buffs.get(a.peer).put(self, a);
return copy;
}
/*public boolean canReceive(Role self, Receive a)
{
Send send = this.buffs.get(self).get(a.peer);
return send != null && send.toDual(a.peer).equals(a);
}*/
//public Set<Receive> receivable(Role r)
public Set<EAction> inputable(Role r) // FIXME: IAction // FIXME: OAction version?
{
/*Map<Role, Boolean> tmp = this.connected.get(r);
if (tmp == null)
{
return Collections.emptySet(); // Not needed, guarded by state kind
}*/
Set<EAction> res = this.buffs.get(r).entrySet().stream()
.filter((e) -> e.getValue() != null)
.map((e) -> e.getValue().toDual(e.getKey()))
.collect(Collectors.toSet());
/*Map<Role, Boolean> tmp = this.connected.get(r);
if (tmp == null)
{
this.buffs.get(r).keySet().forEach((x) -> res.add(new Accept(x)));
}
else
{
this.connected.keySet().stream()
.filter((k) -> !tmp.containsKey(k) || !tmp.get(k))
.forEach((k) -> res.add(new Accept(k)));
}*/
return res;
}
//public Set<IOAction> acceptable(Role r) // Means connection accept actions
// Pre: curr is Accept state, r is accept peer
public Set<EAction> acceptable(Role r, EState curr) // Means connection accept actions
{
Set<EAction> res = new HashSet<>();
Map<Role, Boolean> tmp = this.connected.get(r);
/*if (tmp == null)
{
this.buffs.get(r).keySet().forEach((x) -> res.add(new Accept(x)));
}
else
{
this.connected.keySet().stream()
.filter((k) -> !tmp.containsKey(k) || !tmp.get(k))
.forEach((k) -> res.add(new Accept(k)));
}*/
if (tmp != null)
{
Boolean b = tmp.get(r);
if (b != null && b)
{
return res;
}
}
List<EAction> as = curr.getAllActions();
for (EAction a : as)
{
res.add((EAccept) a);
}
return res;
}
public Set<EAction> wrapable(Role r)
{
Set<EAction> res = new HashSet<>();
Map<Role, Boolean> tmp = this.connected.get(r);
if (tmp != null)
{
this.connected.keySet().stream()
.filter((k) -> tmp.containsKey(k) && tmp.get(k))
.forEach((k) -> res.add(new EWrapServer(k)));
}
return res;
}
/*//public Map<Role, Receive> receivable()
public Map<Role, IOAction> inputable()
{
//Map<Role, Receive> tmp = new HashMap<>();
Map<Role, IOAction> tmp = new HashMap<>();
for (Role r : this.buffs.keySet())
{
Map<Role, Send> tmp2 = this.buffs.get(r);
for (Role r2: tmp2.keySet())
{
tmp.put(r, tmp2.get(r2).toDual(r2));
}
}
return tmp;
}*/
public SBuffers receive(Role self, EReceive a)
{
SBuffers copy = new SBuffers(this);
copy.buffs.get(self).put(a.peer, null);
return copy;
}
@Override
public final int hashCode()
{
int hash = 131;
hash = 31 * hash + this.buffs.hashCode();
hash = 31 * hash + this.connected.hashCode();
return hash;
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof SBuffers))
{
return false;
}
SBuffers b = (SBuffers) o;
return this.buffs.equals(b.buffs) && this.connected.equals(b.connected);
}
@Override
public String toString()
{
//return this.buffs.toString();
return this.buffs.entrySet().stream()
.filter((e) -> e.getValue().values().stream().anyMatch((v) -> v != null))
.collect(Collectors.toMap((e) -> e.getKey(),
(e) -> (e.getValue().entrySet().stream()
.filter((f) -> f.getValue() != null)
//.collect(Collectors.toMap((f) -> f.getKey(), (f) -> f.getValue()))) // Inference not working?
.collect(Collectors.toMap((Entry<Role, ESend> f) -> f.getKey(), (f) -> f.getValue())))
)).toString();
}
}