-- RopeReaderGetImpl.mesa
-- written by Bill Paxton,  January 1981
-- last edit by Bill Paxton, December 22, 1981 9:37 am

DIRECTORY
	RopeReader,
	RopeEditingBLT,
	Rope,
	RopeInline;

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

GetText: PUBLIC PROC
	[reader: Ref, txt: Text, length: NAT ← LAST[NAT]] RETURNS [count: NAT] = {
	rope: ROPE;
	start: Offset;
	offset: NAT ← txt.length;
	[rope, start] ← Position[reader];
	count ← Short[MIN[length, InlineSize[rope]-start, txt.max-offset]];
	GTxt[rope, start, txt, offset, count];
	SetPosition[reader, rope, start+count];
	txt.length ← txt.length+count};

BackwardsGetText: PUBLIC PROC
	[reader: Ref, txt: Text, length: NAT ← LAST[NAT]] RETURNS [count: NAT] = {
	rope: ROPE;
	start: Offset;
	offset: NAT ← txt.length;
	[rope, start] ← Position[reader];
	count ← Short[MIN[length, start, txt.max-offset]];
	GTxt[rope, start ← start-count, txt, offset, count];
	SetPosition[reader, rope, start];
	txt.length ← txt.length+count};

GetString: PUBLIC PROC
	[reader: Ref, str: String, length: NAT ← LAST[NAT]] RETURNS [count: NAT] = {
	rope: ROPE;
	start: Offset;
	offset: NAT ← str.length;
	[rope, start] ← Position[reader];
	count ← Short[MIN[length, InlineSize[rope]-start, str.maxLength-offset]];
	GTxt[rope, start, str, offset, count];
	SetPosition[reader, rope, start+count];
	str.length ← str.length+count;};

BackwardsGetString: PUBLIC PROC
	[reader: Ref, str: String, length: NAT ← LAST[NAT]]
	RETURNS [count: NAT] = {
	rope: ROPE;
	start: Offset;
	offset: NAT ← str.length;
	[rope, start] ← Position[reader];
	count ← Short[MIN[length, start, str.maxLength-offset]];
	GTxt[rope, start ← start-count, str, offset, count];
	SetPosition[reader, rope, start];
	str.length ← str.length+count};

GetChars: PUBLIC PROC [
	reader: Ref,
	chars: REF CharsArray,
	length: NAT ← LAST[NAT],
	offset: NAT ← 0]
	RETURNS [count: NAT] = {
	rope: ROPE;
	start: Offset;
	[rope, start] ← Position[reader];
	count ← Short[MIN[length, InlineSize[rope]-start, charsPerArray-offset]];
	GTxt[rope, start, chars, offset, count];
	SetPosition[reader, rope, start+count]};

BackwardsGetChars: PUBLIC PROC [
	reader: Ref, chars: REF CharsArray,
	length: NAT ← LAST[NAT],
	offset: NAT ← 0]
	RETURNS [count: NAT] = {
	rope: ROPE;
	start: Offset;
	[rope, start] ← Position[reader];
	count ← Short[MIN[length, start, charsPerArray-offset]];
	GTxt[rope, start ← start-count, chars, offset, count];
	SetPosition[reader, rope, start]};

GTxt: PROC
	[base: ROPE, start: Offset, b: REF, offset, len: NAT] = {
	loc: NAT ← offset;
	WHILE len # 0 DO WITH x:base SELECT FROM
	 text => {
	 	st: NAT ← Short[start];
		rem: NAT ← Short[x.length]-st;
		IF rem > len THEN rem ← QShort[len];
		WITH b SELECT FROM
			chars: REF CharsArray => RopeEditingBLT.TextToArrayBlt[to:chars, toLoc:loc, nChars:rem,
							from:@x, fromLoc:st];
			txt: Text => RopeEditingBLT.TextToTextBlt[to:txt, toLoc:loc, nChars:rem,
							from:@x, fromLoc:st];
			str: String => RopeEditingBLT.TextToStringBlt[to:str, toLoc:loc, nChars:rem,
							from:@x, fromLoc:st];
			ENDCASE => ERROR;
		RETURN };
	  node => WITH x:x SELECT FROM
		object  => WITH x.base SELECT FROM
			y: Chars => {
				st: NAT ← Short[start];
				rem: NAT ← Short[x.size]-st;
				IF rem > len THEN rem ← QShort[len];
				WITH b SELECT FROM
					chars: REF CharsArray =>
						RopeEditingBLT.ArrayToArrayBlt[to:chars, toLoc:loc, nChars:rem,
									from:y, fromLoc:st];
					txt: Text =>
						RopeEditingBLT.ArrayToTextBlt[to:txt, toLoc:loc, nChars:rem,
									from:y, fromLoc:st];
					str: String =>
						RopeEditingBLT.ArrayToStringBlt[to:str, toLoc:loc, nChars:rem,
									from:y, fromLoc:st];
					ENDCASE => ERROR;
				RETURN };
			ENDCASE => {
				piecemap: Rope.PieceMapType ← x.pieceMap;
				data: REF ← x.base;
				fetch: Rope.FetchType ← x.fetch;
				rem: Offset ← NonNeg[x.size-start];
				IF rem < len THEN {
					IF rem = 0 THEN EXIT ELSE len ← QShort[rem]};
				IF piecemap # NIL THEN {
					action: PROC [r:ROPE, st: Offset, pl: Offset]
						RETURNS [BOOLEAN] = {
						ln: NAT ← Short[pl];
						GTxt[r, st, b, loc, ln]; loc ← loc + ln;
						RETURN [FALSE]};
					[] ← piecemap[data, start, len, action]}
				ELSE WITH b SELECT FROM
					txt: Text =>
						FOR i: Offset IN [start..start+len) DO
							txt[loc] ← fetch[data,i]; loc ← loc+1; ENDLOOP;
					chars: REF CharsArray =>
						FOR i: Offset IN [start..start+len) DO
							chars[loc] ← fetch[data,i]; loc ← loc+1; ENDLOOP;
					str: String =>
						FOR i: Offset IN [start..start+len) DO
							str[loc] ← fetch[data,i]; loc ← loc+1; ENDLOOP;
					ENDCASE => ERROR;
				EXIT};
		substr  => {
			rem: Offset ← NonNeg[x.size-start];
			IF rem < len THEN {
				IF rem = 0 THEN EXIT ELSE len ← QShort[rem]};
			start ← start + x.start; base ← x.base; LOOP};
		concat  => {
			xpos: Offset ← x.pos;
			rem: Offset ← NonNeg[x.size-start];
			IF rem < len THEN {
				IF rem = 0 THEN EXIT ELSE len ← QShort[rem]};
			IF start+len <= xpos THEN {base ← x.base; LOOP};
			IF start < xpos THEN {
				subLen: NAT ← Short[xpos-start];
				GTxt[x.base, start, b, loc, subLen];
				start ← 0; len ← len - subLen; loc ← loc + subLen}
			ELSE start ← start - xpos;
			base ← x.rest; LOOP };
		replace => {
			xstart: Offset ← x.start;
			xnew: Offset ← x.newPos;
			rem: Offset ← NonNeg[x.size-start];
			IF rem < len THEN
				{IF rem = 0 THEN EXIT ELSE len ← QShort[rem]};
			IF start < xstart THEN
				{subLen: NAT;
				IF xstart-start >= len THEN {base ← x.base; LOOP};
				subLen ← Short[xstart-start];
				GTxt[x.base, start, b, loc, subLen];
				start ← xstart; len ← len - subLen; loc ← loc + subLen};
			IF start < xnew THEN
				{subLen: NAT;
				st: Offset ← start - xstart;
				IF xnew-start >= len THEN
					{start ← st; base ← x.replace; LOOP};
				subLen ← Short[xnew-start];
				GTxt[x.replace, st, b, loc, subLen];
				start ← xnew; len ← len - subLen; loc ← loc + subLen};
			start ← start - xnew + x.oldPos; base ← x.base; LOOP};			  
		ENDCASE => { IF base # NIL THEN ERROR Rope.NoRope;
				IF len = 0 THEN EXIT ELSE ERROR};
	  ENDCASE => { IF base # NIL THEN ERROR Rope.NoRope;
			IF len = 0 THEN EXIT ELSE ERROR};
	  ENDLOOP};

-- ***** Initialization

StartRopeReaderGet: PUBLIC PROC = {};

END.