-- RopeReaderImpl.mesa
-- written by Bill Paxton,  January 1981
-- last edit by Bill Paxton, December 22, 1981 10:24 am

DIRECTORY
	RopeReader,
	Rope,
	RopeInline;

RopeReaderImpl: PROGRAM
   IMPORTS Rope, RopeInline
   EXPORTS RopeReader
   SHARES RopeReader, Rope =
   
BEGIN OPEN RopeReader, RopeInline;

ReadOffEnd: PUBLIC ERROR = CODE;

ReadChar: PUBLIC PROC [reader: Ref, mode: Mode] RETURNS [char: CHAR] = {
	base: ROPE ← reader.rope;
	index, current: Offset ← reader.index+reader.current-reader.first;
	after: Offset ← InlineSize[base];
	first: Offset ← 0;
	
	OffEnd: PROC RETURNS [CHAR] = {
		IF ~reader.charForEndOfRope THEN ERROR ReadOffEnd;
		RETURN [reader.endChar]};
		
	SetReader: PROC = {
		reader.index ← SELECT mode FROM
			get => index+1,
			backwards => index-1,
			peek, peekbackwards => index,
			ENDCASE => ERROR;
		reader.current ← reader.first ← reader.after ← 0};
		
	SetInfo: PROC [txtFlag: BOOLEAN] RETURNS [c: NAT] = {
		c ← Short[current];
		reader.first ← Short[first];
		reader.after ← Short[after];
		reader.txtFlag ← txtFlag;
		SELECT mode FROM
			get => { reader.current ← c+1; reader.index ← index+first-c };
			peekbackwards => { reader.current ← c+1; reader.index ← index+first-c-1 };
			backwards => { reader.current ← c; reader.index ← index+first-c-1 };
			peek => { reader.current ← c; reader.index ← index+first-c };
			ENDCASE => ERROR };

	SELECT mode FROM
	backwards, peekbackwards => IF current=0 THEN RETURN[OffEnd[]] ELSE current ← current-1;
  	get, peek => NULL;
	ENDCASE => ERROR;
	IF current >= after THEN RETURN[OffEnd[]];
	WHILE base # NIL DO WITH x:base SELECT FROM
	  text => { reader.text ← @x; RETURN [x[SetInfo[TRUE]]] };
	  node => WITH x:x SELECT FROM
		object  => IF current < x.size THEN WITH x.base SELECT FROM
			y: Chars => { reader.chars ← y; RETURN [y[SetInfo[FALSE]]] };
			ENDCASE => { SetReader; RETURN [x.fetch[x.base, current]] };
		substr => IF current < x.size THEN {
			offset: Offset;
			current ← current + (offset ← x.start);
			first ← first + offset; after ← after + offset;
			base ← x.base; LOOP };
		concat => IF current < x.size THEN {
		 	xpos: Offset;
			IF current >= (xpos ← x.pos) THEN {
				current ← current - xpos;  after ← after - xpos;
				first ← IF first <= xpos THEN 0 ELSE first - xpos; 
				base ← x.rest;  LOOP };
			IF after > xpos THEN after ← xpos; base ← x.base; LOOP };
		replace => IF current < x.size THEN {
			xstart: Offset ← x.start;
			newPos, oldPos: Offset;
			IF current < xstart THEN {
				IF after > xstart THEN after ← xstart;
				base ← x.base; LOOP };
			IF current < (newPos ← x.newPos) THEN {
				current ← current - xstart;
				after ← MIN[after, newPos] - xstart;
				first ← IF first <= xstart THEN 0 ELSE first-xstart;
				base ← x.replace;  LOOP };
			current ← current - newPos + (oldPos ← x.oldPos);
			after ← after - newPos + oldPos;
			first ← IF first >= newPos THEN first - newPos + oldPos ELSE oldPos;
			base ← x.base; LOOP };
		ENDCASE => ERROR Rope.NoRope;
	  ENDCASE => ERROR Rope.NoRope;
	  EXIT;
	ENDLOOP;
	RETURN[OffEnd[]]};

-- ***** Initialization

StartRopeReader: PUBLIC PROC = {};


END.