{
  Amiga style simpleipc.inc
}

{$DEFINE OSNEEDIPCINITDONE}

uses
  Exec, dos;

ResourceString
  SErrMsgPortExists = 'MsgPort already exists: %s';

const
  PORTNAMESTART = 'fpc_';

Var
  MsgPorts: Classes.TList;

procedure AddMsgPort(AMsgPort: PMsgPort);
begin
  if Assigned(MsgPorts) then
  begin
    MsgPorts.Add(AMsgPort);
  end;
end;

procedure RemoveMsgPort(AMsgPort: PMsgPort);
var
  Idx: Integer;
begin
  if Assigned(MsgPorts) then
  begin
    Idx := MsgPorts.IndexOf(AMsgPort);
    if Idx >= 0 then
    begin
      MsgPorts.Delete(Idx);
      if Assigned(AMsgPort^.mp_Node.ln_Name) and (string(AMsgPort^.mp_Node.ln_Name) <> '') and Assigned(FindPort(AMsgPort^.mp_Node.ln_Name)) then
        RemPort(AMsgPort);
      DeleteMsgPort(AMsgPort);
    end;  
  end;
end;


procedure IPCInit;
begin
  MsgPorts := Classes.TList.Create;
end;

procedure IPCDone;
var
  I: integer;
begin
  try
    for i := 0 to MsgPorts.Count - 1 do
        RemoveMsgPort(PMsgPort(MsgPorts[i]));
    finally  
      FreeAndNil(MsgPorts);  
    end;  
end;


Type
  TAmigaClientComm = Class(TIPCClientComm)
  Private
    FMsgPort: PMsgPort;
    FPortName: String;
  Public
    Constructor Create(AOwner: TSimpleIPCClient); override;
    Procedure Connect; override;
    Procedure Disconnect; override;
    Procedure SendMessage(MsgType : TMessageType; AStream : TStream); override;
    Function  ServerRunning : Boolean; override;
  end;

  TAmigaServerComm = Class(TIPCServerComm)
  Private
    FMsgPort: PMsgPort;
    FPortName: String;
    MsgBody: PMessage;
  Public
    Constructor Create(AOwner: TSimpleIPCServer); override;
    destructor Destroy; override;
    Procedure StartServer; override;
    Procedure StopServer; override;
    Function  PeekMessage(TimeOut : Integer) : Boolean; override;
    Procedure ReadMessage ; override;
    Function GetInstanceID : String;override;
  end;
  
// ####### CLIENT  
  
function SafePutToPort(Msg: PMessage; Portname: string): Boolean;
 var
   Port: PMsgPort;
   PName: PChar;
 begin
   Result := False;
   PName := PChar(Portname + #0);
   Forbid();
   Port := FindPort(PName);
   if Assigned(Port) then
   begin
     if Assigned(Msg) then
       PutMsg(Port, Msg);
     Result := True;
   end;
   Permit();
 end;

Constructor TAmigaClientComm.Create(AOwner: TSimpleIPCClient);
begin
  inherited;
end;

Procedure TAmigaClientComm.Connect;
begin
 
end;

Procedure TAmigaClientComm.Disconnect;
begin
  
end;

Procedure TAmigaClientComm.SendMessage(MsgType : TMessageType; AStream : TStream);
var
  Size: Integer;
  FullSize: Integer;
  Memory: Pointer;
  Temp: PByte;
  MsgHead: Exec.PMessage;
  MP: PMsgPort;
  PortName: string;
begin
  Size := AStream.Size - AStream.Position;
  FullSize := Size + SizeOf(TMessageType) + Sizeof(Exec.TMessage);
  PortName := PORTNAMESTART + Owner.ServerID;
  Memory := System.AllocMem(FullSize);
  MP := CreateMsgPort;
  try
    MsgHead := Memory;
    MsgHead^.mn_ReplyPort := MP;
    MsgHead^.mn_Length := Size;
    Temp := Memory;
    Inc(Temp, SizeOf(Exec.TMessage));
    Move(MsgType, Temp^, SizeOf(TMessageType));
    Inc(Temp, SizeOf(TMessageType));
    AStream.Read(Temp^, Size);
    if SafePutToPort(MsgHead, PortName) then
      WaitPort(MP);
  finally
    System.FreeMem(Memory);
    DeleteMsgPort(MP);
  end;
end;

Function TAmigaClientComm.ServerRunning : Boolean;
begin
  Result := SafePutToPort(nil, PORTNAMESTART + Owner.ServerID); 
end;

// ###### SERVER

Constructor TAmigaServerComm.Create(AOwner: TSimpleIPCServer);
begin
  inherited;
  FMsgPort := CreateMsgPort;
  AddMsgPort(FMsgPort);
  MsgBody := nil;
end;

destructor TAmigaServerComm.Destroy;
begin
  if Assigned(MsgBody) then
    System.FreeMem(MsgBody);
  RemoveMsgPort(FMsgPort);
  inherited;
end;

Procedure TAmigaServerComm.StartServer;
begin
  FPortName := PORTNAMESTART + Owner.ServerID + #0;
  if Assigned(FindPort(PChar(FPortName))) then
  begin
    DoError(SErrMsgPortExists,[FPortName]);
    Exit;
  end;
  FMsgPort^.mp_Node.ln_Name := PChar(FPortName);
  FMsgPort^.mp_Node.ln_Pri := 0;
  AddPort(FMsgPort);
  if Assigned(MsgBody) then
    System.FreeMem(MsgBody); 
  MsgBody := nil;
end;

Procedure TAmigaServerComm.StopServer;
begin
  RemPort(FMsgPort);
  if Assigned(MsgBody) then
    System.FreeMem(MsgBody);
  MsgBody := nil;
end;

Function TAmigaServerComm.PeekMessage(TimeOut : Integer) : Boolean;
var
  Msg: PMessage;
  Temp: PByte;
  StartTime: Int64;
begin
  StartTime := GetMsCount;
  Result := False;
  if TimeOut < 0 then
    TimeOut := MaxInt;
  repeat
    Msg := GetMsg(FMsgPort);
    if Assigned(Msg) then
    begin
      Result := True;
      Temp := PByte(Msg);
      Inc(Temp, SizeOf(Exec.TMessage));
      if Assigned(MsgBody) then
        System.FreeMem(MsgBody);
      MsgBody := System.AllocMem(SizeOf(Exec.TMessage) + SizeOf(TMessageType) + Msg^.mn_Length);
      Move(Msg^, MsgBody^, SizeOf(Exec.TMessage) + SizeOf(TMessageType) + Msg^.mn_Length);
      ReplyMsg(Msg);
      break;
    end;
    Sleep(25);
  until GetMsCount - StartTime >= TimeOut;
end;

Procedure TAmigaServerComm.ReadMessage;
var
  Temp: PByte;
  MsgType: TMessageType;
  Msg: TIPCServerMsg;
begin
  if Assigned(MsgBody) then
  begin
    Temp := Pointer(MsgBody);
    Inc(Temp, SizeOf(Exec.TMessage));
    MsgType := 0;
    Move(Temp^, MsgType, SizeOf(TMessageType));
    Inc(Temp, SizeOf(TMessageType));

    Msg := TIPCServerMsg.Create;
    try
      Msg.MsgType := MsgType;
      Msg.Stream.WriteBuffer(Temp^, MsgBody^.mn_Length);
    except
      FreeAndNil(Msg);
      raise;
    end;
    PushMessage(Msg);

    System.FreeMem(MsgBody);
    MsgBody := nil;
  end;
end;

Function TAmigaServerComm.GetInstanceID: String;
begin
  Result := HexStr(FindTask(nil));
end;

// ###### Register

Function TSimpleIPCServer.CommClass : TIPCServerCommClass;
begin
  Result:=TAmigaServerComm;
end;

function TSimpleIPCClient.CommClass: TIPCClientCommClass;
begin
  Result:=TAmigaClientComm;
end;

