package body Task_Pack_8 is

    task body Buffer_Type is
        type Buffer_Range is range 0 .. 20;
        subtype Buffer_Index is Buffer_Range range 1 .. Buffer_Range'Last;
        Buffer : array (Buffer_Index) of Integer;
        Head, Tail : Buffer_Index := Buffer_Index'First;
        Buffer_Count : Buffer_Range := 0;

    begin
        loop
            select
                when Buffer_Count < Buffer_Range'Last =>
                    accept Take_Item (Item : in Integer) do
                        Buffer (Head) := Item;
                        Head := (Head mod Buffer_Range'Last) + 1;
                        Buffer_Count := Buffer_Count + 1;
                    end Take_Item;
            or
                when Buffer_Count > 0 =>
                    accept Give_Item (Item : out Integer) do
                        Item := Buffer (Tail);
                        Tail := (Tail mod Buffer_Range'Last) + 1;
                        Buffer_Count := Buffer_Count - 1;
                    end Give_Item;
            end select;
        end loop;
    end Buffer_Type;

    task body Producer_Type is
        Number_To_Produce : Natural;
    begin
        loop
            accept Produce (Count : in Natural) do
                Number_To_Produce := Count;
            end Produce;
            for I in 1 .. Number_To_Produce loop
                Buffer_Task.Take_Item (I);
            end loop;
        end loop;
    end Producer_Type;

    task body Consumer_Type is
        Number_To_Consume : Natural;
        Count_Check : Natural := 0;
        Item : Integer;
    begin
        loop
            accept Consume (Count : in Natural) do
                Number_To_Consume := Count;
            end Consume;
            for I in 1 .. Number_To_Consume loop
                Buffer_Task.Give_Item (Item);
                Count_Check := Count_Check + 1;
            end loop;
            accept Wait_For_Consumer_Completion (Check : out Natural) do
                Check := Count_Check;
                Count_Check := 0; -- ready for next use
            end Wait_For_Consumer_Completion;
        end loop;
    end Consumer_Type;

end Task_Pack_8;