Reformatted everything. Eclipse built-in Linewrapping/Comments 140 chars
[Mograsim.git] / era.mi / src / era / mi / logic / timeline / Timeline.java
1 package era.mi.logic.timeline;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.PriorityQueue;
6 import java.util.function.Consumer;
7
8 /**
9  * Orders Events by the time they are due to be executed. Can execute Events individually.
10  * 
11  * @author Fabian Stemmler
12  *
13  */
14 public class Timeline {
15         private PriorityQueue<InnerEvent> events;
16         private long currentTime = 0;
17
18         private final List<Consumer<TimelineEvent>> eventAddedListener;
19
20         public Timeline(int initCapacity) {
21                 events = new PriorityQueue<InnerEvent>(initCapacity, (a, b) -> {
22                         long difference = a.getTiming() - b.getTiming();
23                         if (difference == 0)
24                                 return 0;
25                         return difference < 0 ? -1 : 1;
26                 });
27
28                 eventAddedListener = new ArrayList<>();
29         }
30
31         public boolean hasNext() {
32                 return !events.isEmpty();
33         }
34
35         public void executeNext() {
36                 InnerEvent first = events.poll();
37                 currentTime = first.getTiming();
38                 first.run();
39         }
40
41         public void executeAll() {
42                 while (hasNext())
43                         executeNext();
44         }
45
46         /**
47          * Executes all events up to a given simulation timestamp. The simulation process can be constrained by a real world timestamp.
48          * 
49          * @param timestamp  the simulation timestamp up to which the events will be processed
50          * @param stopMillis the System.currentTimeMillis() when simulation definitely needs to stop.
51          * @return if it was possible to fulfil the goal in the given real world time.
52          * @author Christian Femers
53          */
54         public ExecutionResult executeUpTo(long timestamp, long stopMillis) {
55                 if (events.isEmpty()) {
56                         currentTime = timestamp;
57                         return ExecutionResult.NOTHING_DONE;
58                 }
59                 int checkStop = 0;
60                 InnerEvent first = events.peek();
61                 while (first != null && first.getTiming() <= timestamp) {
62                         events.remove();
63                         currentTime = first.getTiming();
64                         first.run();
65                         // Don't check after every run
66                         checkStop = (checkStop + 1) % 10;
67                         if (checkStop == 0 && System.currentTimeMillis() >= stopMillis)
68                                 return ExecutionResult.RAN_OUT_OF_TIME;
69                         first = events.peek();
70                 }
71                 currentTime = timestamp;
72                 return ExecutionResult.DONE_IN_TIME;
73         }
74
75         public long getSimulationTime() {
76                 return currentTime;
77         }
78
79         public long nextEventTime() {
80                 if (!hasNext())
81                         return -1;
82                 else
83                         return events.peek().timing;
84         }
85
86         public void reset() {
87                 events.clear();
88                 currentTime = 0;
89         }
90
91         public void addEventAddedListener(Consumer<TimelineEvent> listener) {
92                 eventAddedListener.add(listener);
93         }
94
95         public void removeEventAddedListener(Consumer<TimelineEvent> listener) {
96                 eventAddedListener.remove(listener);
97         }
98
99         /**
100          * Adds an Event to the {@link Timeline}
101          * 
102          * @param function       The {@link TimelineEventHandler} that will be executed, when the {@link InnerEvent} occurs on the timeline.
103          * @param relativeTiming The amount of MI ticks in which the {@link InnerEvent} is called, starting from the current time.
104          */
105         public void addEvent(TimelineEventHandler function, int relativeTiming) {
106                 long timing = currentTime + relativeTiming;
107                 TimelineEvent event = new TimelineEvent(timing);
108                 events.add(new InnerEvent(function, event, timing));
109                 eventAddedListener.forEach(l -> l.accept(event));
110         }
111
112         private class InnerEvent {
113
114                 private final long timing;
115                 private final TimelineEventHandler function;
116                 private final TimelineEvent event;
117
118                 /**
119                  * Creates an {@link InnerEvent}
120                  * 
121                  * @param function {@link TimelineEventHandler} to be executed when the {@link InnerEvent} occurs
122                  * @param timing   Point in the MI simulation {@link Timeline}, at which the {@link InnerEvent} is executed;
123                  */
124                 InnerEvent(TimelineEventHandler function, TimelineEvent event, long timing) {
125                         this.function = function;
126                         this.event = event;
127                         this.timing = timing;
128                 }
129
130                 public long getTiming() {
131                         return timing;
132                 }
133
134                 public void run() {
135                         function.handle(event);
136                 }
137
138                 @Override
139                 public String toString() {
140                         return event.toString();
141                 }
142         }
143
144         @Override
145         public String toString() {
146                 return "simulation time: " + currentTime + ", " + events.toString();
147         }
148
149         public static long toNanoseconds(long ticks) {
150                 return ticks; // TODO: Alter this when it has been determined how ticks should relate to real time.
151         }
152
153         public enum ExecutionResult {
154                 NOTHING_DONE, DONE_IN_TIME, RAN_OUT_OF_TIME
155         }
156 }