1 module libwasm.array;
2 
3 import libwasm.event;
4 import libwasm.node;
5 import memutils.ct;
6 import libwasm.node;
7 import libwasm.dom;
8 import libwasm.types;
9 import libwasm.rt.array;
10 
11 nothrow:
12 @safe:
13 
14 template extractEventPaths(T, Ts...)
15 {
16   import std.meta : staticMap, AliasSeq;
17   import std.traits : getSymbolsByUDA;
18 
19   alias events = getSymbolsByUDA!(T, eventemitter);
20   alias children = getChildren!T;
21   template recur(string NextT)
22   {
23     alias recur = extractEventPaths!(typeof(__traits(getMember, T, NextT)), AliasSeq!(Ts, NextT));
24   }
25 
26   template prefixNames(string Event)
27   {
28     import memutils.ct : tuple;
29 
30     enum prefixNames = tuple(Ts, Event);
31   }
32 
33   alias eventNames = staticMap!(getName, AliasSeq!(events));
34   alias prefixed = staticMap!(prefixNames, eventNames);
35   alias extractEventPaths = AliasSeq!(prefixed, staticMap!(recur, getChildren!T));
36 }
37 
38 template join(string delimiter, elems...)
39 {
40   static if (elems.length == 0)
41     enum join = "";
42   else static if (elems.length == 1)
43     enum join = elems[0];
44   else
45     enum join = elems[0] ~ delimiter ~ join!(delimiter, elems[1 .. $]);
46 }
47 
48 mixin template ArrayItemEvents(T)
49 {
50   static foreach (path; extractEventPaths!(T))
51   {
52     mixin Slot!(join!("_", path.expand));
53     mixin("void __" ~ join!("_", path.expand) ~ "(size_t addr) { " ~ join!("_", path.expand) ~ ".emit(this.getIndexInArray(addr));}");
54   }
55 }
56 
57 void assignEventListeners(T)(ref HTMLArray!T arr, ref T item)
58 {
59   alias eventPaths = extractEventPaths!(T);
60   static foreach (path; eventPaths)
61   {
62     mixin("item." ~ join!(".", path.expand) ~ ".add(&arr.__" ~ join!("_", path.expand) ~ ");");
63   }
64 }
65 
66 auto getIndexInArray(T)(auto ref HTMLArray!T arr, size_t ptr)
67 {
68   import std.algorithm : countUntil;
69 
70   return arr[].countUntil!((ref T* item) {
71     auto baseAddr = cast(size_t)(cast(void*) item);
72     return baseAddr <= ptr && (baseAddr + T.sizeof) > ptr;
73   });
74 }
75 
76 struct HTMLArray(T)
77 {
78   @child DynamicArray!(T*) appender;
79   mixin ArrayItemEvents!T;
80   alias appender this;
81   void put(T* t)
82   {
83     this.assignEventListeners(*t);
84     appender.put(t);
85   }
86 }
87 
88 struct Updater(T)
89 {
90   private
91   {
92     T* list;
93     size_t idx;
94   }
95   this(T* list)
96   {
97     this.list = list;
98     foreach (i; list.items)
99     {
100       i.node.marked = true;
101     }
102   }
103 
104   ~this()
105   {
106     if (idx < list.items.length)
107       foreach (i; list.items[idx .. $])
108       {
109         if (i.node.marked)
110           unmount(*i);
111       }
112     list.items.shrinkTo(idx);
113   }
114 
115   void put(Item)(Item* t)
116   {
117     size_t i = idx++;
118     t.node.marked = false;
119     if (list.items.length > i)
120     {
121       if (list.items[i] == t)
122       {
123         return;
124       }
125       // TODO: here we can use replaceChild
126       if (list.items[i].node.marked)
127       {
128         unmount(*list.items[i]);
129       }
130       if (idx + 1 < list.items.length)
131       {
132         if (list.items[i + 1] == t)
133         {
134           list.items.remove(i);
135           return;
136         }
137         else
138         {
139           list.items[i] = t;
140           list.items.assignEventListeners(*t);
141           if (list.items[i + 1].node.marked)
142             list.node.renderBefore((*list.items[i]), list.items[i + 1].node);
143           else
144           {
145             size_t off = 2;
146             while (i + off < list.items.length)
147             {
148               if (list.items[i + off].node.marked)
149               {
150                 list.node.renderBefore((*list.items[i]), list.items[i + off].node);
151                 return;
152               }
153               off += 1;
154             }
155             list.node.render(*list.items[i]);
156             return;
157           }
158         }
159       }
160       else
161       {
162         list.items[i] = t;
163         list.items.assignEventListeners(*t);
164         list.node.render(*list.items[i]);
165       }
166     }
167     else
168     {
169       list.put(t);
170     }
171   }
172 }
173 
174 import std.traits : hasMember, isPointer;
175 
176 bool removeItem(T)(ref T app, size_t idx)
177 { //}if (hasMember!(T, "remove")) {
178   app.remove(idx);
179   return true;
180 }
181 
182 bool removeItem(T, U)(ref T app, ref U t) if (isPointer!(U))
183 {
184   import std.algorithm : countUntil, remove;
185 
186   auto idx = app[0 .. $].countUntil(t);
187   if (idx == -1)
188     return false;
189   return app.removeItem(idx);
190 }
191 
192 struct List(T, string tag)
193 {
194   mixin NodeDef!tag;
195   @child HTMLArray!(T) items;
196   void put(T* t)
197   {
198     items.put(t);
199     libwasm.dom.render(node, *items[$ - 1]);
200   }
201 
202   void shrinkTo(size_t size)
203   {
204     if (size < items.length)
205       foreach (i; items[size .. $])
206       {
207         unmount(*i);
208       }
209     items.shrinkTo(size);
210   }
211 
212   void remove(size_t idx)
213   {
214     
215       .removeChild(items.appender[idx]);
216     import std.algorithm : remove;
217 
218     items.appender.removeItem(idx);
219   }
220 }
221 
222 alias UnorderedList(T) = List!(T, "ul");