MODULE CommObxStreamsClient;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	IMPORT Services, CommStreams, TextModels, TextControllers, StdLog;

	CONST
		protocol = "CommTCP";			(* driver for TCP/IP communication, over Winsock *)
		remoteAdr = "127.0.0.1:900";	(* loopback address, so you can test on your local machine;
														choose a port number that is not used yet (here: 900).
														You could also specify an IP address in the form
														"mymachine.mydomain.com:900" *)
		localAdr = "";							(* don't specify an address or port on the client side *)

	TYPE
		Sender = POINTER TO RECORD (Services.Action)
			prev, succ: Sender;	(* linked to allow stopping upon module unloading *)
			stream: CommStreams.Stream;	(* during sending, the communicates through this object *)
			idx, len: INTEGER;					(* index for start of data into the buf array, and length remaining to be sent *)
			buf: ARRAY 256 OF BYTE		(* data to be sent *)
		END;

	VAR
		senders: Sender;

	PROCEDURE (s: Sender) Do;
		VAR written: INTEGER;
	BEGIN
		IF s.stream.IsConnected() THEN
			s.stream.WriteBytes(s.buf, s.idx, s.len, written);	(* poll for outgoing data *)
			INC(s.idx, written); DEC(s.len, written);
			IF s.len > 0 THEN	(* keep action alive if there remains further data to send *)
				Services.DoLater(s, Services.now)
			ELSE
				s.stream.Close;
				IF s.prev # NIL THEN s.prev.succ := s.succ ELSE senders := s.succ END;
				IF s.succ # NIL THEN s.succ.prev := s.prev END
			END
		ELSE	(* connection was closed by server *)
			IF s.prev # NIL THEN s.prev.succ := s.succ ELSE senders := s.succ END;
			IF s.succ # NIL THEN s.succ.prev := s.prev END;
			IF s.idx = 0 THEN StdLog.String("client: connection was not accepted by server")
			ELSE StdLog.String("client: connection was closed by server")
			END;
			StdLog.Ln
		END
	END Do;


	PROCEDURE Start (s: Sender);
		VAR stream: CommStreams.Stream; res: INTEGER;
	BEGIN
		CommStreams.NewStream(protocol, localAdr, remoteAdr, stream, res);
		IF stream # NIL THEN
			s.prev := NIL; s.succ := senders; senders := s;
			IF s.succ # NIL THEN s.succ.prev := s END;
			s.stream := stream;
			Services.DoLater(s, Services.now);
			StdLog.String("client: connection opened"); StdLog.Ln
		ELSE
			StdLog.String("client: error opening the connection ("); StdLog.Int(res); StdLog.Char(")"); StdLog.Ln
		END
	END Start;

	PROCEDURE Stop;
	BEGIN
		WHILE senders # NIL DO
			senders.stream.Close; Services.RemoveAction(senders);
			senders := senders.succ
		END
	END Stop;

	PROCEDURE SendTextSelection*;
		VAR c: TextControllers.Controller; beg, end, i, len: INTEGER; rd: TextModels.Reader; ch: CHAR; s: Sender;
	BEGIN
		c := TextControllers.Focus();
		IF (c # NIL) & c.HasSelection() THEN
			NEW(s);
			c.GetSelection(beg, end);
			rd := c.text.NewReader(NIL); rd.SetPos(beg);
			i := 0; len := end - beg;
			IF len >= LEN(s.buf) - 1 THEN len := LEN(s.buf) - 1 END;	(* clip string if necessary *)
			WHILE len # 0 DO
				rd.ReadChar(ch);
				IF ch < 100X THEN	(* skip Unicode characters *)
					s.buf[i] := SHORT(SHORT(ORD(ch))); INC(i)
				END;
				DEC(len)
			END;
			s.idx := 0; s.len := i;
			Start(s)
		ELSE
			StdLog.String("client: no text selection found"); StdLog.Ln
		END
	END SendTextSelection;

CLOSE
	Stop	(* prevent the client from trapping after module unloading *)
END CommObxStreamsClient.

 CommObxStreamsClient.SendTextSelection
