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