package polly.rx.core.orion.datasource; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import polly.rx.core.orion.FleetEvent; import polly.rx.core.orion.FleetHeatMap; import polly.rx.core.orion.FleetListener; import polly.rx.core.orion.FleetTracker; import polly.rx.core.orion.OrionException; import polly.rx.core.orion.model.Fleet; import polly.rx.core.orion.model.Quadrant; import de.skuzzle.jeve.EventProvider; import de.skuzzle.polly.sdk.time.Milliseconds; import de.skuzzle.polly.sdk.time.Time; import de.skuzzle.polly.tools.Check; import de.skuzzle.polly.tools.collections.TemporaryValueMap; public class MemoryFleetTracker implements FleetTracker { private final static long MAX_AGE = Milliseconds.fromHours(3); private final static long ORION_MAX_AGE = Milliseconds.fromDays(1); private final TemporaryValueMap<Integer, Fleet> orionFleets; private final TemporaryValueMap<String, LinkedList<Fleet>> fleets; private final EventProvider events; private final FleetHeatMap heatMap; public MemoryFleetTracker(FleetHeatMap heatMap) { this.orionFleets = new TemporaryValueMap<>(ORION_MAX_AGE); this.fleets = new TemporaryValueMap<>(MAX_AGE); this.events = EventProvider.newDefaultEventProvider(); this.heatMap = heatMap; } @Override public void addFleetListener(FleetListener listener) { this.events.addListener(FleetListener.class, listener); } @Override public void removeFleetListener(FleetListener listener) { this.events.addListener(FleetListener.class, listener); } private Date getThresholdDate() { return new Date(Time.currentTimeMillis() - MAX_AGE); } @Override public synchronized void updateOrionFleets(String reporter, Collection<? extends Fleet> ownFleets) throws OrionException { for (final Fleet f : ownFleets) { Check.number(f.getRevorixId()).isPositiveOrZero(); final Fleet prev = this.orionFleets.put(f.getRevorixId(), f); if (prev == null || !prev.getSector().equals(f.getSector())) { // This fleet has been moved! this.heatMap.update(f); } } this.events.dispatch(FleetListener.class, new FleetEvent(this, reporter, new ArrayList<>(ownFleets)), FleetListener::ownFleetsUpdated); } private void clearOldestFleets(Collection<Fleet> fleets, Fleet current, Date threshold) { final Iterator<Fleet> it = fleets.iterator(); while (it.hasNext()) { final Fleet f = it.next(); if (f.getDate().compareTo(threshold) < 0 || f.equals(current)) { it.remove(); } } } @Override public synchronized Collection<? extends Fleet> getOrionUserFleets() { return this.orionFleets.values(); } @Override public Collection<? extends Fleet> getFleets() { final List<Fleet> result = new ArrayList<>(); synchronized (this.orionFleets) { result.addAll(this.orionFleets.values()); } synchronized (this.fleets) { for (final LinkedList<Fleet> fleets : this.fleets.values()) { result.addAll(fleets); } } return result; } @Override public void updateFleets(String reporter, Collection<? extends Fleet> fleets) throws OrionException { final Date threshold = getThresholdDate(); synchronized (this.fleets) { for (final Fleet fleet : fleets) { LinkedList<Fleet> existing = this.fleets.get(fleet.getOwnerName()); if (existing == null) { existing = new LinkedList<>(); } clearOldestFleets(existing, fleet, threshold); existing.addFirst(fleet); // always put, to prevent automatic deletion this.fleets.put(fleet.getName(), existing); } } this.events.dispatch(FleetListener.class, new FleetEvent(this, reporter, new ArrayList<>(fleets)), FleetListener::fleetsUpdated); } @Override public synchronized Collection<? extends Fleet> getFleets(Quadrant quadrant) { final Collection<Fleet> result = new ArrayList<>(); for (final Fleet f : this.getFleets()) { if (f.getSector().getQuadName().equals(quadrant.getName())) { result.add(f); } } return result; } }