package unsw.blackout; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; // prefer vectors for cache locality import java.util.Optional; import unsw.response.models.EntityInfoResponse; import unsw.response.models.FileInfoResponse; import unsw.utils.Angle; public class BlackoutController { private Vector Entities = new Vector(); // We use optional as a more explicit way of saying we return null, or to represent infinity etc. // Note, the HD part is not entirely implemented. /** * Returns an optional Entitybase, where optional means it does not exist. */ private Optional getEntity(String Id) { for (EntityBase entity : this.Entities) { if (entity.getId().equals(Id)) { return Optional.of(entity); } } return Optional.empty(); } /** * Removes an entity based on a String id, does nothing if it already exists. */ private void removeEntity(String id) { Optional entity = getEntity(id); if (entity.isEmpty()) { return; } this.Entities.remove(entity.get()); } /** * Adds an entity based on a String id, does nothing if it already exists. */ private void addEntity(EntityBase entity) { if (getEntity(entity.getId()).isPresent()) { return; } this.Entities.add(entity); } // Public methods. public void createDevice(String deviceId, String type, Angle position) { if(type.equals(HandheldDevice.class.getSimpleName())) { this.addEntity(new HandheldDevice(deviceId, position)); } else if (type.equals(LaptopDevice.class.getSimpleName())) { this.addEntity(new LaptopDevice(deviceId, position)); } else if (type.equals(DesktopDevice.class.getSimpleName())) { this.addEntity(new DesktopDevice(deviceId, position)); } else if (type.equals(CloudStorageDevice.class.getSimpleName())) { this.addEntity(new CloudStorageDevice(deviceId, position)); } } public void removeDevice(String deviceId) { Optional entity = this.getEntity(deviceId); if (entity.isEmpty() || !(entity.get() instanceof DeviceBase)) { return; } this.removeEntity(deviceId); } public void createSatellite(String satelliteId, String type, double height, Angle position) { if (type.equals(StandardSatellite.class.getSimpleName())) { this.addEntity(new StandardSatellite(satelliteId, height, position)); } else if (type.equals(ShrinkingSatellite.class.getSimpleName())) { this.addEntity(new ShrinkingSatellite(satelliteId, height, position)); } else if (type.equals(RelaySatellite.class.getSimpleName())) { this.addEntity(new RelaySatellite(satelliteId, height, position)); } else if (type.equals(ElephantSatellite.class.getSimpleName())) { this.addEntity(new ElephantSatellite(satelliteId, height, position)); } } public void removeSatellite(String satelliteId) { Optional entity = this.getEntity(satelliteId); if (entity.isEmpty() || !(entity.get() instanceof SatelliteBase)) { return; } this.removeEntity(satelliteId); } public List listDeviceIds() { ArrayList list = new ArrayList(); for (EntityBase entity : this.Entities) { if (!(entity instanceof DeviceBase)) { continue; } list.add(entity.getId()); } return list; } public List listSatelliteIds() { ArrayList list = new ArrayList(); for (EntityBase entity : this.Entities) { if (!(entity instanceof SatelliteBase)) { continue; } list.add(entity.getId()); } return list; } public void addFileToDevice(String deviceId, String filename, String content) { Optional entity = this.getEntity(deviceId); if (entity.isEmpty() || !(entity.get() instanceof DeviceBase)) { return; } DeviceBase device = (DeviceBase)entity.get(); device.addFile(filename, content); } public EntityInfoResponse getInfo(String id) { Optional optional_entity = this.getEntity(id); if (optional_entity.isEmpty()) { return null; } EntityBase entity = optional_entity.get(); Angle position = entity.getAngle(); double height = entity.getHeight(); String type = entity.getClass().getSimpleName(); Map files = new HashMap<>(); for (File file : entity.getReceivedFiles()) { String filename = file.getFilename(); String transmitted_contents = file.getTransmittedContents(); int final_size = file.getContentsSize(); boolean is_transmitted = file.hasFullyTransmitted(); if (is_transmitted && entity.canShrink()) { if (file.isQuantum()) { final_size = (int)((double)final_size * (2.0 / 3.0) + 0.5); } } files.put(filename, new FileInfoResponse(filename, transmitted_contents, final_size, is_transmitted)); } return new EntityInfoResponse(id, position, height, type, files); } /** * Helper function, sends all files associated with the entity to their targets if they exist. */ private void sendEntityFiles(EntityBase entity) { Vector remove_files = new Vector(); for (File file : entity.getSentFiles()) { if (file.hasFullyTransmitted()) { continue; } Optional target = this.getEntity(file.getTargetId()); if (target.isEmpty()) { // as per the reference implementation, if it's deleted just ignore it (undef in spec) continue; } if (entity.maybeTransferFile(file, target.get())) { continue; } remove_files.add(file); } entity.removeSentFiles(remove_files); } public void simulate() { for (EntityBase entity : this.Entities) { entity.move(); } // For sending files, we need the origin entity, available in BlackoutController. for (EntityBase entity : this.Entities) { this.sendEntityFiles(entity); } } /** * Simulate for the specified number of minutes. * You shouldn't need to modify this function. */ public void simulate(int numberOfMinutes) { for (int i = 0; i < numberOfMinutes; i++) { simulate(); } } public List communicableEntitiesInRange(String id) { Optional optional_entity = this.getEntity(id); if (optional_entity.isEmpty()) { return null; } EntityBase entity = optional_entity.get(); List communicable_entities = new ArrayList(); for (EntityBase e : this.Entities) { if (!entity.canCommunicate(e, this.Entities)) { continue; } communicable_entities.add(e.getId()); } return communicable_entities; } public void sendFile(String fileName, String fromId, String toId) throws FileTransferException { // Assumes we can communicate! (part of the spec) Optional optional_entity_from = this.getEntity(fromId); Optional optional_entity_to = this.getEntity(toId); if (optional_entity_from.isEmpty() || optional_entity_to.isEmpty()) { return; } EntityBase entity_from = optional_entity_from.get(); EntityBase entity_to = optional_entity_to.get(); entity_from.sendFileTo(fileName, entity_to); } }