unit Ws.ServiceImpl;

interface

uses
  Common.Logging,
  System.SysUtils,
  XData.Server.Module,
  XData.Service.Common,
  Sparkle.WebSocket,
  System.JSON,
  Sparkle.Sys.Timer,
  Sparkle.HttpServer.Context,
  Ws.Service,
  BaseRequest,
  LoginRequest,
  Pkg.Json.DTO,
  Generics.Collections;

type
  [ServiceImplementation]
  TWebSocketService = class(TInterfacedObject, IWebSocketService)
  private
    procedure WebSockerConnectionHandler;
    function Health: string;
    procedure ProcessJsonMessage(const AMsg: string);
  end;

implementation

{ TWebSocketService }

procedure TWebSocketService.WebSockerConnectionHandler;
var
  Upgrader: IWebSocketUpgrader;
  WebSocket: IWebSocket;
  Timer: TSparkleTimer;
  Msg: IWebSocketMessage;
  JSONObject: TJSONObject;
  MsgStr: string;
begin
  Logger.Log(1, 'websocket begin-------');

  // Check if the client sent an websocket request, if yes the IWebSocketUpgrader interface will be available.
  Upgrader := THttpServerContext.Current.Item<IWebSocketUpgrader>;
  if Upgrader = nil then
  begin
    TXDataOperationContext.Current.Handler.SetStatusCode(400);
    Exit;
  end;
   Logger.Log(1, 'websocket upgrading-------');

  // Upgrade to websocket
  WebSocket := Upgrader.Upgrade;

  Logger.Log(1, 'websocket upgraded-------');
  // Send a message to client every 2 seconds
  Timer := TSparkleTimer.Create(
    procedure(Value: TObject)
    begin
      if WebSocket.State = TWebSocketState.Open then
        WebSocket.Send('Server message at ' + TimeToStr(Time));
    end,
    nil, 2000, TTimerType.Periodic
  );

  // Receive messages
  try
    while WebSocket.State = TWebSocketState.Open do
    begin
      Msg := WebSocket.Receive;
      case Msg.MessageType of
        TWebSocketMessageType.Text:
        begin
          MsgStr := TEncoding.Default.GetString(Msg.Data);
          Logger.Log(1, 'Web Socket Message: ' + MsgStr);
        end;
        //TODO: HERE we need to create a map to store connections for later
        //       and implement protocol to communicate
        //  store websocket obj, handle another layer of compression/encryption,
        //  probably store the websocket connection in a map with device id or some other uuid
        //self.ProcessJsonMessage(MsgStr);
        //TWebSocketMessageType.Close:
        //  WebSocket.SendClose(WebSocketStatusCodes.NormalClosure);
      end;
    end;
  finally
    Timer.Free;
  end;
end;

function TWebSocketService.Health: string;
begin
  Result := 'ok';
end;

procedure TWebSocketService.ProcessJsonMessage(const AMsg: string);
var
  MsgDTO:  TBaseRequest;
  request: TRequest;
  rawJsObj: TJSONArray;
begin
  MsgDTO := nil;
  try
    rawJsObj:=TJSONObject.ParseJSONValue(AMsg) as TJSONArray;
    MsgDTO.FromJson(AMsg);

    for request in MsgDTO.Request do
    begin
      // Process each Request object
      Logger.Log(1, 'Action: ' + request.RequestId);
      if  request.RequestId = 'doLogin' then
      begin
        Logger.Log(1, 'Action: ' + request.RequestId);
        //TODO: implement instantiation and handling of each request type
        //  not sure if upcasting works in delphi here but probably not so we do this:
        //     rawJsObj:=TJSONObject.ParseJSONValue(AMsg) as TJSONArray
        //          and then ---->
        //     loginRequest.FromJson(rawJsObj.Items[0].ToJSON());
        var
          loginRequest:TLoginRequest;
        begin
          loginRequest.FromJson(rawJsObj.Items[0].ToJSON());
          //loginRequest.LoginInfo.Shift
          //TODO: it would be really great to have the JDO convert to database sql
        end;
      end;


    end;
    // ... further logic
  finally
    MsgDTO.Free;
  end;
end;


initialization
  RegisterServiceType(TWebSocketService);

end.
