1 module hio.events;
2 
3 import std.datetime;
4 import std.exception;
5 import std.container;
6 import std.experimental.logger;
7 import std.typecons;
8 import core.sync.mutex;
9 import std.format;
10 
11 import hio.common;
12 
13 //import nbuff;
14 
15 enum AppEvent : int {
16     NONE = 0x00,
17     IN   = 0x01,
18     OUT  = 0x02,
19     ERR  = 0x04,
20     CONN = 0x08,
21     HUP  = 0x10,
22     TMO  = 0x20,
23     USER = 0x40,
24     IMMED= 0x80,
25     ALL  = 0x7f
26 }
27 private immutable string[int] _names;
28 
29 shared static this() {
30     _names = [
31         0:"NONE",
32         1:"IN",
33         2:"OUT",
34         4:"ERR",
35         8:"CONN",
36        16:"HUP",
37        32:"TMO",
38        64:"USER"
39     ];
40 }
41 
42 alias HandlerDelegate = void delegate(AppEvent) @safe;
43 alias SigHandlerDelegate = void delegate(int) @safe;
44 alias FileHandlerFunction = void function(int, AppEvent) @safe;
45 //alias NotificationHandler = void delegate(Notification) @safe;
46 alias FileHandlerDelegate = void delegate(int, AppEvent) @safe;
47 
48 string appeventToString(AppEvent ev) @safe pure {
49     import std.format;
50     import std.range;
51 
52     string[] a;
53     with(AppEvent) {
54         foreach(e; [IN,OUT,ERR,CONN,HUP,TMO]) {
55             if ( ev & e ) {
56                 a ~= _names[e];
57             }
58         }
59     }
60     return a.join("|");
61 }
62 
63 class NotFoundException : Exception {
64     this(string msg, string file = __FILE__, size_t line = __LINE__) @safe {
65         super(msg, file, line);
66     }
67 }
68 
69 class NotImplementedException : Exception {
70     this(string msg, string file = __FILE__, size_t line = __LINE__) @safe {
71         super(msg, file, line);
72     }
73 }
74 
75 //final class FileDescriptor {
76 //    package {
77 //        immutable int   _fileno;
78 //        HandlerDelegate _handler;
79 //        AppEvent        _polling;
80 //    }
81 //    this(int fileno) nothrow @safe {
82 //        _fileno = fileno;
83 //    }
84 //    override string toString() const @safe {
85 //        import std.format: format;
86 //        return appeventToString(_polling);
87 //        //return "FileDescriptor: filehandle: %d, events: %s".format(_fileno, appeventToString(_polling));
88 //    }
89 //}
90 
91 //class CanPoll {
92 //    union Id {
93 //        int     fd = -1;
94 //    }
95 //    Id  id;
96 //}
97 
98 abstract class EventHandler {
99     abstract void eventHandler(AppEvent) @safe;
100 }
101 
102 abstract class FileEventHandler {
103     abstract void eventHandler(int, AppEvent) @safe;
104 }
105 
106 final class Timer {
107     private static ulong timer_id = 1;
108     package {
109         immutable ulong           _id;
110         immutable SysTime         _expires;
111         immutable HandlerDelegate _handler;
112         immutable string          _file;
113         immutable int             _line;
114     }
115     int opCmp(in Timer other) const nothrow pure @safe {
116         int timeCmp = _expires.opCmp(other._expires);
117         if ( timeCmp != 0 ) {
118             return timeCmp;
119         }
120         return _id < other._id ? -1 : 1;
121     }
122 
123     bool eq(const Timer b) const pure nothrow @safe {
124         return this._id == b._id && this._expires == b._expires && this._handler == b._handler;
125     }
126     
127     this(Duration d, HandlerDelegate h, string f = __FILE__, int l =  __LINE__) @safe {
128         if ( d == Duration.max ) {
129             _expires = SysTime.max;
130         } else {
131             _expires = Clock.currTime + d;
132         }
133         _handler = h;
134         _id = timer_id;
135         _file = f;
136         _line = l;
137         timer_id++;
138     }
139     this(SysTime e, HandlerDelegate h, string f = __FILE__, int l =  __LINE__) @safe {
140         enforce(e != SysTime.init, "Unintialized expires for new timer");
141         enforce(h != HandlerDelegate.init, "Unitialized handler for new Timer");
142         _expires = e;
143         _handler = h;
144         _file = f;
145         _line = l;
146         _id = timer_id++;
147     }
148     override string toString() const @trusted {
149         import std.format: format;
150         return "timer: expires: %s, id: %d, addr %X (%s:%d)".format(_expires, _id, cast(void*)this, _file, _line);
151     }
152 }
153 
154 final class Signal {
155     private static ulong signal_id = 1;
156     package {
157         immutable int   _signum;
158         immutable ulong _id;
159         immutable SigHandlerDelegate _handler;
160     }
161 
162     this(int signum, SigHandlerDelegate h) {
163         _signum = signum;
164         _handler = h;
165         _id = signal_id++;
166     }
167     int opCmp(in Signal other) const nothrow pure @safe {
168         if ( _signum == other._signum ) {
169             return _id < other._id ? -1 : 1;
170         }
171         return _signum < other._signum ? -1 : 1;
172     }
173     override string toString() const @trusted {
174         import std.format: format;
175         return "signal: signum: %d, id: %d".format(_signum, _id);
176     }
177 }
178 
179 struct IORequest {
180     size_t              to_read = 0;
181     bool                allowPartialInput = true;
182     immutable(ubyte)[]  output;
183 
184     void delegate(IOResult) @safe callback;
185 
186 }
187 
188 struct IOResult {
189     immutable(ubyte)[]  input;      // what we received
190     immutable(ubyte)[]  output;     // updated output slice
191     bool                timedout;   // if we timedout
192     bool                error;      // if there was an error
193     string toString() const @trusted {
194         import std.format;
195         return "in:[%(%02X %)], out:[%(%02X %)], tmo: %s, error: %s".format(input, output, timedout?"yes":"no", error?"yes":"no");
196     }
197 }
198 
199 struct CircBuff(T) {
200     enum Size = 512;
201     private
202     {
203         ushort start=0, length = 0;
204         T[Size] queue;
205     }
206 
207     invariant
208     {
209         assert(length<=Size);
210         assert(start<Size);
211     }
212 
213     auto get() @safe
214     in
215     {
216         assert(!empty);
217     }
218     out
219     {
220         assert(!full);
221     }
222     do
223     {
224         enforce(!empty);
225         auto v = queue[start];
226         length--;
227         start = (++start) % Size;
228         return v;
229     }
230 
231     void put(T v) @safe
232     in
233     {
234         assert(!full);
235     }
236     out
237     {
238         assert(!empty);
239     }
240     do
241     {
242         enforce(!full);
243         queue[(start + length)%Size] = v;
244         length++;
245     }
246     bool empty() const @safe @property @nogc nothrow {
247         return length == 0;
248     }
249     bool full() const @safe @property @nogc nothrow {
250         return length == Size;
251     }
252 }
253 
254 alias Broadcast = Flag!"broadcast";
255 
256 //struct NotificationDelivery {
257 //    Notification _n;
258 //    Broadcast    _broadcast;
259 //}
260 
261 //class Notification {
262 //    import  containers;
263 //
264 //    private SList!(void delegate(Notification) @safe) _subscribers;
265 //
266 //    void handler(Broadcast broadcast = Yes.broadcast) @trusted {
267 //        debug tracef("Handle %s".format(broadcast));
268 //        if ( broadcast )
269 //        {
270 //            debug tracef("subscribers %s".format(_subscribers.range));
271 //            foreach(s; _subscribers.range) {
272 //                debug tracef("subscriber %s".format(&s));
273 //                s(this);
274 //                debug tracef("subscriber %s - done".format(&s));
275 //            }
276 //        } else
277 //        {
278 //            auto s = _subscribers.front;
279 //            s(this);
280 //        }
281 //    }
282 //
283 //    void subscribe(void delegate(Notification) @safe s) @safe @nogc nothrow {
284 //        _subscribers ~= s;
285 //    }
286 //
287 //    void unsubscribe(void delegate(Notification) @safe s) @safe @nogc {
288 //        _subscribers.remove(s);
289 //    }
290 //}
291 //@safe unittest {
292 //    import std.stdio;
293 //    class TestNotification : Notification {
294 //        int _v;
295 //        this(int v) {
296 //            _v = v;
297 //        }
298 //    }
299 //    
300 //    auto ueq = CircBuff!Notification();
301 //    assert(ueq.empty);
302 //    assert(!ueq.full);
303 //    foreach(i;0..ueq.Size) {
304 //        auto ue = new TestNotification(i);
305 //        ueq.put(ue);
306 //    }
307 //    assert(ueq.full);
308 //    foreach(n;0..ueq.Size) {
309 //        auto i = ueq.get();
310 //        assert(n==(cast(TestNotification)i)._v);
311 //    }
312 //    assert(ueq.empty);
313 //    foreach(i;0..ueq.Size) {
314 //        auto ue = new TestNotification(i);
315 //        ueq.put(ue);
316 //    }
317 //    assert(ueq.full);
318 //    foreach(n;0..ueq.Size) {
319 //        auto i = ueq.get();
320 //        assert(n==(cast(TestNotification)i)._v);
321 //    }
322 //    //
323 //    int testvar;
324 //    void d1(Notification n) @safe {
325 //        testvar++;
326 //        auto v = cast(TestNotification)n;
327 //    }
328 //    void d2(Notification n) {
329 //        testvar--;
330 //    }
331 //    auto n1 = new TestNotification(1);
332 //    n1.subscribe(&d1);
333 //    n1.subscribe(&d2);
334 //    n1.subscribe(&d1);
335 //    n1.handler(Yes.broadcast);
336 //    assert(testvar==1);
337 //    n1.unsubscribe(&d2);
338 //    n1.handler(Yes.broadcast);
339 //    assert(testvar==3);
340 //}
341 
342 //class Subscription {
343 //    NotificationChannel     _channel;
344 //    void delegate() @safe   _handler;
345 //    shared this(shared NotificationChannel c, void delegate() @safe h) @safe nothrow {
346 //        _channel = c;
347 //        _handler = h;
348 //    }
349 //}
350 //
351 //private shared int shared_notifications_id;
352 //shared class SharedNotification {
353 //    import containers;
354 //    import core.atomic;
355 //    private {
356 //        int                     _n_id;
357 //        //private shared SList!Subscription   _subscribers;
358 //        private Subscription[]  _subscribers;
359 //    }
360 //
361 //    shared this() @safe {
362 //        _n_id = shared_notifications_id;
363 //        atomicOp!"+="(shared_notifications_id, 1);
364 //    }
365 //
366 //    void handler(Broadcast broadcast = Yes.broadcast) @safe {
367 //        if ( broadcast )
368 //        {
369 //            //foreach(s; _subscribers) {
370 //            //    s(this);
371 //            //}
372 //        } else
373 //        {
374 //            //auto s = _subscribers.front;
375 //            //s(this);
376 //        }
377 //    }
378 //
379 //    void subscribe(NotificationChannel c, void delegate() @safe s) @safe nothrow shared {
380 //        _subscribers ~= new shared Subscription(c, s);
381 //    }
382 //
383 //    void unsubscribe(NotificationChannel c, void delegate() @safe s) @safe {
384 //        //_subscribers.remove(Subscription(c, s));
385 //    }
386 //}
387 
388 
389 @safe unittest {
390     //info("testing shared notifications");
391     //auto sna = new shared SharedNotification();
392     //auto snb = new shared SharedNotification();
393     //assert(sna._n_id == 0);
394     //assert(snb._n_id == 1);
395     //shared NotificationChannel c = new shared NotificationChannel;
396     //shared SharedNotification sn = new shared SharedNotification();
397     //sn.subscribe(c, delegate void() {});
398     //info("testing shared notifications - done");
399 }