--
-- 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 1 then

-- bufferized versions of net_Send
local sendBuf = { }
local net_Send_orig = net_Send
function net_Send(socket, str, flush)
   local t = sendBuf[socket]
   if not t then t = { } sendBuf[socket] = t end
   tinsert(t, str)
   if flush then
      return net_Flush(socket)
   end
   return 1
end

function net_Flush(socket)
   local t
   t = sendBuf[socket]
   if not t then return end
   local res  = net_SendTable(socket, t)
   sendBuf[socket] = nil
   return res
end

local net_Recv_orig = net_Recv
function net_Recv(socket)
   net_Flush(socket)
   return net_Recv_orig(socket)
end

local net_Close_orig = net_Close
function net_Close(socket)
   net_Flush(socket)
   return net_Close_orig(socket)
end

else

net_Flush = function() return 1 end

end


function WriteTosendfile()
   local file = openfile(tosendFile, "w")
   if not file then
      return -1
   end

   local i
   for i=1, getn(tosend), 1 do
      write(file, format("%s %s %s %d %s\n", tosend[i].t, tosend[i].k, tosend[i].n, tosend[i].result, tosend[i].residue))
   end
   closefile(file)
end

function ReadTosendfile()
   tosend = { }

   local file = openfile(tosendFile, "r")
   if not file then
      return
   end

   local t, k, n, result, residue
   while 1 do
      t = read(file, "*w")
      k = read(file, "*w")
      n = read(file, "*w")
      result = read(file, "*n")
      residue = read(file, "*w")
      if t and k and n and result and residue then
	 tinsert(tosend, { t = t, k = k, n = n, result = result, residue = residue } )
      else
	 break
      end
   end
   closefile(file)
end

function WriteWorkfile(t, k, n, more)
   local file = openfile(workFile, "w")
   if not file then
      return -1
   end
   write(file, format("%s\n", t))
   write(file, format("%s %s\n", k, n))
   local i
   for i=1, getn(more), 1 do
      write(file, format("%s %s\n", more[i].k, more[i].n))
   end
   closefile(file)
end

function ClearWorkfile()
   local file = openfile(workFile, "w")
   if not file then
      return -1
   end
   closefile(file)
end

function ReadWorkfile()
   local file = openfile(workFile, "r")
   if not file then
      return
   end
   
   local t = read(file, "*w")
   local k, n
   local more = { }
   k = read(file, "*w")
   n = read(file, "*w")
   while 1 do
      local k = read(file, "*w")
      local n = read(file, "*w")
      if k and n then
	 tinsert(more, { k = k, n = n } )
      else
	 break
      end
   end
   closefile(file)

   return t, k, n, more
end

-- Login on the server
function Login(ps, pp)
   if socket then
      return
   end
   
   -- connect on the server
   socket = net_Connect(ps or server, pp or port)

   if not socket then
      if not asynchronous then
	 print("Error connecting to server!")
      end
      
      return -1
   end

--   print("Successfully connected to server!")

   -- login
   net_Send(socket, "Login")
   net_Send(socket, username)
   net_Send(socket, password)

   -- get login confirmation from the server
   local res = net_Recv(socket)
   if res ~= "LOGGED" then
      print("Could not log on to the server")
      Logout()
      return -1
   end

--   print("LOGGED ON")
end

-- Logout from the server
function Logout()
   -- close the connection
   if socket then
--      print("LOGGING OUT")

      net_Send(socket, "Exit")

      net_Close(socket)
      socket = nil
   end
end

-- Ask for a type of test and k/n pair to work on from the server
-- Returns : t, k, n if success, nil if failed
function GetPair()

   if Login() then
      return
   end

   -- ask for a new k/n pair
   net_Send(socket, "AskPair")

   -- get an answer from the server
   local res
   res = net_Recv(socket)
   if res ~= "OK" then
      if not asynchronous then
	 print("No k/n pairs given by the server")
      end
      return
   end
   local t = net_Recv(socket)
   local k = net_Recv(socket)
   local n = net_Recv(socket)
   if not t or not k or not n then
      print("No k/n pairs given by the server")
      return
   end

   -- confirm client works on given pair
   net_Send(socket, t)
   net_Send(socket, k)
   net_Send(socket, n)
   res = net_Recv(socket)
   if res ~= "OK" then
      print("Need to choose a new pair")
      return
   end

   return t, k, n
end

-- Send a result to the server
-- returns : nil if success, 
--           -1 if connection error, 
--           -2 if result not accepted by the server
function SendResult(t, k, n, result)
   if Login() then
      return -1
   end

   if not net_Send(socket, "GiveResult") then return -1 end
   if not net_Send(socket, t) then return -1 end
   if not net_Send(socket, k) then return -1 end
   if not net_Send(socket, n) then return -1 end
   if not net_Send(socket, result) then return -1 end
   local res = net_Recv(socket)

   -- check reception confirmation from the server
   if res ~= "OK" then
      Logout()

      return -2
   end
end

function Exit()
   Logout()

   exit(0)
end

-- Send all results (tosend table content)
function SendAllResults(retries)
   if not tosend[1] then
      return
   end

   local changed, firstsubmit

   retries = retries or 1 -- maximum number of time to try to send result

   local sendres = -1

   -- try to send results to the server
   print("["..date("%Y-%m-%d\ %H:%M:%S").."]")
   while tosend[1] do
      local r = tosend[1]
      local t, k, n, result, residue = r.t, r.k, r.n, r.result, r.residue

      local i=0
      repeat
	 if result == 0 then
	    sendres = SendResult(t, k, n, "0")
	    if not sendres and OnPrime then
	       OnPrime(t, k, n)
	    end
	 elseif result == -2 then
	    sendres = SendResult(t, k, n, residue)
	 else
	    sendres = SendResult(t, k, n, "ERROR")
	 end
	 i = i + 1

	 --print(res, i)

	 if not sendres then
	    if OnSubmit then
	       OnSubmit(t, k, n, result, residue)
	    end

--	    if not firstsubmit then
--	       print("["..date("%c").."]")
--	    end
--	    OutputBoth(format("Result %s/%s successfully sent to the server.", k, n))
--	    if not firstsubmit then
--	       print("")
--	       firstsubmit = 1
--	    end
	 end

	 -- server refused our result, ignore it
	 if sendres == -2 then
	    local i
	    --Logout() -- TEMP
	    --for i=1,1000,1 do
	    OutputBoth([[The server refused your new result : 
either someone else computed it already 
or the server is now configured to 
work on other numbers.]])
	    --end
	    sendres = nil -- treat it as successful
	    break
	 end

	 if stopCheck() then
	    return -- return with no error
	 end

	 if sendres then
	    Logout()

	    if not asynchronous then
	       OutputBoth("Could not send result.")
	    end

	    -- wait one minute before retrying
	    if i<retries then
	       print("Sleeping for one minute...")
	       sleep(60)
	    end
	 end
      until not sendres or i>=retries

      if sendres then
	 return -1
      end

      tremove(tosend, 1)
      changed = 1
      if WriteTosendfile() then
	 return -1
      end
   end

   if changed then
      if SendStructureToAllGUIs then
	 SendStructureToAllGUIs("tosend", tosend, 1)
      end
   end
end

return 1
