-- SMALL TASK Environment for task queue exercise
-- Currently number 5 in the queue module
-- OCT 31, 1983
-- Dec 15, revised to inclkude initializations needed in CONSUMER
-- this one includes a regular queue package
-- REVISED 10-1-84 NO INPUT FOR PERFORMANCE SUITE
-- Author Bob Laddaga


Package QUEUE_PKG is  --a package implementing circular queues for 
		      -- CHARACTER items.

  -- MAX_COUNT is the  maximum number of items the queue can hold.
    MAX_COUNT : constant := 10;  

  type QUEUE is private;


    function  EMPTY_Q ( Q : QUEUE ) return BOOLEAN;
    --  Returns TRUE if Q is empty, FALSE otherwise.

    function  FULL_Q ( Q : QUEUE ) return BOOLEAN;
    -- Returns TRUE if QUEUE is full, FALSE otherwise.


    procedure ENQUEUE ( Q : in out QUEUE; IN_ITEM : in CHARACTER );
    -- Adds IN_ITEM to the front of Q.
    -- Does nothing (just returns) if Q is full.

    procedure DEQUEUE ( Q : in out QUEUE; OUT_ITEM : out CHARACTER );
    -- Removes an item from the rear of Q, returning it in OUT_ITEM.
    -- Does nothing (just returns) if Q is empty.

private

  -- MAX_SIZE is the upper bound of the array holding the queue.
    MAX_SIZE  : constant := MAX_COUNT - 1;

  -- MODNUM is the type of the index of the array holding the queue.
    subtype MODNUM is INTEGER range 0 .. MAX_SIZE;

  -- COUNTNUM is the type of the counter for items in the array
    subtype COUNTNUM is INTEGER range 0 .. MAX_COUNT;

    type Q_ARR is array ( 0 .. MAX_SIZE ) of CHARACTER;

    type QUEUE is
      record
	FRONT : MODNUM := 1;
	REAR  : MODNUM := 0;
	COUNT : COUNTNUM := 0;
	ITEM : Q_ARR;
    end record;



end QUEUE_PKG;





with QUEUE_PKG, TEXT_IO; use QUEUE_PKG, TEXT_IO;
procedure TASK_EXERCISE is 

  NUM_PRODUCERS : INTEGER := 2;

  task BUFFER is
    entry IN_CHAR( IN_ITEM : in CHARACTER );
    entry OUT_CHAR( OUT_ITEM : out CHARACTER );
    entry STOP;
  end BUFFER;

  task INPUT_SERVER is
    entry GET_STRING( STR : in out STRING; LAST : out INTEGER );
    entry STOP;
  end INPUT_SERVER;

  task FIRST_PRODUCER;

  task SECOND_PRODUCER;

  task CONSUMER;


  task body INPUT_SERVER is
    begin
    loop
      select
        accept GET_STRING ( STR : in out STRING; LAST : out INTEGER )do
          PUT( "Type a string of 10 characters: " );
--          GET_LINE( STR, LAST );
          STR := "ABCDEFGHIJ";
          LAST := 10;
        end;
      or
        accept STOP; exit;
      or
         terminate;
      end select;
    end loop;
  end INPUT_SERVER;
    

  task body FIRST_PRODUCER is 
    STR1 : STRING ( 1 .. 10 ) := "          ";
    LAST1 : INTEGER;

    begin -- FIRST_PRODUCER
    INPUT_SERVER.GET_STRING( STR1, LAST1 );
    for I in 1 .. LAST1 loop
      BUFFER.IN_CHAR( STR1( I ) );
    end loop;
    BUFFER.IN_CHAR( ASCII.EOT );
  end FIRST_PRODUCER;

 
  task body SECOND_PRODUCER is
    STR2 : STRING ( 1 .. 10 ) := "          ";
    LAST2 : INTEGER;

    begin -- SECOND_PRODUCER
    INPUT_SERVER.GET_STRING( STR2, LAST2 );
    for I in 1 .. LAST2 loop
      BUFFER.IN_CHAR( STR2( I ) );
    end loop;
    BUFFER.IN_CHAR( ASCII.EOT );
  end SECOND_PRODUCER;


  task body CONSUMER is
    CHAR : CHARACTER := ' ';
    NUM_ENDED : INTEGER := 0;

    begin -- CONSUMER
     loop
       BUFFER.OUT_CHAR( CHAR );
       if CHAR = ASCII.EOT then NUM_ENDED := NUM_ENDED + 1; end if;
       if NUM_ENDED = NUM_PRODUCERS then exit;
        else
         PUT( CHAR );
       end if;
     end loop;
     INPUT_SERVER.STOP;
     BUFFER.STOP;
  end CONSUMER;


  task body BUFFER is separate;
  begin -- TASK_EXERCISE

  null;

end TASK_EXERCISE;



package body QUEUE_PKG is

    function  MODADD1( I : MODNUM )  return  MODNUM  is
    -- the function which takes an integer between 0 and
    -- MAX_SIZE and returns the integer incremented by 1
    -- modulo MAX_SIZE + 1.
      begin
	return ( (I + 1) mod MAX_COUNT );
      end MODADD1;


    
    function  EMPTY_Q ( Q : QUEUE ) return BOOLEAN  is 
    -- Returns TRUE if the Q is empty FALSE otherwise.
      begin
--	if ( Q.COUNT = 0 ) then put_line ("queue is empty");
--	 		   else put_line ("queue is not empty");
--        end if;
	return ( Q.COUNT = 0 );
      end EMPTY_Q;


    function  FULL_Q ( Q : QUEUE ) return BOOLEAN is
    -- Returns  TRUE if the Q is full FALSE otherwise.
      begin
--	if ( Q.COUNT = MAX_COUNT  ) then put_line ("queue is full");
--	 		   else put_line ("queue is not full");
--        end if;
	return ( Q.COUNT = MAX_COUNT );
      end FULL_Q;



    procedure ENQUEUE ( Q : in out QUEUE; IN_ITEM : in CHARACTER ) is
    -- Adds IN_ITEM to the front of Q.
    -- Does nothing (just returns) if Q is full.
      begin 
	if  Q.COUNT = MAX_COUNT then  return;  end if;
	Q.REAR  :=  MODADD1( Q.REAR );
     	Q.ITEM(Q.REAR)  :=  IN_ITEM;
        Q.COUNT :=  Q.COUNT + 1;
      end ENQUEUE;
 

    procedure DEQUEUE ( Q : in out QUEUE; OUT_ITEM : out CHARACTER ) is
    -- Removes an item from the rear of Q, returning it in OUT_ITEM.
    -- Does nothing (just returns) if Q is empty.

      begin 
	if  Q.COUNT = 0 then  return;  end if;
        OUT_ITEM :=  Q.ITEM( Q.FRONT );
        Q.FRONT  :=  MODADD1( Q.FRONT );
        Q.COUNT  :=  Q.COUNT - 1;
      end DEQUEUE;


end QUEUE_PKG;



