Removed redundant timing in InnerEvent and made it Comparable
[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 {
16         private PriorityQueue<InnerEvent> events;
17         private long currentTime = 0;
18
19         private final List<Consumer<TimelineEvent>> eventAddedListener;
20
21         public Timeline(int initCapacity)
22         {
23                 events = new PriorityQueue<InnerEvent>(initCapacity);
24
25                 eventAddedListener = new ArrayList<>();
26         }
27
28         public boolean hasNext()
29         {
30                 return !events.isEmpty();
31         }
32
33         public void executeNext()
34         {
35                 InnerEvent first = events.peek();
36                 if (first != null)
37                         executeUpTo(first.getTiming(), -1);
38         }
39
40         public void executeAll()
41         {
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. A value of -1 means no timeout.
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         {
56                 if (events.isEmpty())
57                 {
58                         currentTime = timestamp;
59                         return ExecutionResult.NOTHING_DONE;
60                 }
61                 int checkStop = 0;
62                 InnerEvent first = events.peek();
63                 while (first != null && first.getTiming() <= timestamp)
64                 {
65                         events.remove();
66                         currentTime = first.getTiming();
67                         first.run();
68                         // Don't check after every run
69                         checkStop = (checkStop + 1) % 10;
70                         if (checkStop == 0 && System.currentTimeMillis() >= stopMillis)
71                                 return ExecutionResult.RAN_OUT_OF_TIME;
72                         first = events.peek();
73                 }
74                 currentTime = timestamp;
75                 return ExecutionResult.DONE_IN_TIME;
76         }
77
78         public long getSimulationTime()
79         {
80                 return currentTime;
81         }
82
83         public long nextEventTime()
84         {
85                 if (!hasNext())
86                         return -1;
87                 return events.peek().getTiming();
88         }
89
90         public void reset()
91         {
92                 events.clear();
93                 currentTime = 0;
94         }
95
96         public void addEventAddedListener(Consumer<TimelineEvent> listener)
97         {
98                 eventAddedListener.add(listener);
99         }
100
101         public void removeEventAddedListener(Consumer<TimelineEvent> listener)
102         {
103                 eventAddedListener.remove(listener);
104         }
105
106         /**
107          * Adds an Event to the {@link Timeline}
108          * 
109          * @param function       The {@link TimelineEventHandler} that will be executed, when the {@link InnerEvent} occurs on the timeline.
110          * @param relativeTiming The amount of MI ticks in which the {@link InnerEvent} is called, starting from the current time.
111          */
112         public void addEvent(TimelineEventHandler function, int relativeTiming)
113         {
114                 long timing = currentTime + relativeTiming;
115                 TimelineEvent event = new TimelineEvent(timing);
116                 events.add(new InnerEvent(function, event));
117                 eventAddedListener.forEach(l -> l.accept(event));
118         }
119
120         private class InnerEvent implements Comparable<InnerEvent>
121         {
122                 private final TimelineEventHandler function;
123                 private final TimelineEvent event;
124
125                 /**
126                  * Creates an {@link InnerEvent}
127                  * 
128                  * @param function {@link TimelineEventHandler} to be executed when the {@link InnerEvent} occurs
129                  * @param timing   Point in the MI simulation {@link Timeline}, at which the {@link InnerEvent} is executed;
130                  */
131                 InnerEvent(TimelineEventHandler function, TimelineEvent event)
132                 {
133                         this.function = function;
134                         this.event = event;
135                 }
136
137                 public long getTiming()
138                 {
139                         return event.getTiming();
140                 }
141
142                 public void run()
143                 {
144                         function.handle(event);
145                 }
146
147                 @Override
148                 public String toString()
149                 {
150                         return event.toString();
151                 }
152
153                 @Override
154                 public int compareTo(InnerEvent o)
155                 {
156                         long difference = getTiming() - o.getTiming();
157                         if (difference == 0)
158                                 return 0;
159                         return difference < 0 ? -1 : 1;
160                 }
161         }
162
163         @Override
164         public String toString()
165         {
166                 return "simulation time: " + currentTime + ", " + events.toString();
167         }
168
169         public static long toNanoseconds(long ticks)
170         {
171                 return ticks; // TODO: Alter this when it has been determined how ticks should relate to real time.
172         }
173
174         public enum ExecutionResult
175         {
176                 NOTHING_DONE, DONE_IN_TIME, RAN_OUT_OF_TIME
177         }
178 }