{$MODE OBJFPC}
{$MODESWITCH ALLOWINLINE}
unit dtaskque;

interface

uses
  dobjects;

type
ITask = object(IInterface)
  procedure Execute; virtual; abstract;
end;

PTaskQue = ^TTaskQue;
TTaskQue = object(TObject)
public const
  READY = 0;
  RUNNING = 1;
  DEAD = 2;
private
  FState: LongInt;
  FProgress: LongInt;
  FTasks: array of ITask;
  FThread: TThreadId;
  function GetCount: LongInt;
  function GetState: LongInt;
public
  constructor Init;
  destructor Done; virtual;
public // Only in READY state
  procedure AddTask(Task: ITask); inline;
  procedure AddTasks(const Tasks: array of ITask);
  function Run(Async: Boolean = True): Boolean;
  procedure DropTasks;
  procedure WaitAll(TimoutMs: LongInt = 0); inline;
  property Progress: LongInt read FProgress;
  property Count: LongInt read GetCount;
  property State: LongInt read GetState;
end;

implementation

function TTaskQue.GetCount: LongInt;
begin
  Result := Length(FTasks);
end;

function TTaskQue.GetState: LongInt;
begin
  if FState = RUNNING then begin
    // check if thread is dead
  end;
  Result := FState;
end;

constructor TTaskQue.Init;
begin
  FProgress := 0;
  FState := READY;
end;

destructor TTaskQue.Done;
begin
  WaitAll;
  SetLength(FTasks, 0);
end;

procedure TTaskQue.AddTask(Task: ITask);
begin
  if FState <> RUNNING then begin
    SetLength(FTasks, Length(FTasks) + 1);
    FTasks[High(FTasks)] := Task;
  end;
end;

procedure TTaskQue.AddTasks(const Tasks: array of ITask);
var
  I: LongInt;
begin
  for I := Low(Tasks) to High(Tasks) do
    AddTask(Tasks[I]);
end;

function RunTaskQue(Que: Pointer): PtrInt;
begin
  with PTaskQue(Que)^ do begin
    while FProgress < Length(FTasks) do begin
      FTasks[FProgress].Execute;
      Inc(FProgress);
    end;
    DropTasks;
    FProgress := 0;
    FState := TTaskQue.READY;
    Result := 0;
  end;
end;

function TTaskQue.Run(Async: Boolean = True): Boolean;
begin
  if FState = RUNNING then
    Exit(True);
  if Count = 0 then
    Exit(True);
  FState := RUNNING;
  if Async then begin
    FThread := BeginThread(@RunTaskQue, @Self);
    Exit(True);
  end else begin
    RunTaskQue(@Self);
    Exit(True);
  end;
end;

procedure TTaskQue.DropTasks;
begin
  if FState <> RUNNING then begin
    SetLength(FTasks, 0);
    FProgress := 0;
  end;
end;

procedure TTaskQue.WaitAll(TimoutMs: LongInt);
begin
  WaitForThreadTerminate(FThread, 0);
end;

end.
