*****************************************************************************************************
; WP Paragraph Manipulation Routines
; 
;   This file contains the routines used to manipulate the paragraph data structure used by WP. The
; paragraph structure is described below:
;
; The paragraph data structure is an array from 1..W_LastP of paragraph info blocks:
;
;	W_pBlockHand	gequ 0  	: handle to the block that the par is in
;	W_pOffset		gequ 4  	: offset into this block that starts the par
;	W_pAttr	  	gequ 6  	: attribute word for the par
;	W_pRulerHand	gequ 8  	: handle to the ruler that governs this par
;	W_pPixels		gequ 12 	: number of pixels tall this par is
;	W_pLineHand	  	gequ 14 	: handle to the line struct for this par (Purgeable)
;	W_pLastLine	  	gequ 18 	: number of last line in the par+1, cr is 0
;	W_pBytes	 	gequ 20 	: equate for size of paragraph block.
;
*****************************************************************************************************

	load 	'macros.dump'
	include 	'driver.equ'
	include 	'wp.equ'

W_StartMaxP		equ 10
W_StartBlockSize	equ $1000
W_GrowAmount	equ $400
	
	IMPORT	D_SetFileChanged
	IMPORT	D_Deref
	IMPORT	D_GrowHandle
	IMPORT	D_GrowLHandle
	IMPORT	D_MainZPage
	IMPORT	D_NeedHand

	IMPORT	W_CurDoc
	IMPORT	W_FindFont
	IMPORT	W_GetLRecPtr
	IMPORT	W_GetLineRec
	IMPORT	W_LastP
	IMPORT	W_MakeLines
	IMPORT	W_MaxP
	IMPORT	W_PHandle
	IMPORT	W_PPtr
	
	IMPORT	W_RulBits
	IMPORT	W_RulBytes
	IMPORT	W_Selected
	
	IMPORT	W_StartLine
	IMPORT	W_StartOffset
	IMPORT	StdRuler
	IMPORT	W_StartPar
	IMPORT	W_ValidLines
	IMPORT	W_WPRuler
	IMPORT	W_Stuff

	ENTRY	W_GetAddr
	ENTRY	W_GetLastLGuts
	ENTRY	W_GetParRec
	ENTRY	W_LessRoom
	ENTRY	W_MakeEmptyBlock
	ENTRY	W_MakeNewParRec
	ENTRY	W_MakeRoom
	ENTRY	W_NewRuler
	ENTRY	W_UseRuler

*****************************************************************************************************
; W_InitPars ( )
;
;   This routine is called to setup and empty document. It will create a new paragraph array and a
; single carriage return paragraph.
;
; Note: Memory errors are returned.

W_InitPars	PROC	EXPORT
	;Using 	wpglobals
	;Using 	W_ParData
	
	local 	ptr:l,TextPtr:l,RulerHand:l,TextBlock:l
	error	err
	begin

; Ask for initial paragraph array handle. If memory error then exit with error.

	call	D_NeedHand,in=(#W_StartMaxP*W_pBytes:l),out=(ax:l),err=err
	jcs	exit

; Lock down paragraph handle and get pointer to first paragraph entry.

	movelong	ax,W_PHandle
	jsl	D_Deref
	movelong	ax,W_PPtr
	moveword 	#W_StartMaxP,W_MaxP
	moveword 	#1,W_LastP
	rcall	W_GetParRec,in=(#1:a),out=(ptr:ax)

; Get text block for 1st paragraph.

	call	W_MakeEmptyBlock,out=(TextBlock:l),err=err
	jcs	@killpar
	movelong	TextBlock,[ptr]

; Initialize 1st paragraph entry in paragraph array.

	moveword 	#W_BHeader,[ptr]:#W_pOffset
	moveword 	#0,[ptr]:#W_pAttr
	movelong 	#0,[ptr]:#W_pLineHand
	moveword 	#1,[ptr]:#W_pLastLine

; Get ruler for 1st paragraph. If memory error then clean up and exit with error. Else use ruler.

	call	W_NewRuler,out=(RulerHand:l),err=err
	bcs	@killtext
	movelong	RulerHand,[Ptr]:#W_pRulerHand
	call	W_UseRuler,in=(#1:w,RulerHand:l)

; Make room for initial text block contents - if error then clean up and exit.

	call	W_MakeRoom,in=(#1:w,#0:w,#W_TextHeader+1:w),err=err
	bcs	@killruler

; Set up contents of 1st paragraph text block - single carriage return.

	rcall	W_GetAddr,in=(#1:a,#0:x),out=(TextPtr:ax)
	moveword 	#W_StandFont,[TextPtr]
	moveword 	#W_StandStyle,[TextPtr]:#2
	moveword 	#Black,[TextPtr]:#4
	movebyte 	#cr,[TextPtr]:#W_TextHeader

; Create lines for 1st paragraph and initialize globals. If we can't make lines then return memory
; error.

	call	W_MakeLines,in=(#1:w),err=err
	bcs	@killruler
	stz 	W_StartLine
	stz 	W_Selected
	moveword 	#1,W_StartPar
	moveword 	#W_TextHeader,W_StartOffset
	bra	exit

; Memory error occurred so clean up allocated memory.

@killruler	tool	_DisposeHandle,in=(RulerHand:l)
@killtext	tool	_DisposeHandle,in=(TextBlock:l)
@killpar	tool	_DisposeHandle,in=(W_PHandle:l)

exit	return


	ENDP

****************************************************************
* W_GetAddr - returns the physical address of par a, offset x
* returns address in ax
W_GetAddr	PROC	EXPORT
	;Using D_GlobalData
ParRec	equ 0
handle	equ 4
ptr	equ 8

	tay
	phd
	lda >D_MainZPage
	tcd
	phx	;D_Save offset for later

	tya
	jsl W_GetParRec
	movelong ax,ParRec

	movelong [ParRec],handle
	movelong [handle],ptr

	ldx ptr+2

	ldy #W_pOffset
	lda [ParRec],y
	clc
	adc ptr
	bcc NoInc
	inx
	clc
NoInc	adc 1,s
	bcc NoInc2
	inx

NoInc2	ply
	pld
	rtl

	ENDP




****************************************************************
* W_GetParRec - gets the physical address of par a's record structure
* adress is returned in ax
* NOTE!!! this must be changed as W_pBytes is changed. Hard-wired.
W_GetParRec	PROC	EXPORT
	;Using wpglobals
	;Using D_GlobalData

	tay
	phd
	lda >D_MainZPage
	tcd

	ldx W_PPtr+2

	tya
	dec a	;since pars start with 1
	asl a
	asl a
	sta 2	;the times 4 part

	asl a
	bcc NoInca
	inx	;if over here, takes 2 inx
	inx
NoInca
	asl a	;the times 16 part
	bcc NoInc
	inx
	clc
NoInc
	adc 2	;add times4 with times16
	bcc NoInc2
	inx
	clc
NoInc2
	adc W_PPtr	;add to Par ptr offset
	bcc NoInc3
	inx
NoInc3
	pld
	rtl

	ENDP


*****************************************************************************************************
; W_MakeEmptyBlock ( ): BlockHandle:long
;
;   This routine is called to allocate a new empty text block and initializes its header counts.

W_MakeEmptyBlock PROC	EXPORT
	;Using 	W_ParData
	
	output 	BlockHand:l
	local 	ptr:l
	error 	err
	begin

	call	D_NeedHand,in=(#W_StartBlockSize:l),out=(ax:l),err=err
	bcs	@exit
	movelong	ax,BlockHand
	movelong	[BlockHand],ptr
	moveword 	#W_StartBlockSize,[ptr]:#W_BSize
	moveword 	#W_BHeader,[ptr]:#W_BUsed

@exit	return

	ENDP

*****************************************************************************************************
; W_DoRoom ( Paragraph:word, Offset:word, Bytes:word )
;
;   This routine is called to adjust the given paragraph's text block by the given number of bytes.
; Bytes is a signed number possibly negative. If negative then call LessRoom, else use MakeRoom.

W_DoRoom	PROC	EXPORT

	lda 	4,s		;look at bytes
	bpl 	W_MakeRoom
	eor 	#$ffff		; negate into a positive value to pass LessRoom.
	inc 	a
	sta 	4,s
	brl 	W_LessRoom

	ENDP

*****************************************************************************************************
; W_MakeRoom ( Paragraph:word, Offset:word, Bytes:word )
;
;   This routine is called to make a gap in the text for the given paragraph, at the given offset,
; of Bytes in size. If insufficient space exists in the block then it will be split or grown as
; needed. Memory errors are returned.
;
; Note: Assumes affected blocks are unlocked.

W_MakeRoom	PROC	EXPORT
	;Using 	wpglobals
	;Using 	W_ParData
	EXPORT	NotLast
	EXPORT	W_CurrentBlockHandle
	EXPORT	W_CurrentBlockHandle2
	
	input 	par:w,offset:w,bytes:w
	local 	BlockPtr:l,BlockHand:l,newsize:w,ParPtr:l,LastParPtr:l
	local 	used:w,newmax:w,BlockHand2:l,BlockPtr2:l,ParPtr2:l
	local 	addbytes:w,CurPar:w
	error 	err
	begin

; Clear error status and current block handles. Current block handles are used to keep track of
; which handles we are using so that if the out of memory queue is called it won't try to collapse
; text/rulers for blocks we are in the process of changing!

	stz	err
	stzl	W_CurrentBlockHandle
	stzl	W_CurrentBlockHandle2

; If insertion gap size is zero then simply exit with no error, else we are manipulating the document
; so go ahead and set file changed bit.

	lda 	bytes
	jeq 	exit
	call	D_SetFileChanged,in=(W_CurDoc:l)

; Try to make gap in text block {main outer loop}. Get text block for the given paragraph.

TryAgain	rcall	W_GetParRec,in=(par:a),out=(ParPtr:ax)
	movelong 	[ParPtr],BlockHand
	movelong 	[BlockHand],BlockPtr

; Branch if not enough room exists in the current block to handle the growth. If block is already
; of StartBlockSize then 

	ldy 	#W_BUsed
	lda 	[BlockPtr],y
	sta 	used
	clc
	adc 	bytes
	sta 	newsize
	jcs 	NoRoom
	cmpw 	newsize,[BlockPtr]
	bcc 	IsRoom
	cmpw 	newsize,#W_StartBlockSize
	jcs 	NoRoom

; Grow the text block to StartBlockSize (obviously it has been collapsed to <4k). Remember the
; dialog that we are mucking with so that collapse rulers will not try to collapse the text block
; that we are trying to grow! If we can not grow the text block then try to split it and create a
; new one. (just like if we couldn't get the room).

	movelong 	BlockHand, W_CurrentBlockHandle	
	stzl	W_CurrentBlockHandle2
	call	D_GrowHandle,in=(#0:w,#W_StartBlockSize:w,BlockHand:l),err=err
	jcs	NoRoom

; Successfully grew the text block - set its new size.

	movelong 	[BlockHand],BlockPtr
	moveword 	#W_StartBlockSize,[BlockPtr]:#W_BSize

; Enough room exists in the text block, so make the gap and update used size and other ptrs.

IsRoom	rcall	W_GetAddr,in=(par:a,offset:x)

	phx   			;src high
	pha   			;src low
	phx   			;dest high
	clc
	adc 	bytes
	pha   			;dest low
	bcc 	@noinc
	inx
	txa
	sta 	3,s		; source & dest now on stack.
@noinc	pea	0  		; length high word - never moving more than 64k
	lda 	used
	sec
	ldy 	#W_pOffset
	sbc 	[ParPtr],y
	sbc 	offset   		; used-(paroffset(inblock)+offset(inpar))
	pha
	_BlockMove   		; mem move

; Update the used count in the block. Get pointer to last paragraph that needs to be have its ptr
; updated. While paragraphs left to go and they share the same text block - update paragraph pointers
; to reflect gap insertion. Exit with error status still clear. - succeeded.

	moveword 	newsize,[BlockPtr]:#W_BUsed
	rcall	W_GetParRec,in=(W_LastP:a),out=(LastParPtr:ax)
	bra	@loopchk
	
@loop	cmpl 	[ParPtr],BlockHand
	bne 	@endloop
	ldy 	#W_pOffset
	addword 	[ParPtr]:y,bytes,[ParPtr]:y
@loopchk	addwl	#W_pBytes,ParPtr
	cmpl 	LastParPtr,ParPtr
	bcs 	@loop
@endloop	brl 	exit

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; Either we were unable to grow the paragraph's current text block to allow for the gap size or it
; was already 4k so we want to perform a split. Three cases exist:
;
; 1) Paragraph is already in a block by itself.
; 2) Paragraph is last paragraph to use the text block we want to split.
; 3) Paragraph is not last to use text block.
;
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

; Check if only paragraph to use text block. If not first paragraph check if previous paragraph had
; same text block, and if not last paragraph check if next paragraph had same text block.

NoRoom	movelong 	[BlockHand],BlockPtr
	cmpw	par,#1
	beq 	@nonebefore
	dec 	a
	jsl 	W_GetParRec
	movelong 	ax,ParPtr
	cmpl 	[ParPtr],BlockHand
	beq	@NotOnly
	
@nonebefore	cmpw	par,W_LastP
	beq	IsOnly
	inc 	a
	jsl 	W_GetParRec
	movelong 	ax,ParPtr
	cmpl 	[ParPtr],BlockHand
	bne	IsOnly
@NotOnly	brl	NotAlone

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; CASE 1: Paragraph is already in a block by itself.
;
;   In this case we grow the text block beyond 4k upto 64k. If text block will exceed 64k then exit
; with memory error. Make sure to remember text block we are trying to grow so collapse rulers/text
; in the out of memory queue won't try to shrink what we're growing.

IsOnly	ldy 	#W_BUsed
	lda 	[BlockPtr],y
	clc
	adc 	bytes
	bcs 	@gomemerr		
	addword 	newsize,#W_GrowAmount,newmax
	bcs	@gomemerr
	stzl	W_CurrentBlockHandle2
	movelong	BlockHand,W_CurrentBlockHandle 
	call	D_GrowHandle,in=(#0:w,newmax:w,BlockHand:l)
	bcc	@case1ok
@gomemerr	brl	domemerr

; Succeeded in growing text block so update used and max counts.

@case1ok	movelong 	[Blockhand],BlockPtr
	moveword 	newmax,[BlockPtr]		; update max
	moveword 	newsize,[BlockPtr]:#W_BUsed   	; update used

; Enough room exists in the text block, so make the gap and return with error status clear. Restore
; the paragraph pointer since NoRoom above could have left it pointing to either previous or next
; paragraph instead.

	rcall	W_GetParRec,in=(par:a),out=(ParPtr:ax)
	rcall	W_GetAddr,in=(par:a,offset:x)
	phx   				; src high
	pha   				; src low
	phx   				; dest high
	clc
	adc 	bytes
	pha   				; dest low
	bcc 	@noinc
	inx
	txa
	sta 	3,s
@noinc	pea	0 			; length high word - never more than 64k
	lda 	used
	sec
	ldy 	#W_pOffset
	sbc 	[ParPtr],y
	sbc 	offset   			; used-(paroffset(inblock)+offset(inpar))
	pha
	_BlockMove   			; mem move
	brl	exit

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

; Check if paragraph is last paragraph to use the text block. If is last paragraph in the document
; then we will need to create a new block for it (don't need to check to see if it will fit in next
; block - there is none). Addbytes = bytes - (used-offset).

NotAlone	rcall	W_GetParRec,in=(par:a),out=(ParPtr:ax)

	subword 	[BlockPtr]:#W_BUsed,[ParPtr]:#W_pOffset,a
	addword 	a,bytes,addbytes

	cmpw	par,W_LastP
	jeq 	MakeNewOne
	inc 	a
	jsl 	W_GetParRec
	movelong 	ax,ParPtr2

	cmpl 	[ParPtr2],BlockHand
	jeq 	NotLast

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; CASE 2: Paragraph is last paragraph to use the text block.
;
;   In this case we need to check to see if the paragraph will fit in the next text block. If not we
; need to create a new one, else make gap at start of next text block. Update pointers for all other
; paragraphs using the same text block.

IsLast	movelong 	[ParPtr2],BlockHand2
	movelong 	[BlockHand2],BlockPtr2

	addword 	addbytes,[BlockPtr2]:#W_BUsed,a
	cmpw 	a,[BlockPtr2]:#W_BSize
	jcs 	MakeNewOne

	lda 	par
	inc 	a
	ldx 	#0
	jsl 	W_GetAddr
	phx   				;src high
	pha   				;src low
	phx   				;dest high
	clc
	adc 	addbytes
	pha   				;dest low
	bcc 	@noinc
	inx
	txa
	sta 	3,s
@noinc	pea	0 			; length high word - never more than 64k
	pushword 	[BlockPtr2]:#W_BUsed
	_BlockMove   			;mem move

	ldy 	#W_BUsed
	addword 	addbytes,[BlockPtr2]:y,[BlockPtr2]:y
	
	rcall	W_GetParRec,in=(W_LastP:a),out=(LastParPtr:ax)
@loop	cmpl 	[ParPtr2],BlockHand2
	bne	@endloop
	ldy 	#W_pOffset
	addword 	[ParPtr2]:y,addbytes,[ParPtr2]:y
	addwl 	#W_pBytes,ParPtr2
	cmpl 	LastParPtr,ParPtr2
	bcs 	@loop
@endloop	bra 	GotRoom

; Allocate new text block for the paragraph. Either its the last paragraph in the document or there
; was not enough room in the text block of the next paragraph after it. If memory error then return
; it and exit with carry set.

MakeNewOne	movelong	BlockHand,W_CurrentBlockHandle
	stzl	W_CurrentBlockHandle2
	call	W_MakeEmptyBlock,out=(BlockHand2:l)
	jcs	domemerr

; If we need more than 4k then we have to resize the empty block we just got. If resize fails or
; will generate a >64k text block then return with memory error. Note: don't want out of memory
; queue trying to shrink the handle we're trying to grow! If we can't resize the block make sure
; we dispose of it.

	cmpw 	addbytes,#W_StartBlockSize
	bcc 	GotRoom
	movelong 	BlockHand2,W_CurrentBlockHandle	
	addword	addbytes,#W_GrowAmount,a	
	bcs	@killblock2
@growit	sta	newmax
	pea	0
	pha
	pushlong 	BlockHand2
	jsl 	D_GrowHandle
	bcc	@gotit
@killblock2	tool	_DisposeHandle,in=(BlockHand2:l)
	brl 	domemerr	

; Successfully resized the block - set its max counts.  Used is set below.

@gotit	movelong 	[BlockHand2],BlockPtr2
	MoveWord	newmax,[BlockPtr2]

; At this point we can assume that BlockHand2 can contain the given paragraph. Move all the text
; for the paragraph before where we want the gap into our new text block.

GotRoom	addlong 	[BlockHand2],#W_BHeader,LastParPtr
	rcall	W_GetAddr,in=(par:a,#0:x)
	phx   				; src high
	pha   				; src low
	pushlong 	LastParPtr
	pushword 	#0   			; high, never moving more than 64k
	pushword 	offset
	_BlockMove   			; mem move

; Get the total number of bytes that existed in the paragraph before growing it. Move text up
; to create the gap.

	subword 	addbytes,bytes,addbytes	
	rcall	W_GetAddr,in=(par:a,offset:x)
	phx   				; src high
	pha   				; src low
	addword 	bytes,offset,a
	addwl	a,LastParPtr
	pushlong 	LastParPtr
	pushword 	#0   			; high, never moving more than 64k
	subword 	addbytes,offset,s
	_BlockMove   			; mem move

; Update used counts for both original text block and new text block. Subtract the total number of
; bytes in the paragraph from the first block used count and add bytes grown to second block count.
; Replace text block for paragraph with the new one we have created. Exit with no errors.

	movelong 	[ParPtr],BlockHand
	movelong 	[BlockHand],BlockPtr
	movelong	[BlockHand2],BlockPtr2
	
	ldy 	#W_BUsed
	subword 	[BlockPtr]:y,addbytes,[BlockPtr]:y
	addword 	bytes,addbytes,a
	addword 	a,[BlockPtr2]:y,[BlockPtr2]:y
	
	movelong 	BlockHand2,[ParPtr]
	moveword 	#W_BHeader,[ParPtr]:#W_pOffset
	brl 	exit

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; CASE 3: Paragraph is not last to use text block.
;
;   In this case we need to put all the paragraphs after this one in their own block and then call
; MakeRoom to make this paragraph as big as we can.

; Get pointers to last document paragraph and next paragraph.

NotLast	rcall	W_GetParRec,in=(W_LastP:a),out=(LastParPtr:ax)
	lda 	par
	inc 	a
	sta 	CurPar
	jsl 	W_GetParRec
	movelong 	ax,ParPtr
	movelong	ax,ParPtr2

; Get amount of space taken up by all latter paragraphs that share the same text block. Then find
; the last paragraph that shares the same text block with the paragraph we're trying to grow. If
; all remaining paragraphs in the document share this text block then we will need to create a
; brand new text block.

	subword 	[BlockPtr]:#W_BUsed,[ParPtr]:#W_pOffset,addbytes

@loop	cmpl 	[ParPtr2],BlockHand
	bne 	GotIt
	inc 	CurPar
	addwl	#W_pBytes,ParPtr2
	cmpl 	LastParPtr,ParPtr2
	bcs 	@loop
	brl 	MakeNewOne2

; ParPtr2 now points to first paragraph with a different text block. Get its text block pointer.

GotIt	movelong 	[ParPtr2],BlockHand2	
	movelong 	[BlockHand2],BlockPtr2  

; Check if the text block can contain the latter paragraphs. If not we will need to create a brand
; new text block to hold them.

	addword 	addbytes,[BlockPtr2]:#W_BUsed,a
	cmpw 	a,[BlockPtr2]:#W_BSize
	jcs 	MakeNewOne2

; Enough memory exists in the next text block to hold the paragraphs we are bumping. So move the next
; text blocks contents to make room for inserting the text we are bumping.

	lda 	Curpar
	ldx 	#0
	jsl 	W_GetAddr
	phx   				; src high
	pha   				; src low
	phx   				; dest high
	clc
	adc 	addbytes
	pha   				; dest low
	bcc 	@noinc
	inx
	txa
	sta 	3,s
@noinc	pea	0			; length high word - never more than 64k
	subword 	[BlockPtr2]:#W_BUsed,#W_BHeader,s
	_BlockMove   			; mem move

; Update all the pointers for the paragraphs which reference the text block we just made a gap in.
; ParPtr2 initially points to curpars text block.

	rcall	W_GetParRec,in=(W_LastP:a),out=(LastParPtr:ax)
@loop	cmpl 	[ParPtr2],BlockHand2
	jne 	GotRoom2
	ldy 	#W_pOffset
	addword 	[ParPtr2]:y,addbytes,[ParPtr2]:y
	addwl	#W_pBytes,ParPtr2
	cmpl 	LastParPtr,ParPtr2
	bcs 	@loop
	bra 	GotRoom2

; Need to create a new text block to hold the paragraphs that come after the one that we are trying
; to place a gap in (so it will have the text block to itself). If we get memory error then return
; it to the caller.

MakeNewOne2	movelong	BlockHand, W_CurrentBlockHandle
	stzl	W_CurrentBlockHandle2
	call	W_MakeEmptyBlock,out=(BlockHand2:l)
	jcs 	domemerr

; If we need more than 4k then we have to resize the empty block we just got. If resize fails or
; will generate a >64k text block then return with memory error. Note: don't want out of memory
; queue trying to shrink the handle we're trying to grow! If we can't resize the block make sure
; we dispose of it.

	cmpw 	addbytes,#W_StartBlockSize
	bcc 	GotRoom2
	movelong 	BlockHand2,W_CurrentBlockHandle	
	addword	addbytes,#W_GrowAmount,a	
	bcs	@killblock2
@growit	pea	0
	pha
	pushlong 	BlockHand2
	jsl 	D_GrowHandle
	bcc	@gotit
@killblock2	tool	_DisposeHandle,in=(BlockHand2:l)
	brl 	domemerr	

; Successfully resized the block - set its used and max counts.

@gotit	movelong 	[BlockHand2],BlockPtr2
	moveword 	addbytes,[BlockPtr2]:#W_BUsed
	addword 	a,#W_GrowAmount,[BlockPtr2]

; We can assume at this point that BlockHand2 can contain the paragraphs after par. Move the text for
; these paragraphs into BlockHand2. Addbytes = amount of text that needs to be moved.

GotRoom2	lda 	par
	inc 	a
	jsl 	W_GetParRec
	movelong 	ax,ParPtr

	movelong 	[ParPtr],BlockHand
	movelong 	[BlockHand],BlockPtr
	addlong 	[BlockHand2],#W_BHeader,LastParPtr

	lda 	par
	inc 	a
	ldx 	#0
	jsl 	W_GetAddr
	phx   				; src high
	pha   				; src low
	pushlong 	LastParPtr
	pushword 	#0   			; high, never moving more than 64k
	pushword 	addbytes
	_BlockMove   			; mem move

; Remove bytes from original text block used count and add to BlockHand2 used count.

	movelong 	[Blockhand2],BlockPtr2
	ldy 	#W_BUsed
	subword 	[BlockPtr]:y,addbytes,[BlockPtr]:y
	addword 	[BlockPtr2]:y,addbytes,[BlockPtr2]:y

; Get pointer to first paragraph that differed in text block ( or past last paragraph in document ).
; Get negative offset for paragraph's into new text block. For all paragraphs that came after par
; originally and are now in their own text block update their offsets. We have successfully
; split the block - now try to grow par's text block to the correct size.

	rcall	W_GetParRec,in=(CurPar:a),out=(LastParPtr:ax)
	subword 	#W_BHeader,[ParPtr]:#W_pOffset,addbytes

@loop	ldy 	#W_pOffset
	addword 	[ParPtr]:y,addbytes,[ParPtr]:y
	movelong 	BlockHand2,[ParPtr]
	addwl	#W_pBytes,ParPtr
	cmpl 	ParPtr,LastParPtr
	bcc 	@loop
	brl 	TryAgain

domemerr	moveword	#$201,err
exit	stzl	W_CurrentBlockHandle
	stzl	W_CurrentBlockHandle2
	return

W_CurrentBlockHandle	DS.L	1
W_CurrentBlockHandle2	DS.L	1

	ENDP

*****************************************************************************************************
; W_LessRoom ( Paragraph:word, Offset:word, Bytes:word )
;
;   This routine is called to remove the given number of bytes at the given offset in the given
; paragraph's text block.
;
; Note: Always returns with no error (carry clear).

W_LessRoom	PROC	EXPORT
	;Using 	wpglobals
	;Using 	W_ParData
	
	input 	par:w,offset:w,bytes:w
	local 	BlockPtr:l,BlockHand:l,newsize:w,ParPtr:l,LastParPtr:l
	local 	used:w,temp:l,ParOffset:w
	error	err
	begin

; Deleting text so set file changed status. Get pointer to text block for paragraph.

	stz	err
	call	D_SetFileChanged,in=(W_CurDoc:l)
	rcall	W_GetParRec,in=(par:a),out=(ParPtr:ax)
	movelong 	[ParPtr],BlockHand
	moveword 	[ParPtr]:#W_pOffset,ParOffset
	movelong 	[BlockHand],BlockPtr

; Subtract bytes from used count for text block.

	ldy 	#W_BUsed
	lda 	[BlockPtr],y
	sta 	used
	sec
	sbc 	bytes
	sta 	newsize

	rcall	W_GetAddr,in=(par:a,offset:x),out=(temp:ax)
	clc
	adc 	bytes
	bcc 	@noinc
	inx
@noinc	phx   				; source
	pha
	pushlong 	temp			; dest
	pushword 	#0   			; high, never moving more than 64k
	lda 	used
	sec
	sbc 	offset
	sbc 	bytes
	sbc 	ParOffset
	pha
	_BlockMove   			; mem move

; Update used count in text block.

	moveword 	newsize,[BlockPtr]:#W_BUsed	
	
; Update all paragraph offsets for latter paragraphs that share the same text block.

	rcall	W_GetParRec,in=(W_LastP:a),out=(LastParPtr:ax)
	bra 	@check

@loop	cmpl 	[ParPtr],BlockHand
	bne 	@endloop
	ldy 	#W_pOffset
	subword 	[ParPtr]:y,bytes,[ParPtr]:y
@check	addwl	#W_pBytes,ParPtr
	cmpl 	LastParPtr,ParPtr
	bcs 	@loop
@endloop

exit	return

	ENDP

*****************************************************************************************************
; W_NewRuler ( ): RulerHandle:long
;
;   This routine is called to allocate a new standard ruler and initialize its used count to zero.
;
; Note: can return memory errors.

W_NewRuler	PROC	EXPORT
	;Using 	W_RulerData
	;Using 	WPGlobals

	output 	DogRuler:l
	local 	ptr:l
	error	err
	begin

	call	D_NeedHand,in=(#W_RulBytes:l),out=(DogRuler:l),err=err
	bcs	@exit
	tool	_PtrToHand,in=(#StdRuler:l,DogRuler:l,#W_RulBytes:l)
	movelong 	[DogRuler],ptr
	moveword 	#0,[ptr]	

@exit	return

	ENDP

*****************************************************************************************************
; W_ReadRuler ( RulerHandle:ax )
;
;   This routine is called to move the given ruler's information into the WP current ruler globals.

W_ReadRuler	PROC	EXPORT
	;Using 	WPGlobals

	tool	_HandToPtr,in=(ax:l,#W_WPRuler:l,#W_RulBytes:l)
	rtl

	ENDP

*****************************************************************************************************
; W_ReadRulerP ( ParagraphPtr:ax )
;
;   This routine is called to move the given paragraph's ruler information into the WP current 
; ruler globals.

W_ReadRulerP PROC	EXPORT
	;Using 	D_GlobalData
	;Using 	WPGlobals
	
ParRec	equ 0

	tay
	phd
	lda 	>D_MainZPage
	tcd
	sty 	ParRec
	stx 	ParRec+2
	ldy 	#W_pRulerHand+2
	lda 	[ParRec],y
	tax
	ldy 	#W_pRulerHand
	lda 	[ParRec],y
	jsl 	W_ReadRuler
	pld
	rtl

	ENDP

*****************************************************************************************************
; W_UseRuler ( Count:word, RulerHand:long )
;
;   This routine is called to increment the ruler's used count. This routine should be called
; whenever a paragraph is using the ruler.

W_UseRuler	PROC	EXPORT

	input 	times:w,ruler:l
	local 	ptr:l
	begin

	movelong 	[ruler],ptr
	addword 	times,[ptr],[ptr]

	return

	ENDP

*****************************************************************************************************
; W_UnUseRuler ( Count:word, RulerHand:long )
;
;   This routine is called to decrement the ruler's used count. This routine should be called
; whenever a paragraph is no longer using a ruler.

W_UnUseRuler PROC	EXPORT
	input 	times:w,ruler:l
	local 	ptr:l
	begin

	movelong 	[ruler],ptr
	subword 	[ptr],times,[ptr]
	beq 	@killit
	bpl 	@exit
@killit	tool	_DisposeHandle,in=(ruler:l)

@exit	return

	ENDP

*****************************************************************************************************
; W_FindLine ( Paragraph:word, Offset:word ): Line:word
;
;   This routine is called to return the line in the paragraph corresponding to the given offset
; into the paragraph.

W_FindLine	PROC	EXPORT

	input 	par:w,offset:w
	local 	handle:l,ptr:l,ParRec:l,LinePtr:l,Ptr2:l
	output 	line:w
	begin

; Get line handle for paragraph. If no line handle exits then create valid line handles for the
; paragraph. If we can't make lines for paragraph then return with line set to last line and hope
; it's good enough.

	rcall	W_GetParRec,in=(par:a),out=(ParRec:ax)
	movelong 	[ParRec]:#W_pLineHand,handle
	cpzl 	handle
	bne 	@notNIL

@makelines	call	W_ValidLines,in=(par:w)
	bcc	@gotlines
	moveword	[ParRec]:#W_pLastLine,line
	dec	line
	brl	exit

; If line handle has been purged then try to make lines.

@gotlines	movelong 	[ParRec]:#W_pLineHand,handle
@notNIL	movelong 	[handle],ptr
	cpzl 	ptr
	beq 	@makelines

; Loop through all lines for the paragraph comparing the given offset against them to determine in
; which line the offset falls.

	stz 	line
@loop	inc	line
	rcall	W_GetLineRec,in=(ptr:ax,line:y),out=(lineptr:ax)

	cmpw 	offset,[LinePtr]:#W_lOffset
	beq 	@checktab
	bcs 	@loop
	bra 	@foundline

@checktab	rcall	W_GetAddr,in=(par:a,offset:x),out=(ptr2:ax)
	cmpb 	[Ptr2],#9
	bne	@loop

@foundline	dec 	line
	cmpw 	[ParRec]:#W_pLastLine,line
	beq 	@skip2
	bcs 	exit

@skip2	dec 	a
	sta 	line

exit	return

	ENDP

*****************************************************************************************************
; W_MakeCR ( Paragraph:word, Line:word, Offset:word )
;
;   This routine is called to add a carriage return at the given offset in the given paragraph/line.
; This will create a new paragraph. The method used to insert the paragraph is as follows:
;
; 1. Get current font information
; 2. MakeRoom in paragraph at the given offset to insert a new text header + 1 carriage return char.
; 3. Put the current font information into the gap we made.
; 4. Make a new paragraph entry after the given paragraph.
; 5. Setup the new paragraph's offset.
; 6. MakeLines for both new paragraphs.
;
; Note: This routine can return memory errors.

W_MakeCr	PROC	EXPORT
	;Using 	wpglobals
	;Using 	W_ParData

	input 	par:w,line:w,offset:w
	local 	font:w,style:w,color:w,ptr:l,ParRec:l
	local 	ParRec2:l
	error	err
	begin

; Determine current font information at place we want to insert CR. Make room for new carriage return
; and text header in text block. If memory error then exit with error.

	call	W_FindFont,in=(par:w,line:w,offset:w),out=(style:w,font:w,color:w)
	call	W_MakeRoom,in=(par:w,offset:w,#W_TextHeader+1:w),err=err
	bcs	@goexit

; Made the gap. Now want to make sure we will be able to grow the paragraph array. Try to create new
; paragraph entry. If we can't then remove the gap and return with a memory error.

	call	W_MakeNewParRec,in=(par:w),err=err
	bcc	@gotpar
	call	W_LessRoom,in=(par:w,offset:w,#W_TextHeader+1:w)
@goexit	brl	exit

; Now get pointer to where we want CR and text header to start. Insert CR and text header.

@gotpar	rcall	W_GetAddr,in=(par:a,offset:x),out=(ptr:ax)	
	moveword 	#cr,[ptr]
	moveword 	font,[ptr]:#1
	moveword 	style,[ptr]:#3
	moveword 	color,[ptr]:#5

; Get pointers to par and new par we added.

	rcall	W_GetParRec,in=(par:a),out=(ParRec:ax)
	lda 	par
	inc 	a
	jsl 	W_GetParRec
	movelong 	ax,ParRec2

; New paragraph's offset is going to be 1 past the CR we inserted @ offset in par.

	moveword 	[ParRec]:#W_pOffset,a
	sec				; inc 1 past the cr
	adc 	offset
	sta 	[ParRec2],y

; Make lines for old and new paragraphs.

	call	W_MakeLines,in=(par:w)
	lda 	par
	inc 	a
	pha
	jsl 	W_MakeLines

exit	return

	ENDP

*****************************************************************************************************
; W_MakeNewParRec ( Paragraph:word )
;
;   This routine is called to create a new paragraph record in the paragraph array - after the given
; paragraph. The new paragraph entry is initialized as follows:
;
; 	TextBlock 	- same as Paragraph
;	offset	- ???
;	Attr	- 0
;	RulerHandle	- same as Paragraph
;	Pixels	- 0
;	LineHandle	- NIL
;	LastLine	- 0
;
; Note: Memory errors are returned!

W_MakeNewParRec PROC	EXPORT
	;Using 	wpglobals
	
ParIncment 	equ 10

	input 	par:w
	local 	Src:l,Dest:l,Length:l,NewMax:w,NewSize:l
	error	err
	begin

; Check if paragraph array has enough room to just add entry. If not try to grow paragraph array to
; new size. If memory error then exit with error globals intact; else update W_MaxP.

	stz	err
	cmpw 	W_LastP,W_MaxP
	bcc 	@hasroom
	adc 	#ParIncment-1
	sta 	NewMax

	tool	_Multiply,in=(NewMax:w,#W_pBytes:w),out=(NewSize:l)
	call	D_GrowLHandle,in=(NewSize:l,W_PHandle:l),out=(W_PPtr:l),err=err
	jcs	exit
	moveword	NewMax,W_MaxP

; Enough room now exists in paragraph array handle to add extra paragraph entry. Get pointers to
; par and par+1. Copy par through last par contents up one paragraph entry.

@hasroom	rcall	W_GetParRec,in=(par:a),out=(src:ax)
	lda 	par
	inc 	a
	jsl 	W_GetParRec
	movelong 	ax,Dest

	inc 	W_LastP
	rcall	W_GetParRec,in=(W_LastP:a),out=(length:ax)
	cmpw	par,W_LastP
	beq	@nomove

	pushlong 	Src
	pushlong 	Dest
	sublong 	Length,Src,s
	_BlockMove

; Initialize other fields of new paragraph record. Note: TextBlock and RulerHandles will have been
; inherited by the copy.

@nomove	lda	#0
	moveword	a,[Dest]:#W_pAttr
	moveword	a,[Dest]:#W_pLastLine
	moveword	a,[Dest]:#W_pPixels
	moveword	a,[Dest]:#W_pLineHand
	moveword	a,[Dest]:#W_pLineHand+2
	call	W_UseRuler,in=(#1:w,[Dest]:#W_pRulerHand:l)

exit	return

	ENDP

*****************************************************************************************************
; W_ParLock ( Paragraph:word )
;
;   This routine is called to lock the text block and line handles for the given paragraph.

W_ParLock	PROC	EXPORT

	input 	par:w
	local 	ParRec:l
	begin

	rcall	W_GetParRec,in=(par:a),out=(ParRec:ax)
	tool	_HLock,in=([ParRec]:#W_pBlockHand:l)
	tool	_HLock,in=([ParRec]:#W_pLineHand:l)

	return

	ENDP

*****************************************************************************************************
; W_ParUnLock ( Paragraph:word )
;
;   This routine is called to unlock the text block and line handles for the given paragraph.

W_ParUnLock	PROC	EXPORT

	input 	par:w
	local 	ParRec:l
	begin

	rcall	W_GetParRec,in=(par:a),out=(ParRec:ax)
	tool	_HUnLock,in=([ParRec]:#W_pBlockHand:l)
	tool	_HUnLock,in=([ParRec]:#W_pLineHand:l)

	return

	ENDP

*****************************************************************************************************
; W_GetLastLine ( Paragraph:a ):Line:a
;
;   This routine is called to return the last line for the given paragraph.

W_GetLastLine PROC	EXPORT

	pha
	pha
	pha
	jsl 	W_GetLastLGuts
	plx
	pla
	rtl
	
	ENDP

*****************************************************************************************************
; W_GetLastLGuts ( Paragraph:word ): Line:word, Offset:word
;
;   This routine is called to return the last line for the given paragraph and its offset in the
; paragraph's text block.

W_GetLastLGuts PROC	EXPORT
	;Using 	wpglobals
	;Using 	D_GlobalData

	input 	par:w
	output 	line:w,offset:w
	local 	ptr:l,LinePtr:l
	begin

	rcall	W_GetParRec,in=(Par:a),out=(Ptr:ax)
	moveword 	[ptr]:#W_pLastLine,line
	call	W_GetLRecPtr,in=(par:w,line:w),out=(lineptr:l)
	moveword 	[LinePtr]:#W_lOffset,offset
	dec	line

	return

	ENDP

*****************************************************************************************************
; W_GetEndPar ( Paragraph:word ): Bytes:word
;
;   This routine is called to return the number of bytes in the given paragraph.

W_GetEndPar	PROC	EXPORT
	;Using 	wpglobals

	input 	par:w
	local 	ParRec:l,Block:l,ptr:l
	output 	bytes:w
	begin

	rcall	W_GetParRec,in=(par:a),out=(parrec:ax)
	movelong 	[ParRec],Block
	movelong 	[Block],ptr
	moveword 	[ptr]:#W_BUsed,bytes

	cmpw 	par,W_LastP
	bcs 	@GotBytes
	cmpl 	[ParRec],[ParRec]:#W_pBytes
	bne 	@GotBytes
	moveword 	[ParRec]:#W_pOffset+W_pBytes,bytes

@GotBytes	subword 	bytes,[ParRec]:#W_pOffset,bytes
	dec 	bytes

	return

	ENDP

*****************************************************************************************************
; W_DelPars ( Par1:word, Par2:word )
;
;   This routine is called to delete the range of paragraphs between 1 & 2 inclusive.

W_DelPars	PROC	EXPORT
	;Using 	wpglobals

	input 	par1:w,par2:w
	local 	EndPtr:l,EP1Ptr:l,bytes:l,Pm1Ptr:l,CurPar:l,BPtr:l
	local 	FirstBlock:l,LastBlock:l,OldBlock:l,ParRec:l
	local 	ptr:l,BegOffset:w,EndOffset:w,LastParPtr:l
	begin

; Lock down paragraph array handle and get pointer to its end.

	rcall	D_Deref,in=(W_PHandle:ax)
	lda 	W_LastP
	inc 	a
	jsl 	W_GetParRec
	movelong 	ax,EndPtr

; Get pointers to paragraph before par1 and paragraph after par2.

	lda 	par1
	dec 	a
	jsl 	W_GetParRec
	movelong 	ax,Pm1Ptr

	lda 	par2
	inc 	a
	jsl 	W_GetParRec
	movelong 	ax,EP1Ptr

; Initialize first and last text blocks for loop.

	movelong 	[Pm1Ptr],FirstBlock
	stzl 	LastBlock
	cmpw 	W_LastP,par2
	beq 	@GotLast
	movelong 	[EP1Ptr],LastBlock

@GotLast	moveword 	par1,CurPar
	stzl 	OldBlock

PLoop	rcall	W_GetParRec,in=(CurPar:a),out=(ParRec:ax)
	cmpw 	[ParRec]:#W_pAttr,#W_PgBrk
	beq 	NextPar

	call	W_UnUseRuler,in=(#1:w,[ParRec]:#W_pRulerHand:l)
	cpzl 	[ParRec]:#W_pLineHand
	beq 	NoKill
	tool	_DisposeHandle,in=([ParRec]:#W_pLineHand:l)

NoKill	cmpl 	[ParRec],OldBlock
	beq 	NextPar
	movelong 	[ParRec],OldBlock
	cmpl 	OldBlock,FirstBlock
	beq 	NextPar
	cmpl 	OldBlock,LastBlock
	beq 	NextPar
	tool	_DisposeHandle,in=(OldBlock:l)

NextPar	inc 	CurPar
	cmpw 	par2,CurPar
	jge 	PLoop

; now am done with all the in between pars, work on extremes

	movelong 	[FirstBlock],BPtr
	cmpl 	FirstBlock,LastBlock
	bne 	NotSame

	moveword 	[EP1Ptr]:#W_pOffset,EndOffset
	moveword 	[PM1Ptr]:#W_pBytes+W_pOffset,BegOffset
	bra 	GotBOffset

NotSame	cmpl 	[PM1Ptr],[PM1Ptr]:#W_pBytes
	bne 	NoZap
	moveword 	[PM1Ptr]:#W_pBytes+W_pOffset,[BPtr]:#W_BUsed

NoZap	moveword 	#W_BHeader,BegOffset
	cpzl 	LastBlock
	jeq 	NoMove
	rcall	W_GetParRec,in=(par2:a),out=(parrec:ax)
	cmpl 	[ParRec],[EP1Ptr]
	jne 	DoArray
	movelong 	[LastBlock],BPtr
	moveword 	[EP1Ptr]:#W_pOffset,EndOffset

GotBOffset	ldx 	BPtr+2
	addword 	EndOffset,Bptr,a
	bcc 	NoIx
	inx
NoIX	phx
	pha
	ldx 	BPtr+2
	addword 	BegOffset,Bptr,a
	bcc 	NoIx2
	inx
NoIX2	phx
	pha
	pushword 	#0
	subword 	[Bptr]:#W_BUsed,EndOffset,s
	_BlockMove

	subword 	EndOffset,BegOffset,bytes
	subword 	[BPtr]:#W_BUsed,bytes,[BPtr]:#W_BUsed
	movelong 	EP1Ptr,ParRec
	
; Now update all relevant ptrs.

	rcall	W_GetParRec,in=(W_LastP:a),out=(LastParPtr:ax)
	bra 	@check
@loop	cmpl 	[ParRec],LastBlock
	bne 	@endloop

	ldy 	#W_pOffset
	subword 	[ParRec]:y,bytes,[ParRec]:y
	addwl	#W_pBytes,ParRec
@check	cmpl 	LastParPtr,ParRec
	bge 	@loop
@endloop

; Now adjust the par array

DoArray	sublong EndPtr,EP1Ptr,bytes

	pushlong 	EP1Ptr
	lda 	par1
	jsl 	W_GetParRec
	phx
	pha
	pushlong 	bytes
	_BlockMove

; W_LastP - (par2-par1+1)

NoMove	lda 	W_LastP
	clc
	sbc 	par2
	clc
	adc 	par1
	sta 	W_LastP

	return

	ENDP


*****************************************************************************************************
; W_CheckWidow ( Paragraph:a )
;
;   This routine is called to check if the given paragraph can be widowed or not. The carry will be
; set on return if widow is set in the ruler bits of the paragraph's ruler.

W_CheckWidow PROC	EXPORT
	;Using 	wpglobals
	;Using 	D_GlobalData

handle	equ 	0
ptr	equ 	4

	tay
	phd
	lda 	>D_MainZPage
	tcd
	clc
	lda 	W_Stuff
	bne 	@exit

	tya
	jsl 	W_GetParRec
	jsl 	W_ReadRulerP

	lda 	W_RulBits
	and 	#W_r_widow
	clc
	beq 	@exit
	sec
@exit	pld
	rtl

	ENDP
	END

