(* Ulm's Oberon Library
   Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany
   ----------------------------------------------------------------------------
   Ulm's Oberon Library is free software; you can redistribute it
   and/or modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either version
   2 of the License, or (at your option) any later version.

   Ulm's Oberon Library is distributed in the hope that it will be
   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   ----------------------------------------------------------------------------
   E-mail contact: oberon@mathematik.uni-ulm.de
   ----------------------------------------------------------------------------
   $Id: StreamCondi.om,v 1.1 1994/02/22 20:10:24 borchert Exp $
   ----------------------------------------------------------------------------
   $Log: StreamCondi.om,v $
   Revision 1.1  1994/02/22  20:10:24  borchert
   Initial revision

   ----------------------------------------------------------------------------
   AFB 1/92
   ----------------------------------------------------------------------------
*)

MODULE ulmStreamConditions;

   IMPORT Conditions := ulmConditions, Events := ulmEvents, Priorities := ulmPriorities, RelatedEvents := ulmRelatedEvents, Streams := ulmStreams;

   CONST
      msgFailed* = 0; (* message was not processed by the implementation *)
      invalidOp* = 1; (* operation was not read or write *)
      errorcodes* = 2;
   TYPE
      ErrorEvent* = POINTER TO ErrorEventRec;
      ErrorEventRec* =
	 RECORD
	    (Events.EventRec)
	    errorcode*: SHORTINT;
	 END;
   VAR
      errormsg*: ARRAY errorcodes OF Events.Message;
      error*: Events.EventType;

   CONST
      read* = 0; write* = 1; (* operations *)
   TYPE
      Operation* = SHORTINT; (* read or write *)
   TYPE
      CreateConditionMessage* =
	 RECORD
	    (Streams.Message)
	    (* in-parameters *)
	    operation*: Operation; (* read or write *)
	    (* out-parameters *)
	    condition*: Conditions.Condition; (* return value *)
	    stream*: Streams.Stream; (* message processed for this stream *)
	    msgProcessed*: BOOLEAN; (* initially FALSE; has to be set to TRUE *)
	 END;
      TestConditionMessage* =
	 RECORD
	    (Streams.Message)
	    (* in-parameters *)
	    operation*: Operation; (* read or write *)
	    errors*: RelatedEvents.Object; (* relate errors to this object *)
	    (* out-parameters *)
	    wouldblock*: BOOLEAN;
	    msgProcessed*: BOOLEAN;
	 END;

   TYPE
      Condition = POINTER TO ConditionRec;
      ConditionRec =
	 RECORD
	    (Conditions.ConditionRec)
	    stream: Streams.Stream;
	    operation: Operation;
	 END;
   VAR
      domain: Conditions.Domain;

   PROCEDURE InitErrorHandling;
   BEGIN
      Events.Define(error);
      Events.SetPriority(error, Priorities.liberrors);
      errormsg[msgFailed] :=
	 "operation not processed by underlying stream implementation";
      errormsg[invalidOp] := "invalid operation (read or write expected)";
   END InitErrorHandling;

   PROCEDURE Error(object: RelatedEvents.Object; errorcode: SHORTINT);
      VAR
	 event: ErrorEvent;
   BEGIN
      NEW(event); event.type := error; event.message := errormsg[errorcode];
      event.errorcode := errorcode;
      RelatedEvents.Raise(object, event);
   END Error;

   PROCEDURE Test(domain: Conditions.Domain; condition: Conditions.Condition;
		  errors: RelatedEvents.Object) : BOOLEAN;
      VAR
	 msg: TestConditionMessage;
   BEGIN
      WITH condition: Condition DO
	 CASE condition.operation OF
	 | read:  IF Streams.InputInBuffer(condition.stream) THEN
		     RETURN TRUE
		  END;
	 | write: IF Streams.OutputWillBeBuffered(condition.stream) THEN
		     RETURN TRUE
		  END;
	 END;
	 msg.operation := condition.operation;
	 msg.errors := errors;
	 msg.wouldblock := TRUE;
	 msg.msgProcessed := FALSE;
	 Streams.Send(condition.stream, msg);
	 IF ~msg.msgProcessed THEN
	    Error(errors, msgFailed); RETURN FALSE
	 END;
	 RETURN ~msg.wouldblock
      END;
   END Test;

   PROCEDURE InitDomain;
      VAR
	 if: Conditions.Interface;
	 desc: Conditions.Description;
   BEGIN
      NEW(if); if.test := Test;
      NEW(desc); desc.caps := {}; desc.internal := TRUE;
      NEW(domain); Conditions.InitDomain(domain, if, desc);
   END InitDomain;

   PROCEDURE Create*(VAR condition: Conditions.Condition;
		    s: Streams.Stream; operation: Operation);
      (* condition = NIL in error case, eg if the associated
	 stream implementation does not interpret such messages
      *)
      VAR
	 msg: CreateConditionMessage;
	 newcond: Condition;
   BEGIN
      IF (operation # read) & (operation # write) THEN
	 condition := NIL; Error(s, invalidOp); RETURN
      END;
      msg.operation := operation; msg.condition := NIL;
      msg.stream := s; msg.msgProcessed := FALSE;
      Streams.Send(s, msg);
      IF ~msg.msgProcessed THEN
	 Error(s, msgFailed); condition := NIL; RETURN
      END;
      IF (msg.condition # NIL) & (msg.stream = s) THEN
	 (* underlying implementation has its own domain and
	    defines it own conditions
	 *)
	 condition := msg.condition; RETURN
      END;
      NEW(newcond); newcond.stream := s; newcond.operation := operation;
      Conditions.Init(newcond, domain);
      condition := newcond;
   END Create;

BEGIN
   InitErrorHandling;
   InitDomain;
END ulmStreamConditions.
