1package com.xtremelabs.robolectric.util;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.List;
6import java.util.ListIterator;
7
8public class Scheduler {
9    private List<PostedRunnable> postedRunnables = new ArrayList<PostedRunnable>();
10    private long currentTime = 0;
11    private boolean paused = false;
12
13    public long getCurrentTime() {
14        return currentTime;
15    }
16
17    public void pause() {
18        paused = true;
19    }
20
21    public void unPause() {
22        paused = false;
23        advanceToLastPostedRunnable();
24    }
25
26    public boolean isPaused() {
27        return paused;
28    }
29
30    public void postDelayed(Runnable runnable, long delayMillis) {
31        if (paused || delayMillis > 0) {
32            postedRunnables.add(new PostedRunnable(runnable, currentTime + delayMillis));
33            Collections.sort(postedRunnables);
34        } else {
35            runnable.run();
36        }
37    }
38
39    public void post(Runnable runnable) {
40        postDelayed(runnable, 0);
41    }
42
43    public void postAtFrontOfQueue(Runnable runnable) {
44        if (paused) {
45            postedRunnables.add(0, new PostedRunnable(runnable, currentTime));
46        } else {
47            runnable.run();
48        }
49    }
50
51    public void remove(Runnable runnable) {
52        ListIterator<PostedRunnable> iterator = postedRunnables.listIterator();
53        while (iterator.hasNext()) {
54            PostedRunnable next = iterator.next();
55            if (next.runnable == runnable) {
56                iterator.remove();
57            }
58        }
59    }
60
61    public boolean advanceToLastPostedRunnable() {
62        if (enqueuedTaskCount() < 1) {
63            return false;
64        }
65
66        return advanceTo(postedRunnables.get(postedRunnables.size() - 1).scheduledTime);
67    }
68
69    public boolean advanceToNextPostedRunnable() {
70        if (enqueuedTaskCount() < 1) {
71            return false;
72        }
73
74        return advanceTo(postedRunnables.get(0).scheduledTime);
75    }
76
77    public boolean advanceBy(long intervalMs) {
78        long endingTime = currentTime + intervalMs;
79        return advanceTo(endingTime);
80    }
81
82    public boolean advanceTo(long endingTime) {
83        if (endingTime - currentTime < 0 || enqueuedTaskCount() < 1) {
84            return false;
85        }
86
87        int runCount = 0;
88        while (nextTaskIsScheduledBefore(endingTime)) {
89            runOneTask();
90            ++runCount;
91        }
92        currentTime = endingTime;
93
94        return runCount > 0;
95    }
96
97    public boolean runOneTask() {
98        if (enqueuedTaskCount() < 1) {
99            return false;
100        }
101
102        PostedRunnable postedRunnable = postedRunnables.remove(0);
103        currentTime = postedRunnable.scheduledTime;
104        postedRunnable.run();
105        return true;
106    }
107
108    public boolean runTasks(int howMany) {
109        if (enqueuedTaskCount() < howMany) {
110            return false;
111        }
112
113        while (howMany > 0) {
114            PostedRunnable postedRunnable = postedRunnables.remove(0);
115            currentTime = postedRunnable.scheduledTime;
116            postedRunnable.run();
117            howMany--;
118        }
119        return true;
120    }
121
122    public int enqueuedTaskCount() {
123        return postedRunnables.size();
124    }
125
126    public boolean areAnyRunnable() {
127        return nextTaskIsScheduledBefore(currentTime);
128    }
129
130    public void reset() {
131        postedRunnables.clear();
132        paused = false;
133    }
134
135    public int size() {
136        return postedRunnables.size();
137    }
138
139    class PostedRunnable implements Comparable<PostedRunnable> {
140        Runnable runnable;
141        long scheduledTime;
142
143        PostedRunnable(Runnable runnable, long scheduledTime) {
144            this.runnable = runnable;
145            this.scheduledTime = scheduledTime;
146        }
147
148        @Override
149        public int compareTo(PostedRunnable postedRunnable) {
150            return (int) (scheduledTime - postedRunnable.scheduledTime);
151        }
152
153        public void run() {
154            runnable.run();
155        }
156    }
157
158    private boolean nextTaskIsScheduledBefore(long endingTime) {
159        return enqueuedTaskCount() > 0 && postedRunnables.get(0).scheduledTime <= endingTime;
160    }
161}
162