1 module sbylib.event.event;
2 
3 import std;
4 import sbylib.wrapper.glfw;
5 import sbylib.event;
6 
7 interface IEvent {
8     void kill();
9     void addFinishCallback(void delegate());
10     bool isAlive() const;
11     EventContext[] context();
12 }
13 
14 class Event(Args...) : IEvent {
15     private void delegate(Args) callback;
16     private void delegate(Exception) onerror;
17     public bool delegate() killCondition;
18     private void delegate()[] finishCallbackList;
19     package EventContext[] _context;
20     private bool alive = true;
21 
22     this() {
23         foreach (c; EventContext.currentContext) {
24             _context ~= c;
25         }
26         foreach (c; context) {
27             c.eventList ~= this;
28         }
29         EventWatcher._eventList ~= this;
30     }
31 
32     void fire(Args args) {
33         try {
34             if (this.isAlive is false) return;
35             if (killCondition && killCondition()) {
36                 this.alive = false;
37                 foreach (cb; finishCallbackList) cb();
38                 return;
39             }
40             if (context.any!(c => c.isBound is false)) return;
41             if (callback) callback(args);
42         } catch (Throwable e) {
43             if (context.empty) {
44                 this.kill();
45             } else {
46                 assert(context.all!(c => c.eventList[].canFind(this))); // for destroy myself
47                 context.each!(c => c.destroy());
48             }
49             assert(this.killCondition !is null && this.killCondition()); // to ensure this event is already dead.
50             throw e;
51         }
52     }
53 
54     void fireOnce(Args args) {
55         this.fire(args);
56         this.kill();
57         this.fire(args); // for launch finish callback
58     }
59 
60     void throwError(Exception e) {
61         this.kill();
62         if (onerror) onerror(e);
63         else throw e;
64     }
65 
66     override bool isAlive() const {
67         return alive;
68     }
69 
70     override void kill() {
71         this.killCondition = () => true;
72     }
73 
74     override void addFinishCallback(void delegate() finishCallback) {
75         if (this.isAlive) {
76             this.finishCallbackList ~= finishCallback;
77         } else {
78             finishCallback();
79         }
80     }
81 
82     override EventContext[] context() {
83         return _context;
84     }
85 
86     static Event create(Args...)(Event!Args delegate(void delegate()) cb) {
87         auto event = new Event;
88         cb(&event.fire).ownedBy(event);
89         return event;
90     }
91 }
92 
93 Event!(Args) then(Args...)(Event!(Args) event, void delegate() callback) 
94     if (Args.length > 0)
95 {
96     assert(event.callback is null);
97     event.callback = (Args _) { callback(); };
98     return event;
99 }
100 
101 Event!(Args) then(Args...)(Event!(Args) event, void delegate(Args) callback) {
102     assert(event.callback is null);
103     event.callback = callback;
104     return event;
105 }
106 
107 Event!(Args) error(Args...)(Event!(Args) event, void delegate(Exception) callback) {
108     assert(event.onerror is null);
109     event.onerror = callback;
110     return event;
111 }
112 
113 Event!(Args) until(Args...)(Event!(Args) event, bool delegate() condition) {
114     assert(event.killCondition is null);
115     event.killCondition = condition;
116     return event;
117 }
118 
119 Event!(Args) once(Args...)(Event!(Args) event) {
120     bool hasRun;
121     event.killCondition = {
122         if (hasRun) return true;
123         hasRun = true;
124         return false;
125     };
126     return event;
127 }
128 
129 Event!(Args) once(Args...)(Event!(Args) event, void delegate() callback) 
130     if (Args.length > 0)
131 {
132     return event.then(callback).once;
133 }
134 
135 Event!(Args) once(Args...)(Event!(Args) event, void delegate(Args) callback) {
136     return event.then(callback).once;
137 }
138 
139 Event!(Args) ownedBy(Args...)(Event!(Args) event, IEvent owner) {
140     return event.until(() => !owner.isAlive);
141 }
142 
143 alias VoidEvent = Event!();
144 
145 class EventWatcher {
146 static:
147 
148     Array!IEvent _eventList;
149 
150     void update() {
151         _eventList.linearRemove(_eventList[].find!(e => !e.isAlive));
152     }
153 
154     const(Array!IEvent) eventList() {
155         return _eventList;
156     }
157 
158 }
159 
160 class EventException : Exception {
161     mixin basicExceptionCtors;
162 }