unit webideclient;

{$mode objfpc}

interface

uses
  Classes, SysUtils, js, web;

type
  TIDEClient = Class;

  TIDEResponseHandler = Procedure (aCode : Integer; aCodeText : String; aPayload : TJSObject) of object;

  { TIDERequest }

  TIDERequest = Class(TObject)
  Private
    FXHR : TJSXMLHttpRequest;
    FOnResponse: TIDEResponseHandler;
    Procedure ProcessResponse;
    procedure DoStateChange;
  Public
    Constructor Create(aMethod, aURl: String; aPayLoad: TJSObject; aOnResponse: TIDEResponseHandler);
  end;

  { TIDEClient }
  TCommandEvent = Procedure (Sender : TObject; aCommands : TJSArray) of object;
  TActionEvent  = Procedure (Sender : TObject; aID : Int64; aName : String; aPayload : TJSObject) of object;

  TIDEClient = Class(TComponent)
  private
    FActionID : NativeInt;
    FOnActionResponse: TActionEvent;
    FPollID : NativeInt;
    FCommandPollInterval : Integer;
    FClientID: NativeInt;
    FIDEURL: String;
    FOnCommands: TCommandEvent;
    FLastPoll : TIDERequest;
    FStartPolling : Boolean;
    procedure DoCommandPoll;
    procedure OnActionSent(aCode: Integer; aCodeText: String; aPayload: TJSObject);
    procedure OnClientRegistered(aCode: Integer; aCodeText: String; aPayload: TJSObject);
    procedure OnCommandsReceived(aCode: Integer; aCodeText: String; aPayload: TJSObject);
  Public
    Constructor Create(aOwner : TComponent); override;
    Procedure RegisterClient;
    Procedure UnRegisterClient;
    Procedure StartCommandPolling;
    Procedure StopCommandPolling;
    Function GetNextID : NativeInt;
    procedure SendAction(Const aName : String; aPayLoad : TJSObject);
    Property IDEURL : String read FIDEURL Write FIDEURL;
    Property ClientID : Int64 read FClientID Write FClientID;
    Property CommandPollInterval : Integer Read FCommandPollInterval Write FCommandPollInterval;
    Property OnCommands : TCommandEvent Read FOnCommands Write FOnCommands;
    Property OnActionResponse : TActionEvent Read FOnActionResponse Write FOnActionResponse;
  end;

implementation

{ TIDEClient }


procedure TIDEClient.DoCommandPoll;

begin
  if Not Assigned(FLastPoll) then
    FLastPoll:=TIDERequest.Create('Get',IDEURL+'Command/'+IntToStr(ClientID)+'/',Nil,@OnCommandsReceived);
end;

procedure TIDEClient.OnActionSent(aCode: Integer; aCodeText: String; aPayload: TJSObject);

Var
  aID : NativeInt;
  aName : string;
  aActionPayload : TJSObject;

begin
  if (aCode div 100)=2 then
    begin
    aID:=NativeInt(aPayLoad['id']);
    aName:=String(aPayLoad['name']);
    aActionPayLoad:=TJSObject(aPayLoad['payload']);
    If Assigned(OnActionResponse) then
      OnActionResponse(Self,aID,aName,aActionPayload);
    end;
end;

procedure TIDEClient.OnClientRegistered(aCode: Integer; aCodeText: String; aPayload: TJSObject);

begin
  if (aCode div 100)=2 then
    begin
    FClientID:=NativeInt(aPayload['id']);
    if FStartPolling then
      StartCommandPolling;
    end
  else
    FClientID:=0;
end;

procedure TIDEClient.OnCommandsReceived(aCode: Integer; aCodeText: String; aPayload: TJSObject);

Var
  A: TJSArray;

begin
  FLastPoll:=Nil;
  if (aCode div 100)<>2 then
    exit;
  if Assigned(aPayload) and isArray(aPayload['commands']) then
    begin
    A:=TJSArray(aPayload['commands']);
    if (A.Length>0) then
      OnCommands(Self,A);
    end;
end;

constructor TIDEClient.Create(aOwner: TComponent);
begin
  Inherited;
  FLastPoll:=Nil;
  IDEURL:='http://'+Window.location.hostname+':'+Window.location.port+'/IDE/';
end;

procedure TIDEClient.RegisterClient;

Var
  P : TJSObject;
  Req : TIDERequest;

begin
  P:=New(['url',window.locationString]);
  req:=TIDERequest.Create('POST',IDEURL+'Client',P,@OnClientRegistered);
end;

procedure TIDEClient.UnRegisterClient;

Var
  Req : TIDERequest;

begin
  Req:=TIDERequest.Create('DELETE',IDEURL+'Client/'+IntToStr(ClientID),Nil,@OnClientRegistered);
end;

procedure TIDEClient.StartCommandPolling;
begin
  if ClientID<>0 then
    FPollID:=Window.setInterval(@DoCommandPoll,FCommandPollInterval)
  else
    FStartPolling:=True;
end;

procedure TIDEClient.StopCommandPolling;
begin
  FStartPolling:=False;
  if (FPollID>0) then
    Window.clearInterval(FPollID);
end;

function TIDEClient.GetNextID: NativeInt;
begin
  Inc(FActionID);
  Result:=FActionID;
end;

procedure TIDEClient.SendAction(const aName: String; aPayLoad: TJSObject);

Var
  aAction : TJSObject;
  aID : NativeInt;
  req: TIDERequest;

begin
  aID:=GetNextID;
  aAction:=New(['id',aID,
                'name',aName,
                'payload',aPayLoad]);
  req:=TIDERequest.Create('POST',IDEURL+'Action/'+IntToStr(ClientID)+'/'+IntToStr(aID),aAction,@OnActionSent);
end;

{ TIDERequest }

procedure TIDERequest.ProcessResponse;

var
  P : TJSObject;

begin
  if ((FXHR.Status div 100)=2) and (FXHR.ResponseHeaders['Content-Type']='application/json') then
    P:=TJSJSON.parseObject(FXHR.responseText)
  else
    P:=Nil;
  if Assigned(FOnResponse) then
    FOnResponse(FXHR.Status,FXHR.StatusText,P);
end;

procedure TIDERequest.DoStateChange;
begin
  case FXHR.readyState of
    TJSXMLHttpRequest.DONE :
      begin
      if Assigned(FOnResponse) then
        ProcessResponse;
      Free;
      end;
  end;
end;

constructor TIDERequest.Create(aMethod, aURl: String; aPayLoad: TJSObject; aOnResponse: TIDEResponseHandler);

Var
  S : String;

begin
  FOnResponse:=aOnResponse;
  FXHR:=TJSXMLHttpRequest.New;
  FXHR.open(aMethod,aURL);
  if assigned(aPayload) then
    S:=TJSJSON.Stringify(aPayload)
  else
    S:='';
  FXHR.setRequestHeader('Content-Type','application/json');
  FXHR.onreadystatechange:=@DoStateChange;
  FXHR.send(S);
end;


end.

