package org.scribble.util;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.scribble.sesstype.name.Role;
// FIXME: factor out with MessageIdMap
// FIXME: cf, WFBuffers.connected
// Mutable
public class ConnectedMap
{
public static enum ConnectedStatus { TRUE, FALSE, AMBIG, }
// dest -> (src -> isConnected)
private Map<Role, Map<Role, ConnectedStatus>> map = new HashMap<>();
private static ConnectedStatus convertStatus(boolean b)
{
return b ? ConnectedStatus.TRUE : ConnectedStatus.FALSE;
}
public ConnectedMap(Set<Role> roles, boolean implicit)
{
roles.forEach((k) ->
{
HashMap<Role, ConnectedStatus> tmp = new HashMap<>();
this.map.put(k, tmp);
roles.forEach((k2) ->
{
if (!k.equals(k2))
{
tmp.put(k2, convertStatus(implicit));
}
});
});
}
public ConnectedMap(ConnectedMap map)
{
for (Role dest : map.getDestinations())
{
for (Role src : map.map.get(dest).keySet())
{
setConnected(dest, src, map.isConnected(dest, src));
}
}
}
// FIXME: refactor
public ConnectedMap merge(ConnectedMap them)
{
Map<Role, Map<Role, ConnectedStatus>> ours = this.map;
Map<Role, Map<Role, ConnectedStatus>> theirs = them.map;
ConnectedMap res = new ConnectedMap(Collections.emptySet(), false);
Set<Role> rs = new HashSet<>(ours.keySet()); // keySet should be sufficient
rs.addAll(theirs.keySet());
for (Role r1 : rs)
{
for (Role r2 : rs)
{
if (this.containsRolePair(r1, r2))
{
ConnectedStatus c1 = ours.get(r1).get(r2);
if (them.containsRolePair(r1, r2))
{
ConnectedStatus c2 = theirs.get(r1).get(r2);
ConnectedStatus c = (c1 == c2) ? c1 : ConnectedStatus.AMBIG;
res.setConnected(r1, r2, c);
}
else
{
res.setConnected(r1, r2, c1);
}
}
else if (them.containsRolePair(r1, r2))
{
ConnectedStatus c2 = theirs.get(r1).get(r2);
res.setConnected(r1, r2, c2);
}
}
}
/*/
ConnectedMap res = new ConnectedMap(this);
for (Role r1 : them.getDestinations())
{
for (Role r2 : them.getSources(r1))
{
ConnectedStatus c2 = them.map.get(r1).get(r2);
if (this.containsRolePair(r1, r2))
{
ConnectedStatus c1 = res.map.get(r1).get(r2);
ConnectedStatus c = (c1 == c2) ? c1 : ConnectedStatus.AMBIG;
res.setConnected(r1, r2, c);
}
else
{
res.setConnected(r1, r2, c2);
}
}
}
//*/
return res;
}
public void connect(Role dest, Role src)
{
setConnected(dest, src, true);
}
public void disconnect(Role dest, Role src)
{
setConnected(dest, src, false);
}
protected void setConnected(Role dest, Role src, ConnectedStatus status)
{
addRolePair(dest, src);
//this.map.get(dest).put(src, isConnected);
ConnectedStatus b = status;
this.map.get(dest).put(src, b);
addRolePair(src, dest);
this.map.get(src).put(dest, b);
}
public void setConnected(Role dest, Role src, boolean isConnected)
{
setConnected(dest, src, convertStatus(isConnected));
}
public Set<Role> getDestinations()
{
return this.map.keySet();
}
public boolean containsDestination(Role dest)
{
return this.map.containsKey(dest);
}
public Set<Role> getSources(Role dest)
{
return this.map.containsKey(dest) ? this.map.get(dest).keySet() : Collections.emptySet();
}
// FIXME: null guards not needed any more
public boolean isConnected(Role dest, Role src)
{
//Map<Role, Boolean> tmp = this.map.get(dest);
Map<Role, ConnectedStatus> tmp = this.map.get(dest);
if (tmp != null)
{
/*Boolean b = tmp.get(src);
return b != null && b;*/
ConnectedStatus b = tmp.get(src);
return b == ConnectedStatus.TRUE;
}
return false;
}
public boolean containsRolePair(Role dest, Role src)
{
return this.map.keySet().contains(dest) && this.map.get(dest).containsKey(src);
}
public void clear()
{
this.map.clear();
}
private void addRolePair(Role dest, Role src)
{
if (!this.map.containsKey(dest))
{
//Map<Role, Boolean> map = new HashMap<>();
Map<Role, ConnectedStatus> map = new HashMap<>();
this.map.put(dest, map);
//map.put(src, false);
map.put(src, ConnectedStatus.FALSE);
}
else if (!this.map.get(dest).containsKey(src))
{
//this.map.get(dest).put(src, false);
//this.map.get(dest).put(src, false);
this.map.get(dest).put(src, ConnectedStatus.FALSE);
}
}
@Override
public String toString()
{
return this.map.toString();
}
@Override
public int hashCode()
{
int hash = 1033;
hash = 31 * hash + super.hashCode();
return hash;
}
@Override
public boolean equals(Object o)
{
if (o == null)
{
return false;
}
if (!(o instanceof ConnectedMap))
{
return false;
}
return this.map.equals(((ConnectedMap) o).map);
}
}