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             tl();
116 
117             //Thread.sleep(dur!("seconds")(2));
118         }
119     }
120 
121     
122 
123 	/**
124 	* Processes an incoming notification
125 	* accordingly
126 	*/
127 	private void process(byte[] data)
128 	{
129 		/* TODO: Implement me */
130 
131 		/* TODO: Check notification type */
132 		ubyte notificationType = data[0];
133 
134 		/* For normal message (to channel or user) */
135 		if(notificationType == 0)
136 		{
137 			/* TODO: Decode using tristanable */
138 			writeln("new message");
139 		}
140 		/* Channel notification (ntype=1) */
141 		else if(notificationType == 1)
142 		{
143 			/* Get the sub-type */
144 			ubyte subType = data[1];
145 
146 			/* If the notification was leave (stype=0) */
147 			if(subType == 0)
148 			{
149                 /* LeaveInfo: <channel>,<username> */
150 				string[] leaveInfo = split(cast(string)data[2..data.length],",");
151                 writeln("LeaveInfo: ",leaveInfo);
152 
153                 /* Decode the LeaveInfo */
154                 string channel = leaveInfo[0];
155                 string username = leaveInfo[1];
156 
157                 /* Find the channel */
158                 Channel matchedChannel = findChannel(channel);
159 
160                 /* Channel leave */
161                 matchedChannel.channelLeave(username);
162 			}
163 			/* If the notification was join (stype=1) */
164 			else if(subType == 1)
165 			{
166                 /* JoinInfo: <channel>,<username> */
167 				string[] joinInfo = split(cast(string)data[2..data.length],",");
168                 writeln("JoinInfo: ",joinInfo);
169 
170                 /* Decode the JoinInfo */
171                 string channel = joinInfo[0];
172                 string username = joinInfo[1];
173 
174                 /* Find the channel */
175                 Channel matchedChannel = findChannel(channel);
176 
177                 /* Channel join */
178                 matchedChannel.channelJoin(username);
179 			}
180 			/* TODO: Unknown */
181 			else
182 			{
183 				
184 			}
185 		}
186 	}
187 
188 
189 
190 
191 
192     private void channelList()
193     {
194         te();
195         channelList_unsafe();
196         tl();
197     }
198 
199     public DClient getClient()
200     {
201         return client;
202     }
203 
204     /**
205     * Lists all channels and displays them
206     *
207     * Only to be aclled when locked (i.e. by the event
208     * loop signal dispatch or when we lock it
209     * i.e. `channelList`)
210     */
211     private void channelList_unsafe()
212     {
213         string[] channelList = client.list();
214 
215         foreach(string channel; channelList)
216         {
217             channels.add(new Label(channel));
218             channels.showAll();
219         }
220     }
221     
222     private Channel findChannel(string channelName)
223     {
224         Channel result;
225 
226         chansLock.lock();
227 
228         foreach(Channel channel; chans)
229         {
230             if(cmp(channel.getName(), channelName) == 0)
231             {
232                 result = channel;
233                 break;
234             }
235         }
236 
237         chansLock.unlock();
238 
239         return result;
240     }
241 
242     private void addChannel(Channel newChannel)
243     {
244         chansLock.lock();
245 
246         chans ~= newChannel;
247 
248         chansLock.unlock();
249     }
250 
251     private void selectChannel(ListBox s)
252     {
253         /* Get the name of the channel selected */
254         string channelSelected = (cast(Label)(s.getSelectedRow().getChild())).getText();
255 
256         /* Check if we have joined this channel already */
257         Channel foundChannel = findChannel(channelSelected);
258 
259         /* If we have joined this channel before */
260         if(foundChannel)
261         {
262             /* TODO: Switch to */
263             writeln("nope time: "~channelSelected);
264 
265             
266         }
267         /* If we haven't joined this channel before */
268         else
269         {
270             /* Join the channel */
271             client.join(channelSelected);
272 
273             /* Create the Channel object */
274             Channel newChannel = new Channel(client, channelSelected);
275 
276             /* Add the channel */
277             addChannel(newChannel);
278 
279             /* Set as the `foundChannel` */
280             foundChannel = newChannel;
281 
282             /* Get the Widgets container for this channel and add a tab for it */
283             notebookSwitcher.add(newChannel.getBox());
284             notebookSwitcher.setTabLabelText(newChannel.getBox(), newChannel.getName());
285 
286             writeln("hdsjghjsd");
287 
288             writeln("first time: "~channelSelected);
289 
290             /* Get the user's list */
291             newChannel.populateUsersList();
292         }
293 
294         /* Switch to the channel's pane */
295         notebookSwitcher.setCurrentPage(foundChannel.getBox());
296 
297         box.showAll();
298         // notebookSwitcher.showAll();
299 
300         /* TODO: Now add the widget */
301 
302         // /* Set this as the currently selected channel */
303         // currentChannel = channelSelected;
304         // currentChannelLabel.setText(currentChannel);
305         // // currentChannelLabel.show();
306         // // box.show();
307 
308         // /* Fetch a list of members */
309         // string[] members = client.getMembers(channelSelected);
310 
311         // /* Display the members */
312         // users.removeAll();
313         // foreach(string member; members)
314         // {
315         //     users.add(new Label(member));
316         //     users.showAll();
317         // }
318 
319         // /* Clear the text area */
320         // textArea.removeAll();
321         // textArea.showAll();
322     }
323 
324 
325     /**
326     * Creates a message box
327     *
328     * A message box consists of two labels
329     * one being the name of the person who sent
330     * the message and the next being the message
331     * itself
332     */
333     private Box createMessageBox()
334     {
335         return null;
336     }
337 
338     private Box getChatPane()
339     {
340         /* The main page of the tab */
341         Box box = new Box(GtkOrientation.HORIZONTAL, 1);
342 
343         /* The channels box */
344         Box channelBox = new Box(GtkOrientation.VERTICAL, 1);
345 
346         /* The channel's list */
347         channels = new ListBox();
348         channels.addOnSelectedRowsChanged(&selectChannel);
349 
350         channelBox.add(new Label("Channels"));
351         channelBox.add(channels);
352 
353         // /* The user's box */
354         // Box userBox = new Box(GtkOrientation.VERTICAL, 1);
355 
356         // /* The user's list */
357         // users = new ListBox();
358 
359         // userBox.add(new Label("Users"));
360         // userBox.add(users);
361         
362         // /* The text box */
363         // Box textBox = new Box(GtkOrientation.VERTICAL, 1);
364         // textBox.add(currentChannelLabel);
365         // textArea = new ListBox();
366         // import gtk.ScrolledWindow;
367 
368         // ScrolledWindow scrollTextChats = new ScrolledWindow(textArea);
369         // textBox.add(scrollTextChats);
370         // import gtk.TextView;
371         // textBox.add(new TextView());
372         
373 
374         // import gtk.TextView;
375         // TextView f = new TextView();
376         // textBox.add(f);
377         
378         notebookSwitcher = new Notebook();
379         //notebookSwitcher.add(newnew Label("test"));
380 
381         box.add(channelBox);
382         box.add(notebookSwitcher);
383         // box.add(textBox);
384         //box.packEnd(notebookSwitcher,0,0,0);
385 
386         // textBox.setChildPacking(scrollTextChats, true, true, 0, GtkPackType.START);
387         box.setChildPacking(notebookSwitcher, true, true, 0, GtkPackType.START);
388         
389         
390 
391         return box;
392     }
393 
394     private int getPageNum()
395     {
396         return gui.notebook.pageNum(box);
397     }
398 
399     public void shutdown()
400     {
401         /* This is called from gui.d */
402         int pageNum = getPageNum();
403 
404         if(pageNum == -1)
405         {
406             /* TODO: Error handling */
407         }
408         else
409         {
410             gui.notebook.removePage(pageNum);
411             gui.notebook.showAll();
412         }
413     }
414 }