unit BQueue;

interface

uses
	LibV;

type
    TBlockQueue		= object
    private
		MaxSize		: u32_t;
        MaxReached	: Boolean;
    	Fifo		: fifo_t;
    public
    	constructor Create(BlockSize:integer);
        destructor  Destroy;
        procedure	Free;
        procedure	SetMaxSize(NewMaxSize:integer);
        procedure	Clear;

        function	Count:integer;
        function	Push(var Source):Boolean;
        function	Pop(var Dest):Boolean;
        function	GetBlock(var Dest;DistanceFromOldest:integer):Boolean;
        function	SetBlock(var Source;DistanceFromOldest:integer):Boolean;
    end;

implementation

constructor TBlockQueue.Create(BlockSize:integer);
begin
	fifoInit(Fifo,BlockSize);
    MaxSize:=0;			// No limit
end;

destructor  TBlockQueue.Destroy;
begin
	fifoClear(Fifo);
end;

procedure	TBlockQueue.Free;
begin
	Destroy;
end;

procedure	TBlockQueue.SetMaxSize(NewMaxSize:integer);
begin
	MaxSize:=NewMaxSize;
end;

procedure	TBlockQueue.Clear;
begin
	fifoClear(Fifo);
    MaxReached:=False;
end;

function	TBlockQueue.Count:integer;
begin
	Result:=fifoCount(Fifo);
end;

(*
**	TODO: convert to use two different private Push methods
*)
function	TBlockQueue.Push(var Source):Boolean;
begin
	if MaxReached then
    begin
    	Fifo.tail := Fifo.tail + Fifo.element_size;
        if Fifo.tail = Fifo.limit then
        	Fifo.tail := Fifo.buf;
		memCopy(Fifo.head,@Source,Fifo.element_size);
		Fifo.head:=Fifo.head+Fifo.element_size;
		if Fifo.head = Fifo.limit then
			Fifo.head := Fifo.buf;
    	Result:=False;
    end
    else
    begin
    	fifoPush(Fifo,@Source);
		MaxReached:=(MaxSize>0) and (MaxSize = fifoCount(Fifo));
        Result:=True;
    end;
end;

function	TBlockQueue.Pop(var Dest):Boolean;
begin
	Result:=fifoPop(Fifo,@Dest);
    MaxReached:=(MaxSize>0) and (MaxSize = fifoCount(Fifo));
end;

function	TBlockQueue.GetBlock(var Dest;DistanceFromOldest:u32_t):Boolean;
var
	qpos	: char_p;
    len		: u32_t;
begin
    len := Fifo.size - Fifo.avail;
	if DistanceFromOldest>=len then
    begin
		Result:=False;
    	Exit;
    end;
    qpos:=Fifo.tail+DistanceFromOldest*Fifo.element_size;
    if qpos >= Fifo.limit then
    	qpos:=Fifo.buf+(qpos-Fifo.limit);
    memCopy(@Dest,qpos,Fifo.element_size);
    Result:=True;
end;

function	TBlockQueue.SetBlock(var Source;DistanceFromOldest:u32_t):Boolean;
var
	qpos	: char_p;
    len		: u32_t;
begin
    len := Fifo.size - Fifo.avail;
	if DistanceFromOldest>=len then
    begin
		Result:=False;
    	Exit;
    end;
    qpos:=Fifo.tail+DistanceFromOldest*Fifo.element_size;
    if qpos >= Fifo.limit then
    	qpos:=Fifo.buf+(qpos-Fifo.limit);
    memCopy(qpos,@Source,Fifo.element_size);
    Result:=True;
end;

end.

