{
 this file is part of Ares
 Aresgalaxy ( http://aresgalaxy.sourceforge.net )

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 }

unit mysupernodes;

interface

uses
 classes,sysutils,blcksock,synsock;

 type
 precord_mysupernode=^record_mysupernode;
 record_mysupernode=record
  fip:cardinal;
  fport:word;
  fresultId:integer;
  fLastPing:cardinal;
  FlastPong:cardinal;
 end;

procedure mysupernodes_create;
procedure mysupernodes_free;
procedure mysupernodes_clear;
procedure mysupernodes_add(ip:cardinal; port:word; resultid:integer);
procedure mysupernodes_remove(ip:cardinal);
function mysupernodes_serialize:string;
function GetDotServerStr:string;
function GetServerStrBinary:string;
procedure GetServerDetails(var ip:cardinal; var port:word);
function IsServerAvailable(ip:cardinal; port:word):boolean;
procedure mySupernodes_ping(Tick:cardinal; USocket:Hsocket);
procedure mySupernodes_pong(Tick:cardinal; ip:cardinal; port:word);
function mySupernodes_count:integer;
function IsSupernodeIP(ip:cardinal):boolean;

var
 fMySupernodes:TThreadList;
 fmysuplistcount:byte;

implementation

uses
 helper_strings,helper_ipfunc,const_timeouts,const_udpTransfer,
 windows;

function mySupernodes_count:integer;
begin
result:=fmysuplistcount;
end;

procedure mysupernodes_create;
begin
fmysuplistcount:=0;
fmySupernodes:=TThreadList.create;
end;

procedure mysupernodes_free;
begin
mysupernodes_clear;
fmySupernodes.free;
end;

function IsSupernodeIP(ip:cardinal):boolean;  
var
list:tlist;
sup:precord_mysupernode;
i:integer;
begin
result:=false;

list:=fmySupernodes.locklist;

 for i:=0 to list.count-1 do begin
  sup:=list[i];
  if sup^.fip=ip then begin
   result:=true;
   break;
  end;
 end;

fmySupernodes.unlocklist;
end;

procedure mysupernodes_clear;
var
list:tlist;
sup:precord_mysupernode;
begin
if fmysuplistcount=0 then exit;

list:=fmySupernodes.locklist;

 while (list.count>0) do begin
   sup:=list[list.count-1];
        list.delete(list.count-1);
   FreeMem(sup,sizeof(record_mysupernode));
 end;

fmySupernodes.unlocklist;

fmysuplistcount:=0;
end;

function IsServerAvailable(ip:cardinal; port:word):boolean;
var
list:tlist;
sup:precord_mysupernode;
i:integer;
begin
result:=false;
if fmysuplistcount=0 then exit;
if ((ip=0) or (port=0)) then exit;

list:=fmySupernodes.locklist;

for i:=0 to list.count-1 do begin
 sup:=list[i];
 if sup^.Fip=ip then
  if sup^.Fport=port then begin
   result:=true;
   break;
  end;
end;

fmySupernodes.Unlocklist;
end;

procedure GetServerDetails(var ip:cardinal; var port:word);
var
list:tlist;
sup:precord_mysupernode;
begin
if fmysuplistcount=0 then begin
 ip:=0;
 port:=0;
 exit;
end;

list:=fmySupernodes.locklist;
  sup:=list[list.count-1];
  ip:=sup^.fip;
  port:=sup^.fport;
fmySupernodes.Unlocklist;
end;

function GetDotServerStr:string;
var
list:tlist;
sup:precord_mysupernode;
begin
if fmysuplistcount=0 then begin
 result:=cAnyHost+':0';
 exit;
end;

list:=fmySupernodes.locklist;
  sup:=list[list.count-1];
  result:=ipint_to_dotstring(sup^.Fip)+':'+inttostr(sup^.fport);
fmySupernodes.Unlocklist;
end;

function GetServerStrBinary:string;
var
list:tlist;
sup:precord_mysupernode;
begin
if fmysuplistcount=0 then begin
 result:=chr(0)+chr(0)+chr(0)+chr(0)+
         chr(0)+chr(0);
 exit;
end;

list:=fmySupernodes.locklist;
  sup:=list[list.count-1];
  result:=int_2_dword_string(sup^.Fip)+
          int_2_word_string(sup^.Fport);
fmySupernodes.Unlocklist;
end;

function mysupernodes_serialize:string;
var
list:tlist;
i:integer;
sup:precord_mysupernode;
begin
result:='';
if fmysuplistcount=0 then exit;

list:=fmySupernodes.locklist;

  for i:=list.count-1 downto 0 do begin
   sup:=list[i];
   result:=result+int_2_dword_string(sup^.Fip)+
                 int_2_word_string(sup^.FPort);
   if length(result)=30 then break;
  end;

fmySupernodes.Unlocklist;
end;

procedure mysupernodes_remove(ip:cardinal);
var
list:tlist;
i:integer;
sup:precord_mysupernode;
begin
list:=fmySupernodes.locklist;

 for i:=0 to list.count-1 do begin
   sup:=list[i];
   if sup^.fip=ip then begin
    list.delete(i);
    FreeMem(sup,sizeof(record_mysupernode));
    fmysuplistcount:=list.count;
    break;
   end;
 end;

fmySupernodes.Unlocklist;

end;

procedure mysupernodes_add(ip:cardinal; port:word; resultid:integer);
var
list:tlist;
i:integer;
sup:precord_mysupernode;
begin
list:=fmySupernodes.locklist;

 for i:=0 to list.count-1 do begin
   sup:=list[i];
   if sup^.fip=ip then begin
    sup^.fresultid:=resultid;   //update resultid
    fmySupernodes.unlocklist;
    exit;
   end;
 end;

 sup:=AllocMem(sizeof(record_mysupernode));
  with sup^ do begin
   fip:=ip;
   fport:=port;
   fresultId:=resultid;
   fLastPing:=0;
   FlastPong:=0;
  end;
 list.add(sup);
 fmysuplistcount:=list.count;

fmySupernodes.unlocklist;

end;



//keep NAT Transfer session alive (called by thread_upload once every 15 seconds)
procedure mySupernodes_ping(Tick:cardinal; USocket:Hsocket);
var
list:Tlist;
i:integer;
sup:precord_mysupernode;
resid:word;
RemoteSin:TVarSin;
buffer:array[0..2] of byte;
begin
if USocket=INVALID_SOCKET then exit;
if fmysuplistcount=0 then exit;

list:=fmySupernodes.locklist;
try

 for i:=0 to list.count-1 do begin
  sup:=list[i];
  if sup^.fresultId=-1 then continue;  
  if tick-sup^.FLastPing>UDPTRANSFER_PINGTIMEOUT then begin
   resid:=sup^.fresultID; // copy to word value

     buffer[0]:=CMD_UDPTRANSFER_PING;
     move(resId,buffer[1],2);
     
     FillChar(RemoteSin, Sizeof(RemoteSin), 0);
     RemoteSin.sin_family:=AF_INET;
     RemoteSin.sin_port:=synsock.htons(sup^.Fport);
     RemoteSin.sin_addr.s_addr:=sup^.Fip;
     synsock.SendTo(Usocket,buffer,3,0,@RemoteSin,sizeof(RemoteSin));

     sup^.FLastPing:=tick;

  end;

 end;

except
end;
fmySupernodes.unlocklist;
end;


// pong arrived from remove thread (in thread_upload)
procedure mySupernodes_pong(Tick:cardinal; ip:cardinal; port:word);
var
list:tlist;
i:integer;
sup:precord_mysupernode;
begin
if fmysuplistcount=0 then exit;

list:=fmySupernodes.locklist;

 for i:=0 to list.count-1 do begin
  sup:=list[i];

  if sup^.Fip=ip then
   if sup^.FPort=port then begin
    sup^.FLastPong:=Tick;
    break;
   end;
 end;

fmySupernodes.unlocklist;
end;


end.
