unit uUDP;

interface

uses
  Windows, SysUtils,
  IdUDPServer, IdBaseComponent, IdComponent, IdUDPBase, IdGlobal,
  IdSocketHandle,
  uPacket, uGT, uGlobalDefs;

type

  cUDP = class(TIdUDPServer) // based on indy UDP transciever
  private
    mPortNo: word;
    mServerName, mIamServerName, mSeekServerName: string;
    mServerIP: string;
    mLog: aLogProc;
    // mSrcIP : string;
    // mEr : int;
    mBroadcast: boolean;
    // procedure  Log( er : int; const s : string );
{$IFDEF VER240 }      // XE3 has different parameter type !
    procedure UDPReadEvent(AThread: TIdUDPListenerThread; AData: array of Byte;
      ABinding: TIdSocketHandle);
{$ELSE }
    procedure UDPReadEvent(AThread: TIdUDPListenerThread; AData: TIdBytes;
      ABinding: TIdSocketHandle);
{$ENDIF }
    procedure SetIamServerName(const name: string);
  public
    oInQ: cLockedQueue;
    constructor Create(port: int; handler: aInputHandler; log: aLogProc = nil);
    // constructor  Create( port : int; wh : HWND; winMesgNo : int; log : aLogProc = nil );
    destructor Destroy; override;

    procedure RequestServer(const ServerName: string);
    procedure AnnounceServer;
    procedure Send(const pkt: aPacket); overload;
    procedure Send(const str: string;
      PktType: aPktType = pkPassThrough); overload;
    procedure FreeMesg(const pm: apPacket);

    property PortNo: word read mPortNo write mPortNo;
    property ServerIP: string read mServerIP;
    property ServerName: string read mServerName;
    property IamServerName: string read mIamServerName write SetIamServerName;
  end;

implementation

uses IdStack;

// ________________________________ READER THREAD _______________________________

{$IFDEF VER240 }

procedure cUDP.UDPReadEvent(AThread: TIdUDPListenerThread; AData: array of Byte;
  ABinding: TIdSocketHandle);
{$ELSE }

procedure cUDP.UDPReadEvent(AThread: TIdUDPListenerThread; AData: TIdBytes;
  ABinding: TIdSocketHandle);
{$ENDIF }
var
  pp: apPacket;
  s: string;
begin
  if (Length(AData) > SizeOfPktHdr) and
    (GStack.LocalAddresses.IndexOf(ABinding.PeerIP) < 0) then
  begin
    // long enough                                            not my echo
    pp := NewPkt(AData);
    if pp.pkFlags = MagicFlags then
    begin
      if pp.pkType = pkRequestServerIP then
      begin
        s := pp.ToString();
        // Log( stUDPRequest, 'UDP Request ' + s );
        if s = mIamServerName then
          AnnounceServer;
      end
      else if pp.pkType = pkAnnounceServer then
      begin
        s := pp.ToString();
        if mSeekServerName = s then
        begin
          mServerName := s;
          mServerIP := ABinding.PeerIP;
          // Log( stUDPReply, 'UDP Relevant Announcement ' + s );
        end
        // else  Log( stUDPReply, 'UDP Announcement ' + s );
      end;
      oInQ.Post(pp, alUDP);
    end;
  end;
end;

// ______________________________ MAIN THREAD ___________________________________

constructor cUDP.Create(port: int; handler: aInputHandler; log: aLogProc = nil);

begin
  inherited Create;
  oInQ := cLockedQueue.Create(handler);

  mPortNo := port;
  DefaultPort := mPortNo;
  mBroadcast := true;
  mLog := log;

  OnUDPRead := UDPReadEvent;
  BroadcastEnabled := mBroadcast;
  Active := true;
end;

destructor cUDP.Destroy;

begin
  Active := false;
  FreeAndNil(oInQ);
  inherited Destroy;
end;

{ procedure  cUDP.Log( er : int; const s : string );

  begin
  if er > mEr then  mEr := er;
  if Assigned( mLog ) then  mLog( er, s );
  end; }

procedure cUDP.Send(const pkt: aPacket);

begin
  if mBroadcast then
    Broadcast(pkt.ToBytes(), mPortNo);
end;

procedure cUDP.Send(const str: string; PktType: aPktType = pkPassThrough);

var
  pp: apPacket;
begin
  pp := NewPkt(str);
  pp.pkType := PktType;
  Send(pp^);
  pp.FreePkt;
end;

procedure cUDP.RequestServer(const ServerName: string);

begin
  Send(ServerName, pkRequestServerIP);
  mSeekServerName := ServerName;
end;

procedure cUDP.AnnounceServer;

begin
  Broadcast(TIdBytes(PktBytes(mIamServerName, pkAnnounceServer)), PortNo);
end;

procedure cUDP.SetIamServerName(const name: string);

begin
  mIamServerName := name;
  // Log( stUDPServer, 'UDP server ' + name );
  AnnounceServer;
end;

procedure cUDP.FreeMesg(const pm: apPacket);

begin
  if pm <> nil then
  begin
    pm.FreePkt;
  end;
end;

end.
