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