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