package polly.rx.core.orion.datasource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import polly.rx.core.orion.OrionException;
import polly.rx.core.orion.PortalEvent;
import polly.rx.core.orion.PortalListener;
import polly.rx.core.orion.PortalUpdater;
import polly.rx.core.orion.model.DefaultPortal;
import polly.rx.core.orion.model.Portal;
import polly.rx.core.orion.model.Sector;
import polly.rx.entities.DBPortal;
import polly.rx.entities.DBSector;
import de.skuzzle.jeve.EventProvider;
import de.skuzzle.polly.sdk.PersistenceManagerV2;
import de.skuzzle.polly.sdk.PersistenceManagerV2.Param;
import de.skuzzle.polly.sdk.PersistenceManagerV2.Read;
import de.skuzzle.polly.sdk.PersistenceManagerV2.Write;
import de.skuzzle.polly.sdk.exceptions.DatabaseException;
import de.skuzzle.polly.sdk.time.Time;
public class DBPortalUpdater implements PortalUpdater {
private final PersistenceManagerV2 persistence;
private final DBQuadrantUpdater quadUpdater;
private final EventProvider events;
public DBPortalUpdater(PersistenceManagerV2 persistence, DBQuadrantUpdater quadUpdater) {
this.persistence = persistence;
this.quadUpdater = quadUpdater;
this.events = EventProvider.newDefaultEventProvider();
}
@Override
public void addPortalListener(PortalListener listener) {
this.events.addListener(PortalListener.class, listener);
}
@Override
public void removePortalListener(PortalListener listener) {
this.events.removeListener(PortalListener.class, listener);
}
private void fire(String reporter, List<Portal> portals, BiConsumer<PortalListener,
PortalEvent> d) {
this.events.dispatch(PortalListener.class,
new PortalEvent(this, reporter, portals), d);
}
@Override
public synchronized Collection<DBPortal> updatePortals(String reporter, Sector sector,
Collection<? extends Portal> portals) throws OrionException {
if (portals.isEmpty()) {
return Collections.emptyList();
}
final List<Portal> moved = new ArrayList<>();
final List<Portal> created = new ArrayList<>();
final List<Portal> removed = new ArrayList<>();
final List<DBPortal> result = new ArrayList<>(portals.size());
try (final Write write = this.persistence.write()) {
final Read read = write.read();
DBSector existingSector = read.findSingle(DBSector.class,
DBSector.QUERY_FIND_SECTOR,
new Param(sector.getQuadName(), sector.getX(), sector.getY()));
if (existingSector == null) {
// if target sector doesn't exist, create it
final Collection<DBSector> updates = this.quadUpdater
.updateSectorInformation(Collections.singleton(sector));
if (updates.isEmpty()) {
// were not able to create sector, so better skip portals too
return result;
}
existingSector = updates.iterator().next();
}
// first step: add new portals
for (final Portal newPortal : portals) {
final DBPortal existing = read.findSingle(DBPortal.class,
DBPortal.QUERY_PORTAL_BY_TYPE_AND_OWNER,
new Param(newPortal.getType(), newPortal.getOwnerName()));
if (existing != null && !existing.equals(newPortal)) {
// portal exists and differs from new portal
// move it to new sector
existing.setSector(existingSector);
moved.add(existing);
} else if (existing == null) {
// new portal needs to be added
final DBPortal portal = new DBPortal(newPortal.getOwnerName(),
newPortal.getOwnerClan(), newPortal.getType(),
existingSector, Time.currentTime());
write.single(portal);
result.add(portal);
created.add(portal);
}
result.add(existing);
}
// second step: remove portals that do not exist anymore
final Collection<DBPortal> currentPortals = read.findList(DBPortal.class,
DBPortal.QUERY_PORTAL_BY_SECTOR, new Param(existingSector));
for (final DBPortal p : currentPortals) {
if (!portals.contains(p)) {
final DefaultPortal copy = new DefaultPortal(p);
p.setSector(null);
write.remove(p);
removed.add(copy);
}
}
} catch (DatabaseException e) {
throw new OrionException(e);
}
this.fire(reporter, created, PortalListener::portalsAdded);
this.fire(reporter, moved, PortalListener::portalsMoved);
this.fire(reporter, removed, PortalListener::portalsRemoved);
return result;
}
}