aboutsummaryrefslogtreecommitdiff
path: root/comp2511/blackout/BlackoutController.java
blob: fcbedc43aafdf3272781ec7414c55b9dd9c79d04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
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<EntityBase> Entities = new Vector<EntityBase>();

    // 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<EntityBase> 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<EntityBase> 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<EntityBase> 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<EntityBase> entity = this.getEntity(satelliteId);
        if (entity.isEmpty() || !(entity.get() instanceof SatelliteBase)) {
            return;
        }
        this.removeEntity(satelliteId);
    }

    public List<String> listDeviceIds() {
        ArrayList<String> list = new ArrayList<String>();
        for (EntityBase entity : this.Entities) {
            if (!(entity instanceof DeviceBase)) {
                continue;
            }
            list.add(entity.getId());
        }
        return list;
    }

    public List<String> listSatelliteIds() {
        ArrayList<String> list = new ArrayList<String>();
        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<EntityBase> 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<EntityBase> 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<String, FileInfoResponse> 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<File> remove_files = new Vector<File>();

        for (File file : entity.getSentFiles()) {
            if (file.hasFullyTransmitted()) {
                continue;
            }

            Optional<EntityBase> 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<String> communicableEntitiesInRange(String id) {
        Optional<EntityBase> optional_entity = this.getEntity(id);

        if (optional_entity.isEmpty()) {
            return null;
        }

        EntityBase entity = optional_entity.get();

        List<String> communicable_entities = new ArrayList<String>();
        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<EntityBase> optional_entity_from = this.getEntity(fromId);
        Optional<EntityBase> 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);
    }
}