--
-- LLRNet - network part of LLR
--
-- (C) 2004-2005 Vincent Penne
--
-- Released under GNU LIBRARY GENERAL PUBLIC LICENSE
-- (See file LICENSE that must be included with this software)
--
--

if not LLRglobals and arg[2] == "-no-console" and FreeConsole then
   FreeConsole()
end

statusFile = "llr-guiconfig.txt"

dofile(EXEDIR.."init.lua")

if not dolib("basic") then
   return
end

if not dolib("client") then
   return
end

if not dolib("fox") then
   return
end

function WriteClientsDesc()
   local i, n
   n = getn(clients)
   local file = io.open(EXEDIR..statusFile, "w")
   if not file then
      print("Could not write to", statusFile)
      return
   end
   file:write("clientsDesc = {\n")
   for i=1, n, 1 do
      local client = clients[i]
      local desc = {
	 name = client.name,
	 address = client.address,
	 port = client.port,
	 password = client.password,
	 connected = client.socket,
      }
      file:write(type_dump(desc, i)..",\n")
   end
   file:write("}\n")
   file:close()
end

function UpdateClientsIndex()
   local i
   for i=1, getn(clients), 1 do
      clients[i].index = i-1
   end
end

function ReceiveStructure(socket, struct)
   struct = struct or { }
   local name = net_Recv(socket)
   local n = net_Recv(socket)
   if not n then return end
   n = tonumber(n)
   if not n then return end
   
   local i
   for i=1, n, 1 do
      local index = net_Recv(socket)
      if not index then return end
      if string.sub(index, 1, 1) == "A" then
	 index = string.sub(index, 2, -1)
      else
	 index = tonumber(string.sub(index, 2, -1))
      end
      --print("index", index)

      local value = net_Recv(socket)
      if not value then return end
      if string.sub(value, 1, 1) == "A" then
	 value = string.sub(value, 2, -1)
      elseif string.sub(value, 1, 1) == "N" then
	 value = tonumber(string.sub(value, 2, -1))
      else
	 value = ReceiveStructure(socket)
      end
      --print("value", value)
      if not index or not value then return end
      struct[index] = value
   end

   return struct, name
end

function UpdateResults(list, name, label)
   --print("UpdateResults")
   list = list or resultsList
   name = name or "results"
   label = label or "%d result(s) :"
   list:clearItems()
   local i
   for i=1, clientList:getNumItems(), 1 do
      if clientList:isItemSelected(i-1)~=0 and clients[i][name] then

	 local client = clients[i]
	 local j, n
	 n = getn(client[name])
	 for j=n, 1, -1 do
	    local result = client[name][j]
	    local res
	    if result.result == -2 then
	       res = "Not prime : "..result.residue
	    elseif result.result == 0 then
	       res = "Prime !"
	    else
	       res = "ERROR !!"
	    end
	    local str = format("%s\t%s\t%s\t%s\t%s", 
			       client.name, result.k, result.n, 
			       res, result.date or "N/A")
	    --print(format("%s %s %s %d %s", result.t, result.k, result.n, result.result, result.residue))
	    if result.result ~= -2 then
	       list:prependItem(str)
	    else
	       list:appendItem(str)
	    end
	 end
      end
   end

   getglobals()[name.."Label"]:setText(format(label, list:getNumItems()))
   --print("--> finished")
end

function UpdateUnsents()
   UpdateResults(unsentsList, "unsents", "%d unsent result(s) :")
end

function UpdateWUs(list, name)
   --print("UpdateResults")
   list = WUsList
   list:clearItems()
   local i
   for i=1, clientList:getNumItems(), 1 do
      if clientList:isItemSelected(i-1)~=0 and clients[i].WUs then

	 local client = clients[i]
	 local j, n
	 n = getn(client.WUs)
	 for j=n, 1, -1 do
	    local result = client.WUs[j]
	    local str = format("%s\t%s\t%s", 
			       client.name, result.k, result.n)
	    list:appendItem(str)
	 end
      end
   end
   WUsLabel:setText(format("%d queued work unit(s) :", list:getNumItems()))
end

GUICommands = {
   MSG = function(client)
	    local text = net_Recv(client.socket)
	    local pos = client.console:getLength()
	    local visible = pos<100 or client.console:isPosVisible(client.console:lineStart(client.console:lineStart(pos)-1))
	    --print("visible", visible)
	    client.console:appendText(text, string.len(text), 1)
	    if visible ~= 0 then
	       client.console:setBottomLine(client.console:getLength())
	    end
	 end,
   STATUS=function(client)
	     local msg = net_Recv(client.socket)
	     if msg then
		ClientUpdateStatus(client, msg)
	     end
	  end,
   STRUCT=function(client, socket)
	     --print("STRUCT")
	     local struct, name = ReceiveStructure(socket)
	     --print("-->", name)
	     if struct and name then
		--dump(struct, name)
		if name == "results" then
		   client.results = struct
		end
		if name == "tosend" then
		   client.unsents = struct
		end
		if name == "WUs" then
		   client.WUs = struct
		end
		if clientList:isItemSelected(client.index) then
		   if name == "results" then
		      UpdateResults()
		   end
		   if name == "tosend" then
		      UpdateUnsents()
		   end
		   if name == "WUs" then
		      UpdateWUs()
		   end
		end
	     end
	  end,
   CONNECTED=function(client, socket)
		if not client.loginSent then
		   net_Send(client.socket, "LOGIN")
		   net_Send(client.socket, client.password)
		   client.loginSent = 1
		end
	     end,

   Exit=function(client, socket)
	   --CloseClient(client, 1)
	end
}

function ClientUpdateStatus(client, status)
   if client.status == status then
      return
   end
   client.status = status
   clientList:setItemText(client.index, format("%s\t%s\t%d\t%s", client.name, client.address, client.port, status))
end

clients = { n=0 }
function NewClient(name, address, port, password, connect)
   local client = {
      name = name,
      address = address,
      port = port,
      password = password,
      target = fox.FXDataTarget(),
      status = "Disconnected from client",
      console = fox.FXText(consoleSwitcher,NULL,ID_TEXT,
			   LAYOUT_FILL_X+LAYOUT_FILL_Y+TEXT_SHOWACTIVE,
			   NULL),
   }

   client.console:create()

   local n = clientList:appendItem(format("%s\t%s\t%d\t%s", name, address, port, client.status))
   client.index = n

   if connect then
      ConnectClient(client)
   end
   
   tinsert(clients, client)
   clientList:setCurrentItem(n)
   clientList:selectItem(n)
end

local monitored = { }
function MonitorClientSocket(client)
   if client.socket == nil then
      print("WARNING : MonitorClientSocket called with no socket !")
      return
   end

   if monitored[client.socket] then
      print("ERROR : Socket already in use !")
      return -1
   end

   FXAddCb(client.target, client.func, SEL_IO_READ)

   if application.addInput then
      application:addInput(client.socket, INPUT_READ+INPUT_EXCEPT, client.target, SEL_IO_READ)
   else
      -- WARNING : cannot monitor INPUT_EXCEPT before we've received anything
      client.event = FXaddEvent(application, client.socket, INPUT_READ, client.target, SEL_IO_READ)
      if not client.event then
	 print("Could not create WSAEVENT object")
	 net_Close(client.socket)
	 client.socket = nil
	 FXRemoveCb(client.target)
	 return -1
      end
   end

   monitored[client.socket] = client
end

function UnmonitorClientSocket(client)
   if client.socket then
      if not monitored[client.socket] then
	 print("WARNING : calling UnmonitorClientSocket on a non monitored client !")
      end
      monitored[client.socket] = nil
   else
      print("WARNING : calling UnmonitorClientSocket on a client with no socket !")
   end

   FXRemoveCb(client.target)

   if client.event then
      FXremoveEvent(application, client.event, INPUT_READ+INPUT_EXCEPT)
      client.event = nil
   else
      if client.socket and application.removeInput then
	 application:removeInput(client.socket, INPUT_READ+INPUT_EXCEPT)
      end
   end

end

function ConnectClient(client)

   if client.socket then
      print("Already connected to client")
      return
   end
   
   local socket = net_Connect(client.address, client.port, 1)
   if not socket then
      ClientUpdateStatus(client, "Failed to connect to client ...")
      return
   end

   client.socket = socket
   client.block = 1
   ClientUpdateStatus(client, "Connecting to client ...")

   client.func = 
      function()
	 if not client.socket then
	    return
	 end

	 if client.block then
	    -- put back the socket in blocking mode
	    if not timerPoll then
	       net_NonBlock(client.socket, 0)
	    end
	    client.block = nil
	 end
	 
	 if client.event then
	    FXselectEvent(client.socket, client.event)
	 end

	 local text = net_Recv(client.socket)
	 if not text then
	    print("disconnected from client", client.name, a, b, c, d)
	    CloseClient(client, 1)
	    return 1
	 end
	 
	 --print("command", text)
	 local f = GUICommands[text]
	 if not f then
	    print("bad command", text, "from client", client.name)
	    CloseClient(client, 1)
	    return 1
	 end
	    
	 f(client, socket, text)
	 if client.socket then
	    net_Flush(client.socket)
	 end

	 if client.event then
	    FXresetEvent(client.event)
	    FXselectEvent(client.socket, client.event, INPUT_READ+INPUT_EXCEPT)
	 end
	 
	 return 1
      end

   MonitorClientSocket(client)
end

function CloseClient(client, noremove)
   local socket = client.socket

   if socket then
      UnmonitorClientSocket(client)
   end

   client.socket = nil

   if socket then
      net_Send(socket, "Exit")
      net_Close(socket)
      client.loginSent = nil
   end

   if not noremove then
      tremove(clients, client.index+1)
      UpdateClientsIndex()
      clientList:removeItem(client.index)
      client.target:delete()
      client.target = nil
      client.console:delete()
   else
      ClientUpdateStatus(client, "Disconnected from client")
   end

   client.results = nil
   UpdateResults()
   client.unsents = nil
   UpdateUnsents()
   client.WUs = nil
   UpdateWUs()
end


if not application then
   --print("Creating new FXApp")
   application = FXApp.FXApp("Llrnet - Remote","Llrnet-Remote");
   if not WIN32 and not os.getenv("DISPLAY") then
      return
   end
   application:init();
end

local title = "Llrnet-Remote"
if LLRglobals then
   title = "LLRnet"
end
local main=FXMainWindow.FXMainWindow(application,title,nil,nil,DECOR_ALL,0,0,610,600);
local this = main
mainWindow = main

local dragshell=fox.FXToolbarShell(this,FRAME_RAISED);
local menubar=fox.FXMenubar(this,dragshell,LAYOUT_SIDE_TOP+LAYOUT_FILL_X)
fox.FXToolbarGrip(menubar,menubar,FXMenubar.ID_TOOLBARGRIP,TOOLBARGRIP_DOUBLE);

local filemenu=fox.FXMenuPane(this);
if LLRglobals and WIN32 then
   FXApp.ID_SHOW_TRAY_ICON = FXApp.ID_LAST
   FXApp.ID_LAST = FXApp.ID_LAST + 1
   fox.FXMenuCommand(filemenu,"Close",NULL,this:getApp(),FXApp.ID_QUIT);
   fox.FXMenuCommand(filemenu,"Show tray icon",NULL,this:getApp(),FXApp.ID_SHOW_TRAY_ICON);
else
   fox.FXMenuCommand(filemenu,"&Quit\tCtl-Q",NULL,this:getApp(),FXApp.ID_QUIT);
end
fox.FXMenuTitle(menubar,"&File",NULL,filemenu);

local clientsmenu=fox.FXMenuPane(this);
fox.FXMenuTitle(menubar,"&Clients",NULL,clientsmenu);

fox.FXHorizontalSeparator(this,LAYOUT_SIDE_TOP+SEPARATOR_GROOVE+LAYOUT_FILL_X);

--local group=fox.FXHorizontalFrame(this,FRAME_NONE+LAYOUT_FILL_X+0*LAYOUT_FILL_Y);
local dragshell=fox.FXToolbarShell(this,0*FRAME_RAISED);
local buttons=fox.FXToolbar(this,dragshell,LAYOUT_SIDE_TOP+LAYOUT_FILL_X+FRAME_RAISED+0*PACK_UNIFORM_WIDTH);
fox.FXToolbarGrip(buttons,buttons,FXToolbar.ID_TOOLBARGRIP,TOOLBARGRIP_DOUBLE);


--local contents=fox.FXHorizontalFrame(this,LAYOUT_SIDE_TOP+FRAME_NONE+LAYOUT_FILL_X+LAYOUT_FILL_Y+PACK_UNIFORM_WIDTH);
local contents=fox.FXSplitter(this,LAYOUT_SIDE_TOP+LAYOUT_FILL_X+LAYOUT_FILL_Y+SPLITTER_TRACKING+SPLITTER_VERTICAL);

local vframe1=fox.FXVerticalFrame(contents,FRAME_SUNKEN+FRAME_THICK+LAYOUT_FILL_Y+LAYOUT_LEFT+LAYOUT_FIX_WIDTH, 0, 0, 300, 200);
--local vframe1=fox.FXPacker(contents)

--local right=fox.FXSwitcher(contents,LAYOUT_FILL_X+LAYOUT_FILL_Y+LAYOUT_RIGHT);
local right=fox.FXTabBook(contents,NULL,NULL,FRAME_SUNKEN+FRAME_THICK+LAYOUT_FILL_X+LAYOUT_FILL_Y+LAYOUT_RIGHT);
--local vframe2=fox.FXVerticalFrame(contents,LAYOUT_FILL_X+LAYOUT_FILL_Y+LAYOUT_RIGHT+PACK_UNIFORM_WIDTH);

fox.FXTabItem(right,"&Info",NULL);
infoBox=fox.FXSplitter(right,LAYOUT_SIDE_TOP+LAYOUT_FILL_X+LAYOUT_FILL_Y+SPLITTER_TRACKING+SPLITTER_VERTICAL+SPLITTER_REVERSED);
--infoBox=fox.FXHorizontalFrame(right,LAYOUT_SIDE_TOP+FRAME_NONE+LAYOUT_FILL_X+LAYOUT_FILL_Y+PACK_UNIFORM_WIDTH)

local group = fox.FXVerticalFrame(infoBox,LAYOUT_FILL_X+LAYOUT_FILL_Y+LAYOUT_RIGHT+FRAME_SUNKEN);
resultsLabel = fox.FXLabel(group,"Results :",NULL,LAYOUT_TOP+LAYOUT_FILL_X+FRAME_SUNKEN);
resultsList=fox.FXIconList(group,nil,1,0*LAYOUT_FIX_WIDTH+LAYOUT_FILL_X+LAYOUT_FILL_Y+ICONLIST_EXTENDEDSELECT, 0, 0, 200, 0);
resultsList:appendHeader("Client",NULL,60);
resultsList:appendHeader("K",NULL,60);
resultsList:appendHeader("N",NULL,60);
resultsList:appendHeader("Result",NULL,220);
resultsList:appendHeader("Date",NULL,160);


local hori=fox.FXSplitter(infoBox,LAYOUT_FIX_HEIGHT+LAYOUT_SIDE_TOP+LAYOUT_FILL_X+LAYOUT_FILL_Y+SPLITTER_TRACKING+SPLITTER_HORIZONTAL+SPLITTER_REVERSED, 0, 0, 0, 120);

local group = fox.FXVerticalFrame(hori,LAYOUT_FIX_WIDTH+LAYOUT_FILL_X+LAYOUT_FILL_Y+LAYOUT_RIGHT+FRAME_SUNKEN, 0, 0, 300, 0);
unsentsLabel = fox.FXLabel(group,"Unsent results :",NULL,LAYOUT_TOP+LAYOUT_FILL_X+FRAME_SUNKEN);
unsentsList=fox.FXIconList(group,nil,1,0*LAYOUT_FIX_WIDTH+LAYOUT_FILL_X+LAYOUT_FILL_Y+ICONLIST_EXTENDEDSELECT, 0, 0, 300, 80);
unsentsList:appendHeader("Client",NULL,60);
unsentsList:appendHeader("K",NULL,60);
unsentsList:appendHeader("N",NULL,60);
unsentsList:appendHeader("Result",NULL,220);
unsentsList:appendHeader("Date",NULL,160);

local group = fox.FXVerticalFrame(hori,LAYOUT_FIX_WIDTH+LAYOUT_FILL_X+LAYOUT_FILL_Y+LAYOUT_RIGHT+FRAME_SUNKEN, 0, 0, 220, 0);
WUsLabel = fox.FXLabel(group,"Queued work units :",NULL,LAYOUT_TOP+LAYOUT_FILL_X+FRAME_SUNKEN);
WUsList=fox.FXIconList(group,nil,1,0*LAYOUT_FIX_WIDTH+LAYOUT_FILL_X+LAYOUT_FILL_Y+ICONLIST_EXTENDEDSELECT, 0, 0, 200, 80);
WUsList:appendHeader("Client",NULL,60);
WUsList:appendHeader("K",NULL,60);
WUsList:appendHeader("N",NULL,60);


--fox.FXLabel(vframe2,"These buttons below\nare connected to the\nFXSwitcher Control.\nSo they are checked\nautomatically depending\non the active page\nof the switcher.",NULL,LAYOUT_FILL_Y+JUSTIFY_LEFT+JUSTIFY_TOP);
fox.FXTabItem(right,"&Console",NULL);
consoleSwitcher=fox.FXSwitcher(right,LAYOUT_FILL_X+LAYOUT_FILL_Y+LAYOUT_RIGHT);


fox.FXLabel(vframe1,"Clients :",NULL,LAYOUT_TOP+LAYOUT_FILL_X+FRAME_SUNKEN);

--clientListTarget=fox.FXDebugTarget();
clientListTarget = fox.FXDataTarget()
clientList=fox.FXIconList(vframe1,clientListTarget,1,0*LAYOUT_FIX_WIDTH+LAYOUT_FILL_X+LAYOUT_FILL_Y+ICONLIST_EXTENDEDSELECT, 0, 0, 300, 0);

FXAddCb(clientListTarget, 
	function(this, sender, ev1, ev2, index)
	   --print(ev1, ev2, data)
	   --clients[index+1].console:show()
	   FXSendCommand(consoleSwitcher, clientListTarget, FXSwitcher.ID_OPEN_FIRST+index, SEL_COMMAND)
	end,
	SEL_CLICKED)
FXAddCb(clientListTarget,
	function()
	   UpdateResults()
	   UpdateUnsents()
	   UpdateWUs()
	end,
	SEL_SELECTED)
FXAddCb(clientListTarget,
	function()
	   UpdateResults()
	   UpdateUnsents()
	   UpdateWUs()
	end,
	SEL_DESELECTED)
FXAddCb(clientListTarget,
	function(this, sender, event1, event2, x,y,d3,d4)
	   --print("popup")
	   --clientsmenu:show()
	   --print(x,y)
	   clientsmenu:popup(nil, x, y)
	   application:runModalWhileShown(clientsmenu)
	   return 1
	end,
	SEL_RIGHTBUTTONRELEASE)
  
clientList:appendHeader("Name",NULL,60);
clientList:appendHeader("Address",NULL,100);
clientList:appendHeader("Port",NULL,40);
clientList:appendHeader("Status",NULL,1000);

--local buttons=fox.FXHorizontalFrame(vframe1,FRAME_NONE+LAYOUT_FILL_X+PACK_UNIFORM_WIDTH);
if LLRglobals and WIN32 then
   fox.FXButton(buttons,"&Close\tClose Llrnet-GUI",NULL,application,ID_QUIT,FRAME_THICK+FRAME_RAISED);
else
   fox.FXButton(buttons,"&Quit\tQuit Llrnet-GUI",NULL,application,ID_QUIT,FRAME_THICK+FRAME_RAISED);
end

local target=fox.FXDataTarget();
fox.FXMenuCommand(clientsmenu,"&Reconnect",NULL,target, 332);
local reconnect =
   fox.FXButton(buttons,"&Reconnect",NULL,target,332,FRAME_THICK+FRAME_RAISED);

FXAddCb(target, 
	function(this, sender, event1, event2) 
	   local i
	   for i=1, clientList:getNumItems(), 1 do
	      if clientList:isItemSelected(i-1)~=0 then
		 ConnectClient(clients[i])
	      end
	   end

	   --print("Button release")
	   --return 1
	end,
	SEL_COMMAND)

local target=fox.FXDataTarget();
fox.FXMenuCommand(clientsmenu,"&Disconnect",NULL,target, 332);
local disconnect =
   fox.FXButton(buttons,"&Disconnect",NULL,target,332,FRAME_THICK+FRAME_RAISED);

FXAddCb(target, 
	function(this, sender, event1, event2) 
	   local i
	   for i=1, clientList:getNumItems(), 1 do
	      if clientList:isItemSelected(i-1)~=0 then
		 CloseClient(clients[i], 1)
	      end
	   end

	   --print("Button release")
	   --return 1
	end,
	SEL_COMMAND)

local target=fox.FXDataTarget();
fox.FXMenuCommand(clientsmenu,"&Select all",NULL,target, 332);
fox.FXButton(buttons,"Select all",NULL,target,332,
	     FRAME_THICK+FRAME_RAISED);
FXAddCb(target, 
	function(this, sender, event1, event2) 
	   local i
	   for i=1, clientList:getNumItems(), 1 do
	      clientList:selectItem(i-1,1)
	   end
	end,
	SEL_COMMAND)

local target=fox.FXDataTarget();
fox.FXMenuCommand(clientsmenu,"&Deselect all",NULL,target, 332);
--fox.FXButton(buttons,"Deselect all",NULL,target,332,
--	     FRAME_THICK+FRAME_RAISED);
FXAddCb(target, 
	function(this, sender, event1, event2) 
	   local i
	   for i=1, clientList:getNumItems(), 1 do
	      clientList:deselectItem(i-1,1)
	   end
	end,
	SEL_COMMAND)

local target=fox.FXDataTarget();
fox.FXMenuCommand(clientsmenu,"&Remove client(s)",NULL,target, 332);
fox.FXButton(buttons,"Remove client(s)",NULL,target,332,
	     FRAME_THICK+FRAME_RAISED);
FXAddCb(target, 
	function(this, sender, event1, event2) 
	   local i
	   for i=clientList:getNumItems(), 1, -1 do
	      if clientList:isItemSelected(i-1)~=0 then
		 CloseClient(clients[i])
	      end
	   end
	   WriteClientsDesc()
	end,
	SEL_COMMAND)

local target=fox.FXDataTarget();
fox.FXMenuCommand(clientsmenu,"&Add a client",NULL,target, 332);
fox.FXButton(buttons,"Add a client",NULL,target,332,
	     FRAME_THICK+FRAME_RAISED);
FXAddCb(target, 
	function()
	   local dialog = 
	      fox.FXDialogBox(main,"New client",
			      DECOR_TITLE+DECOR_BORDER)

	   local vframe=fox.FXVerticalFrame(dialog,LAYOUT_FILL_Y+LAYOUT_LEFT);
	   local matrix=fox.FXMatrix(vframe,2,FRAME_SUNKEN+FRAME_THICK+MATRIX_BY_COLUMNS+LAYOUT_SIDE_TOP+LAYOUT_FILL_X+LAYOUT_FILL_Y);

	   fox.FXLabel(matrix,"Name",NULL,LAYOUT_CENTER_Y+LAYOUT_RIGHT+JUSTIFY_RIGHT+LAYOUT_FILL_ROW);
	   local name = fox.FXTextField(matrix,10,nil,nil,0*TEXTFIELD_REAL+0*JUSTIFY_RIGHT+LAYOUT_CENTER_Y+LAYOUT_CENTER_X+FRAME_SUNKEN+FRAME_THICK+LAYOUT_FILL_ROW);

	   fox.FXLabel(matrix,"Address",NULL,LAYOUT_CENTER_Y+LAYOUT_RIGHT+JUSTIFY_RIGHT+LAYOUT_FILL_ROW);
	   local address = fox.FXTextField(matrix,10,nil,nil,0*TEXTFIELD_REAL+0*JUSTIFY_RIGHT+LAYOUT_CENTER_Y+LAYOUT_CENTER_X+FRAME_SUNKEN+FRAME_THICK+LAYOUT_FILL_ROW);

	   fox.FXLabel(matrix,"Port",NULL,LAYOUT_CENTER_Y+LAYOUT_RIGHT+JUSTIFY_RIGHT+LAYOUT_FILL_ROW);
	   local port = fox.FXTextField(matrix,10,nil,nil,TEXTFIELD_INTEGER+0*JUSTIFY_RIGHT+LAYOUT_CENTER_Y+LAYOUT_CENTER_X+FRAME_SUNKEN+FRAME_THICK+LAYOUT_FILL_ROW);

	   fox.FXLabel(matrix,"Password",NULL,LAYOUT_CENTER_Y+LAYOUT_RIGHT+JUSTIFY_RIGHT+LAYOUT_FILL_ROW);
	   local password = fox.FXTextField(matrix,10,nil,nil,0*TEXTFIELD_REAL+0*JUSTIFY_RIGHT+LAYOUT_CENTER_Y+LAYOUT_CENTER_X+FRAME_SUNKEN+FRAME_THICK+LAYOUT_FILL_ROW);

	   local buttons=fox.FXHorizontalFrame(vframe,FRAME_NONE+LAYOUT_FILL_X+PACK_UNIFORM_WIDTH)
	   fox.FXButton(buttons,"Add",NULL,dialog,FXDialogBox.ID_ACCEPT,
			FRAME_THICK+FRAME_RAISED);
	   fox.FXButton(buttons,"Cancel",NULL,dialog,FXDialogBox.ID_CANCEL,
			FRAME_THICK+FRAME_RAISED);

	   name:setText("Local")
	   address:setText("localhost")
	   port:setText("6999")
	   password:setText("llrnet")

	   dialog:create()
	   local res = dialog:execute()~=0

	   if res then
	      NewClient(name:getText(), address:getText(), tonumber(port:getText()), password:getText(), 1)

	      WriteClientsDesc()
	   end

	   dialog:delete()

	end,
	SEL_COMMAND)

--fox.FXFileList(vframe1,NULL,0,ICONLIST_EXTENDEDSELECT);

--fox.FXDirList(vframe1,0,NULL,0,TREELIST_SHOWS_LINES+TREELIST_SHOWS_BOXES);


local target=fox.FXDataTarget();
fox.FXMenuCommand(clientsmenu,"&Pause client(s)",NULL,target, 332);
fox.FXButton(buttons,"Pause",NULL,target,332,
	     FRAME_THICK+FRAME_RAISED);
FXAddCb(target, 
	function(this, sender, event1, event2) 
	   local i
	   for i=clientList:getNumItems(), 1, -1 do
	      local client = clients[i]
	      if clientList:isItemSelected(i-1)~=0 and client.socket then
		 net_Send(client.socket, "PAUSE")
		 net_Send(client.socket, "0", 1)
	      end
	   end
	   WriteClientsDesc()
	end,
	SEL_COMMAND)

local target=fox.FXDataTarget();
fox.FXMenuCommand(clientsmenu,"&Resume client(s)",NULL,target, 332);
fox.FXButton(buttons,"Resume",NULL,target,332,
	     FRAME_THICK+FRAME_RAISED);
FXAddCb(target, 
	function(this, sender, event1, event2) 
	   local i
	   for i=clientList:getNumItems(), 1, -1 do
	      local client = clients[i]
	      if clientList:isItemSelected(i-1)~=0 and client.socket then
		 net_Send(client.socket, "RESUME")
		 net_Send(client.socket, "0", 1)
	      end
	   end
	   WriteClientsDesc()
	end,
	SEL_COMMAND)


local target=fox.FXDataTarget();
fox.FXMenuCommand(clientsmenu,"&Kill client(s)",NULL,target, 332);
fox.FXButton(buttons,"Kill",NULL,target,332,
	     FRAME_THICK+FRAME_RAISED);
FXAddCb(target, 
	function(this, sender, event1, event2) 
	   if FXAsk([[
You won\'t be able to connect again 
on a client which has been killed, 
unless you restart it locally.
Kill anyway ?]], 
"Yes", "No !")==0 then
	      return
	   end

	   local i
	   for i=clientList:getNumItems(), 1, -1 do
	      local client = clients[i]
	      if clientList:isItemSelected(i-1)~=0 and client.socket then
		 net_Send(client.socket, "KILL")
		 net_Send(client.socket, "0", 1)
	      end
	   end
	   WriteClientsDesc()
	end,
	SEL_COMMAND)

application:create();

main:show(fox.PLACEMENT_SCREEN);

if pcall(dofile, EXEDIR..statusFile) then
   local i, n
   n = getn(clientsDesc)
   for i=1, n, 1 do
      local client = clientsDesc[i]
      NewClient(client.name, client.address, client.port, client.password, client.connected)
   end
   clientsDesc = nil
else
   NewClient("Local", "localhost", 6999, "llrnet", 1)
--   local i
--   for i=1,10,1 do
--      NewClient(format("Local%d", i), "localhost", 6999-i, "llrnet")
--   end
end

FXAddCb(application, 
	function(a,b,event)
	   if event == FXApp.ID_QUIT and trayIcon then
	      mainWindow:hide()
	      return 1
	   end
	   if event == FXApp.ID_SHOW_TRAY_ICON and not trayIcon then
	      trayIcon = TrayIconAdd("LLRtray", 0, nil, "LLRnet")
	      hideTrayIcon = nil
	   end
	end,
	SEL_COMMAND)

-- we run a 300ms timer for asynchronous GUI commands
-- it should not noticeably slow down anything
GUIcommandQueue = { }
if LLRglobals then
   application:addTimeout(100, application, SEL_TIMEOUT)
   FXAddCb(application, 
	   function()
	      while GUIcommandQueue[1] do
		 local cmd = GUIcommandQueue[getn(GUIcommandQueue)]
		 tremove(GUIcommandQueue)
		 cmd()
	      end
	      application:addTimeout(300, application, SEL_TIMEOUT)
	   end, 
	   SEL_TIMEOUT)

--   if WIN32 and not hideTrayIcon then
--      trayIcon = TrayIconAdd("LLRtray", 0, nil, serviceName)
--   end
end

local res = application:run();

WriteClientsDesc()

-- close all clients
print("GUI : Closing connection to all clients")
local i
for i=1,getn(clients),1 do
   CloseClient(clients[i], 1)
end
clients = {}

if not trayIcon and LLRglobals and not GUIquietExit then
   local msg = "Do you want to quit the local LLRnet client too ?"
   if WIN32 and not serviceRunning then
      msg = msg..[[

If not, to stop the LLRnet client later you will need to 
open Windows task manager and kill the process.]]
   end
   local res = FXAsk(msg,
		     "Yes, \nquit LLRnet too", 
		     "No, \njust close the GUI and leave LLRnet in the background")
   
   if res~=0 then
      LLRglobals.GUIexit = 1
      LLRglobals.paused = nil
      LLRglobals.sleeping = nil
      stopSet()
   end
   
end

if GUItrayIcon then
   TrayIconRemove(GUItrayIcon)
   GUItrayIcon = nil
end

mainWindow:hide()
mainWindow:delete()
mainWindow = nil
application:flush()

-- reset all callbacks
fxHandlers = { }
fxCmdHandlers = { }

--application:delete()
application = nil

return res
