1 module Connection;
2 
3 import core.thread;
4 import gui;
5 import gdk.Threads : te = threadsEnter, tl = threadsLeave;
6 import gtk.Box;
7 import std.stdio;
8 import libdnet.dclient;
9 import std.socket;
10 import gtk.ListBox;
11 import gtk.Label;
12 
13 import Channel;
14 import std.string;
15 
16 import core.sync.mutex;
17 
18 import gtk.Notebook;
19 
20 public final class Connection : Thread
21 {
22     private GUI gui;
23     private Box box;
24     private ListBox channels;
25     private ListBox users;
26     private ListBox textArea;
27 
28     private DClient client;
29     private Address address;
30     private string[] auth;
31 
32     /* TODO: Check if we need to protect */
33     /* TODO: So far usage is in signal handlers (mutex safved) and within te-tl lock for notifications */
34     private string currentChannel; /* TODO: Used to track what notificaitons come throug */
35     private Label currentChannelLabel; /* TODO: Using getChild would be nicer, but yeah, this is for the title */
36 
37     /**
38     * All joined Channel-s in this Connection 
39     */
40     private Notebook notebookSwitcher;
41     private Channel[] chans; /*TODO: Technically locking by GTK would make this not needed */
42     private Mutex chansLock;
43     private Channel focusedChan;
44 
45     this(GUI gui, Address address, string[] auth)
46     {
47         super(&worker);
48         this.gui = gui;
49         this.address = address;
50         this.auth = auth;
51 
52         /* Initialize locks */
53         initializeLocks();
54 
55         /* Start the notification atcher */
56         start();
57     }
58 
59     /**
60     * Initializes all locks (other than GDK)
61     */
62     private void initializeLocks()
63     {
64         chansLock =  new Mutex();
65     }
66 
67     private void worker()
68     {
69         /* Create a new Label */
70         currentChannelLabel = new Label("CHANNEL NAME GOES HERE");
71 
72         /**
73         * Setup the tab for this connection
74         */
75         te();
76         box = getChatPane();
77         gui.notebook.add(box);
78         //gui.notebook    setChildPacking(box, true, true, 0, GtkPackType.START);
79        // gui.mainWindow.
80         gui.notebook.setTabLabelText(box, auth[0]~"@"~address.toString());
81         gui.notebook.showAll();
82         tl();
83 
84 
85         /**
86         * Connects and logs in
87         */
88         client = new DClient(address);
89         client.auth(auth[0], auth[1]); /* TODO: DO this without auth (the list in the loop, crahses server) */
90 
91         /* Display all channels */
92         channelList();
93 
94         /**
95         * Notification loop
96         *
97         * Awaits notifications and then displays them
98         */
99         while(true)
100         {
101             /* Receive a notification */
102             byte[] notificationData = client.awaitNotification();
103             writeln(notificationData);
104 
105             te();
106             // import std.conv;
107             // textArea.add(new Label(to!(string)(notificationData)));
108             // textArea.showAll();
109 
110             process(notificationData);
111             //gui.mainWindow.showAll();
112 
113             notebookSwitcher.showAll();
114             
115 
116             tl();
117 
118             //Thread.sleep(dur!("seconds")(2));
119         }
120     }
121 
122     
123 
124 	/**
125 	* Processes an incoming notification
126 	* accordingly
127 	*/
128 	private void process(byte[] data)
129 	{
130 		/* TODO: Implement me */
131 
132 		/* TODO: Check notification type */
133 		ubyte notificationType = data[0];
134 
135 		/* For normal message (to channel or user) */
136 		if(notificationType == 0)
137 		{
138 			/* TODO: Decode using tristanable */
139 			writeln("new message");
140 		}
141 		/* Channel notification (ntype=1) */
142 		else if(notificationType == 1)
143 		{
144 			/* Get the sub-type */
145 			ubyte subType = data[1];
146 
147 			/* If the notification was leave (stype=0) */
148 			if(subType == 0)
149 			{
150                 /* LeaveInfo: <channel>,<username> */
151 				string[] leaveInfo = split(cast(string)data[2..data.length],",");
152                 writeln("LeaveInfo: ",leaveInfo);
153 
154                 /* Decode the LeaveInfo */
155                 string channel = leaveInfo[0];
156                 string username = leaveInfo[1];
157 
158                 /* Find the channel */
159                 Channel matchedChannel = findChannel(channel);
160 
161                 /* Channel leave */
162                 matchedChannel.channelLeave(username);
163 			}
164 			/* If the notification was join (stype=1) */
165 			else if(subType == 1)
166 			{
167                 /* JoinInfo: <channel>,<username> */
168 				string[] joinInfo = split(cast(string)data[2..data.length],",");
169                 writeln("JoinInfo: ",joinInfo);
170 
171                 /* Decode the JoinInfo */
172                 string channel = joinInfo[0];
173                 string username = joinInfo[1];
174 
175                 /* Find the channel */
176                 Channel matchedChannel = findChannel(channel);
177 
178                 /* Channel join */
179                 matchedChannel.channelJoin(username);
180 			}
181 			/* TODO: Unknown */
182 			else
183 			{
184 				
185 			}
186 		}
187 	}
188 
189 
190 
191 
192 
193     private void channelList()
194     {
195         te();
196         channelList_unsafe();
197         tl();
198     }
199 
200     public DClient getClient()
201     {
202         return client;
203     }
204 
205     /**
206     * Lists all channels and displays them
207     *
208     * Only to be aclled when locked (i.e. by the event
209     * loop signal dispatch or when we lock it
210     * i.e. `channelList`)
211     */
212     private void channelList_unsafe()
213     {
214         string[] channelList = client.list();
215 
216         foreach(string channel; channelList)
217         {
218             channels.add(new Label(channel));
219             channels.showAll();
220         }
221     }
222     
223     private Channel findChannel(string channelName)
224     {
225         Channel result;
226 
227         chansLock.lock();
228 
229         foreach(Channel channel; chans)
230         {
231             if(cmp(channel.getName(), channelName) == 0)
232             {
233                 result = channel;
234                 break;
235             }
236         }
237 
238         chansLock.unlock();
239 
240         return result;
241     }
242 
243     private void addChannel(Channel newChannel)
244     {
245         chansLock.lock();
246 
247         chans ~= newChannel;
248 
249         chansLock.unlock();
250     }
251 
252     private void selectChannel(ListBox s)
253     {
254         /* Get the name of the channel selected */
255         string channelSelected = (cast(Label)(s.getSelectedRow().getChild())).getText();
256 
257         /* Check if we have joined this channel already */
258         Channel foundChannel = findChannel(channelSelected);
259 
260         /* If we have joined this channel before */
261         if(foundChannel)
262         {
263             /* TODO: Switch to */
264             writeln("nope time: "~channelSelected);
265 
266             
267         }
268         /* If we haven't joined this channel before */
269         else
270         {
271             /* Join the channel */
272             client.join(channelSelected);
273 
274             /* Create the Channel object */
275             Channel newChannel = new Channel(client, channelSelected);
276 
277             /* Add the channel */
278             addChannel(newChannel);
279 
280             /* Set as the `foundChannel` */
281             foundChannel = newChannel;
282 
283             /* Get the Widgets container for this channel and add a tab for it */
284             notebookSwitcher.add(newChannel.getBox());
285             notebookSwitcher.setTabLabelText(newChannel.getBox(), newChannel.getName());
286 
287             writeln("hdsjghjsd");
288 
289             writeln("first time: "~channelSelected);
290 
291             /* Get the user's list */
292             newChannel.populateUsersList();
293         }
294 
295         /* Switch to the channel's pane */
296         notebookSwitcher.setCurrentPage(foundChannel.getBox());
297 
298         box.showAll();
299         // notebookSwitcher.showAll();
300 
301         /* TODO: Now add the widget */
302 
303         // /* Set this as the currently selected channel */
304         // currentChannel = channelSelected;
305         // currentChannelLabel.setText(currentChannel);
306         // // currentChannelLabel.show();
307         // // box.show();
308 
309         // /* Fetch a list of members */
310         // string[] members = client.getMembers(channelSelected);
311 
312         // /* Display the members */
313         // users.removeAll();
314         // foreach(string member; members)
315         // {
316         //     users.add(new Label(member));
317         //     users.showAll();
318         // }
319 
320         // /* Clear the text area */
321         // textArea.removeAll();
322         // textArea.showAll();
323     }
324 
325 
326     /**
327     * Creates a message box
328     *
329     * A message box consists of two labels
330     * one being the name of the person who sent
331     * the message and the next being the message
332     * itself
333     */
334     private Box createMessageBox()
335     {
336         return null;
337     }
338 
339     private Box getChatPane()
340     {
341         /* The main page of the tab */
342         Box box = new Box(GtkOrientation.HORIZONTAL, 1);
343 
344         /* The channels box */
345         Box channelBox = new Box(GtkOrientation.VERTICAL, 1);
346 
347         /* The channel's list */
348         channels = new ListBox();
349         channels.addOnSelectedRowsChanged(&selectChannel);
350 
351         channelBox.add(new Label("Channels"));
352         channelBox.add(channels);
353 
354         // /* The user's box */
355         // Box userBox = new Box(GtkOrientation.VERTICAL, 1);
356 
357         // /* The user's list */
358         // users = new ListBox();
359 
360         // userBox.add(new Label("Users"));
361         // userBox.add(users);
362         
363         // /* The text box */
364         // Box textBox = new Box(GtkOrientation.VERTICAL, 1);
365         // textBox.add(currentChannelLabel);
366         // textArea = new ListBox();
367         // import gtk.ScrolledWindow;
368 
369         // ScrolledWindow scrollTextChats = new ScrolledWindow(textArea);
370         // textBox.add(scrollTextChats);
371         // import gtk.TextView;
372         // textBox.add(new TextView());
373         
374 
375         // import gtk.TextView;
376         // TextView f = new TextView();
377         // textBox.add(f);
378         
379         notebookSwitcher = new Notebook();
380         notebookSwitcher.setScrollable(true);
381         //notebookSwitcher.add(newnew Label("test"));
382 
383         box.add(channelBox);
384         box.add(notebookSwitcher);
385         // box.add(textBox);
386         //box.packEnd(notebookSwitcher,0,0,0);
387 
388         // textBox.setChildPacking(scrollTextChats, true, true, 0, GtkPackType.START);
389         box.setChildPacking(notebookSwitcher, true, true, 0, GtkPackType.START);
390         
391         
392 
393         return box;
394     }
395 
396     private int getPageNum()
397     {
398         return gui.notebook.pageNum(box);
399     }
400 
401     public void shutdown()
402     {
403         /* This is called from gui.d */
404         int pageNum = getPageNum();
405 
406         if(pageNum == -1)
407         {
408             /* TODO: Error handling */
409         }
410         else
411         {
412             gui.notebook.removePage(pageNum);
413             gui.notebook.showAll();
414         }
415     }
416 }