.include "cursor.inc"
.include "debug.inc"
.include "draw.inc"
.include "edit.inc"
.include "errors.inc"
.include "finalex.inc"
.include "irq.inc"
.include "macros.inc"
.include "memory.inc"
.include "ram.inc"
.include "string.inc"
.include "strings.inc"
.include "util.inc"
.include "zeropage.inc"

;******************************************************************************
; CONSTANTS
MAX_SOURCES=8		; number of source buffers that can be loaded at once
GAPSIZE = $100		; size of gap in gap buffer
POS_STACK_SIZE = 32 	; size of source position stack
MAX_BUFFER_NAME_LEN=16  ; max length of names for each buffer

;******************************************************************************
; FLAGS
FLAG_DIRTY = 1

.BSS

;******************************************************************************
data_start:
sp: 	   .byte 0		; stack pointer for source position stack
stack:     .res POS_STACK_SIZE	; stack for source positions

;******************************************************************************
; BUFFSTATE
; This block of zeropage variables are stored in the order they are
; enumerated below.  When a source buffer is activated, the state for that
; buffer is copied to these zeropage locations.
; The values for the buffer that is being deactivated are copied to the
; "savestate" array
buffstate   = zp::srccur
cursorzp    = zp::srccur
poststartzp = zp::srccur2
line        = zp::srcline
lines       = zp::srclines
end         = zp::srcend
SAVESTATE_SIZE = 10		; space used by above zeropage addresses

.exportzp __src_line
__src_line = line
.exportzp __src_lines
__src_lines = lines

;******************************************************************************
; SAVESTATE
; This buffer holds the "buffer state" for each source buffer. See BUFFSTATE
savestate:  .res MAX_SOURCES*10	; 10 bytes: curl, curr, line, lines, end

.export __src_names
__src_names:
names:	   .res MAX_SOURCES*MAX_BUFFER_NAME_LEN

;******************************************************************************
.export __src_numbuffers
__src_numbuffers:
numsrcs:    .byte 0		; number of buffers
.export __src_activebuff
__src_activebuff:
activesrc:  .byte 0		; index of active buffer (also bank offset)

bank:	    .byte 0
buffs_curx: .res MAX_SOURCES	; cursor X positions for each inactive buffer
buffs_cury: .res MAX_SOURCES	; cursor Y positions for each inactive buffer
banks:      .res MAX_SOURCES	; the corresponding bank for each buffer

flags:	.res MAX_SOURCES	; flags for each source buffer
;******************************************************************************

;******************************************************************************
; DATA
; This buffer holds the text data.  It is a large contiguous chunk of memory
.segment "SOURCE"
.assert * & $ff = 0, error, "source buffers must be page aligned"
data: .res $6000

.CODE
;******************************************************************************
; SAVE
; Backs up the pointers for the active source so that they may be set for
; another source
.export __src_save
.proc __src_save
@save=r0
	; save the cursor position in the current buffer
	ldx activesrc

	lda zp::curx
	sta buffs_curx,x
	lda zp::cury
	sta buffs_cury,x

	; save the bank
	lda bank
	sta banks,x

	; save the data for the source we're switching from
	txa
	asl		; *2
	asl		; *4
	adc activesrc	; *5
	asl		; *10
	tay

	; save the buffer state
	ldx #$00
@l0:	lda buffstate,x
	sta savestate,y
	iny
	inx
	cpx #SAVESTATE_SIZE
	bne @l0

	rts
.endproc

;******************************************************************************
; SET
; Sets the active source to the source in the given ID.
; IN:
;  - .A: ID of the source buffer we're switching to
; OUT:
;  - .C: set if the buffer could not be switched to
.export __src_set
.proc __src_set
@save=r0
	; set the pointers to those of the source we're switching to
	cmp numsrcs
	bcc @ok
	rts		; buffer doesn't exist; return with .C set

@ok:	tax

	sta activesrc
	asl		; *2
	asl		; *4
	adc activesrc	; *5
	asl		; *10
	tay

	; set the active bank ID
	lda banks,x
	sta bank

	; set the cursor position in the new source
	lda buffs_cury,x
	sta zp::cury
	lda buffs_curx,x
	sta zp::curx

	; restore the state for this buffer
	ldx #$00
@l0:	lda savestate,y
	sta buffstate,x
	iny
	inx
	cpx #SAVESTATE_SIZE
	bne @l0

	RETURN_OK
.endproc

;******************************************************************************
; FIND_BANK
; Returns the ID of a free bank to store source in
; OUT:
;  - .A: the next available ID
;  - .C: set if no open bank was found
.proc find_bank
@free=r0
	lda #FINAL_BANK_SOURCE0
@l0:	ldx #$00
	stx @free
@l1:	cmp banks,x
	bne :+
	inc @free		; flag NOT free
:	inx
	cpx #MAX_SOURCES
	bne @l1
	ldx @free
	beq @found
	clc
	adc #$01
	cmp #FINAL_BANK_SOURCE0+MAX_SOURCES
	bcc @l0
	sec		; no open banks
	rts

@found:	RETURN_OK
.endproc

;******************************************************************************
; NEW
; Initializes a new source buffer and sets it as the current buffer
; OUT:
;  - .C: set if the source could not be initialized (e.g. too many open
;        sources)
.export __src_new
.proc __src_new
	ldx numsrcs
	beq @init
	inx
	cpx #MAX_SOURCES
	bcc @saveold
	rts		; err, too many sources

@saveold:
	lda numsrcs
	jsr __src_save	; save current source data
@init:
	; clear the state for the new buffer
	ldx #SAVESTATE_SIZE-1
	lda #$00
:	sta buffstate-1,x
	dex
	bne :-

	lda #<data
	sta cursorzp
	lda #>data
	sta cursorzp+1

	lda #<(data+GAPSIZE)
	sta end
	sta poststartzp
	lda #>(data+GAPSIZE)
	sta end+1
	sta poststartzp+1

	; init line and lines to 1
	inc line
	inc lines

	lda numsrcs
	sta activesrc
	inc numsrcs
	pha

	; set name to 0 (unnamed)
	asl
	asl
	asl
	asl
	tax
	lda #$00
	sta names,x

	; mark the buffer as clean
	pla
	tax
	lda #$00
	sta flags,x

	jsr find_bank
	sta bank

	rts
.endproc

;******************************************************************************
; BUFFER_BY_NAME
; Returns the ID of the buffer associated with the given filename. The carry
; is set if no buffer by the given name exists.
; IN:
;  - .XY: the name of the buffer to search for
; OUT:
;  - .A: the ID of the buffer; use src::set to make it the active buffer
;  - .C: set if no buffer was found by the given name
.export __src_buffer_by_name
.proc __src_buffer_by_name
@name=zp::str0
@other=zp::str2
@names=r0
@cnt=r2
@len=r3
	jsr str::len	; sets @name (str0) to .XY
	sta @len

	ldxy #names
	stxy @names
	lda #$ff
	sta @cnt

@l0:	inc @cnt
	lda @cnt
	cmp numsrcs
	bcs @notfound
	ldxy @names
	stxy @other
	lda @len
	jsr str::compare
	php
	lda @names
	clc
	adc #16
	sta @names
	bcc :+
	inc @names+1
:	plp
	bne @l0
	lda @cnt
	clc		; flag as FOUND
@notfound:
	rts
.endproc

;******************************************************************************
; CURRENT_FILENAME
; Returns the filename for the active buffer
; OUT:
;  - .XY: the filename of the buffer or [NO NAME] if it has no name
;  - .C:  set if the file has no name ([NO NAME])
; CLOBBERS:
;  - r0-r1
.export __src_current_filename
.proc __src_current_filename
	lda __src_activebuff

	; fall through to __src_get_filename
.endproc

;******************************************************************************
; GET_FILENAME
; Returns the filename for the given buffer
; IN:
;  - .A: the id of the buffer to get the name of
; OUT:
;  - .XY: the filename of the buffer or [NO NAME] if it has no name
;  - r0:  the filename (same as .XY)
;  - .C:  set if the file has no name ([NO NAME])
; CLOBBERS:
;  - r0-r1
.export __src_get_filename
.proc __src_get_filename
@out=r0
	asl			; * 16
	asl
	asl
	asl
	tax
	lda __src_names,x
	bne @named

@noname:
	ldxy #strings::noname
	RETURN_ERR ERR_UNNAMED_BUFFER

@named:	txa
	adc #<__src_names
	tax
	lda #>__src_names
	adc #$00
	tay
	RETURN_OK
.endproc

;******************************************************************************
; ISDIRTY
; Returns .Z clear if the active buffer is dirty (has changed since it was last
; marked !DIRTY)
; OUT:
;  - .Z: clear if the buffer has changed since last flagged clean
.export __src_isdirty
.proc __src_isdirty
	ldy activesrc
	lda flags,y
	and #FLAG_DIRTY
	rts
.endproc

;******************************************************************************
; ANYDIRTY
; Returns .Z clear if ANY buffer is dirty (has changed since it was last
; marked !DIRTY)
; OUT:
;  - .Z: clear if any buffer has changed since last flagged clean
.export __src_anydirty
.proc __src_anydirty
	ldy numsrcs
:	lda flags-1,y
	and #FLAG_DIRTY
	bne @done
	dey
	bne :-
@done:	rts
.endproc

;******************************************************************************
; GET_FLAGS
; Returns the flags for the requested buffer
; IN:
;  - .A: the buffer to get flags for
; OUT:
;  - .A: the flags for the active buffer
.export __src_getflags
.proc __src_getflags
	tay
	lda flags,y
	rts
.endproc

;******************************************************************************
; SET_FLAGS
; Sets the flags for the active source buffer
; IN:
;  - .A: the flags to set on the active source buffer
.export __src_setflags
.proc __src_setflags
	ldy activesrc
	sta flags,y
	rts
.endproc

;******************************************************************************
; CLOSE
; Closes the active buffer. If the buffer being closed is the only one open,
; initializes a new buffer and makes it the active buffer.
.export __src_close
.proc __src_close
@cnt=r0
@end=r0
; get number of last byte to move (num_buffers)*2
	lda numsrcs
	bne :+
	rts		; no buffer to close
:	cmp #$01
	bne @close

	; only one buffer open; re-initialize it to "close" it
	dec numsrcs	; set num sources back to 0
	jmp __src_new	; and initialize the buffer

@close:
	; get the number of buffers to shift (numsrcs - activesrc - 1)
	lda numsrcs
	sbc #$01	; .C is set
	sbc activesrc
	beq @cont	; if this was the last buffer, skip shifting
	sta @cnt
	pha		; save this count for moving names

	; get offset to start shifting at
	lda activesrc
	asl		; *2
	asl		; *4
	adc activesrc	; *5
	asl		; *10
	tax

@l0:	; copy all the buffers' data down
	ldy #SAVESTATE_SIZE
:	lda savestate+SAVESTATE_SIZE,x
	sta savestate,x
	inx
	dey
	bne :-
	dec @cnt
	bne @l0

	; copy cursor data and banks down
	ldx activesrc
	inx
@l1:	lda banks,x
	sta banks-1,x
	lda buffs_curx,x
	sta buffs_curx-1,x
	lda buffs_cury,x
	sta buffs_cury-1,x
	inx
	cpx numsrcs
	bne @l1

	; copy the names down
	lda activesrc
	asl		; *2
	asl		; *4
	asl		; *8
	asl		; *16
	tax

	pla
	sta @cnt

@l2:	ldy #MAX_BUFFER_NAME_LEN
:	lda names+MAX_BUFFER_NAME_LEN,x
	sta names,x
	inx
	dey
	bne :-
	dec @cnt
	bne @l2

@cont:	; if there is no next buffer, open the previous
	dec numsrcs
	lda activesrc
	cmp numsrcs
	bcc :+
	dec activesrc
:	lda activesrc
	jmp __src_set
.endproc

;******************************************************************************
; NAME
; Sets the name for the active buffer
; IN:
;  - .XY: the 0-terminated name to set for the active buffer
.export __src_name
.proc __src_name
@name=r0
	stxy @name
	lda activesrc
	asl
	asl
	asl
	asl			; *16
	tax
	ldy #$00
@l0:	lda (@name),y
	sta names,x
	beq @done
	inx
	iny
	cpy #MAX_BUFFER_NAME_LEN
	bne @l0
@done:	rts
.endproc

;******************************************************************************
; PUSHP
; Pushes the current source position to an internal stack.
; OUT:
;   - .C: set on error (the stack is full)
.export __src_pushp
.proc __src_pushp
	lda sp
	cmp #POS_STACK_SIZE-1
	bcc :+
	RETURN_ERR ERR_STACK_OVERFLOW

:	asl
	tax
	inc sp
	lda cursorzp
	sta stack,x
	lda cursorzp+1
	sta stack+1,x
	RETURN_OK
.endproc

;******************************************************************************
; POPP
; Returns the the most recent source position pushed in .YX
; OUT:
;   - .XY: the most recently pushed source position
;   - .C: set on error (the stack is empty)
.export __src_popp
.proc __src_popp
	lda sp
	bne :+
	RETURN_ERR ERR_STACK_UNDERFLOW

:	dec sp
	lda sp
	asl
	tax
	ldy stack+1,x
	lda stack,x
	tax
	RETURN_OK
.endproc

;******************************************************************************
; CURR LINE
; Returns the line number that the source cursor is on
; OUT:
;   - .XY: the current line
.export __src_currline
.proc __src_currline
	ldxy line
	rts
.endproc

;******************************************************************************
; END
; Returns .Z set if the cursor is at the end of the buffer.
; OUT:
;  - .Z: set if the cursor is at the end of the buffer
.export __src_end
.proc __src_end
	ldx poststartzp
	cpx end
	bne @done
	ldx poststartzp+1
	cpx end+1
@done:	rts
.endproc

;******************************************************************************
; END_REP
; Returns .Z set if the current or next cursor position is at the end of the
; buffer.
; OUT:
;  - .Z: set if the cursor is at the end of the buffer
.export __src_end_rep
.proc __src_end_rep
	ldxy end
	sub16 poststartzp
	cpy #$00
	bne @done
	cpx #$00
	beq @done
	cpx #1		; set .Z if LSB is 1
@done:	rts
.endproc

;******************************************************************************
; BEFORE END
; Checks if the source cursor is located just before the end of the buffer.
; OUT:
;  - .Z: set if the cursor is before the end of the buffer
.export __src_before_end
.proc __src_before_end
	ldxy end
	sub16 poststartzp
	cmpw #1
	rts
.endproc

;******************************************************************************
; POS
; Returns the current source position.  You may go to this position with the
; src::goto routine.  Note that if the source changes since this procedure is
; called, this may not be the same (or expected) position
; OUT:
;  - .XY: the current source position
.export __src_pos
.proc __src_pos
	ldxy cursorzp
	rts
.endproc

;******************************************************************************
; START
; Returns .Z set if the cursor is at the start of the buffer.
; OUT:
;  - .Z: set if the cursor is at the start of the buffer
.export __src_start
.proc __src_start
	ldx cursorzp
	bne @done	; if LSB is !0, not the start
	ldx cursorzp+1
	cpx #>data
@done:	rts
.endproc

;******************************************************************************
; BACKSPACE
; Deletes the character immediately before the current cursor position.
; OUT:
;  - .A: the character that was deleted
;  - .C: set if the backspace failed (we're at the START of the source)
.export __src_backspace
.proc __src_backspace
	jsr mark_dirty
	jsr __src_start
	beq @skip
	jsr atcursor
	pha
	cmp #$0d
	bne :+
	decw line
	jsr on_line_deleted
:	decw cursorzp
	pla
	clc
	rts
@skip:	sec
	rts
.endproc

;******************************************************************************
; ON LINE INSERTED
; Callback to handle a line insertion. Various state needs to be shifted when
; this occurs (breakpoints, etc.)
; TODO:
;   - update label addresses after PC
;   - update line program (insert new instruction to inc line)
.proc on_line_inserted
	incw lines

	; shift breakpoints
	jsr edit::currentfile
	sta r0
	lda #$01
	jmp dbg::shift_breakpointsd
.endproc

;******************************************************************************
; ON LINE DELETED
; Callback to handle a line deletion. Various state needs to be shifted when
; this occurs (breakpoints for now, TODO: symbols)
.proc on_line_deleted
	decw lines

	; shift breakpoints
	jsr edit::currentfile
	sta r0
	lda #$01
	jmp dbg::shift_breakpointsu
.endproc

;******************************************************************************
; DELETE
; Deletes the character at the current cursor position.
; OUT:
;  - .A: the character that was deleted
;  - .C: set if there is nothing to delete (cursor is at END)
.export __src_delete
.proc __src_delete
	jsr mark_dirty
	jsr __src_end
	beq @skip
	jsr __src_after_cursor
	cmp #$0d
	bne :+
	jsr on_line_deleted
:	incw poststartzp
	clc
	rts
@skip:	sec
	rts
.endproc

.PUSHSEG
.segment "BANKCODE2"
;******************************************************************************
; NEXT
; Moves the cursor up one character in the gap buffer
; OUT:
;  - .A: the character at the new cursor position in .A
.export __src_next
.proc __src_next
	; do __src_end inline to save the cycles from JSR and RTS
	ldx poststartzp
	cpx end
	bne @cont
	ldx poststartzp+1
	cpx end+1
	beq @done

@cont:	; switch to the bank that contains the source buffer's data
	lda bank
	sta $9c02

	; move one byte from the end of the gap to the start
	ldy #$00
	lda (poststartzp),y
	sta (cursorzp),y

	incw cursorzp
	incw poststartzp

	; switch back to main bank
	ldx #FINAL_BANK_MAIN
	stx $9c02

	cmp #$0d
	bne @done
	incw line
@done:	RETURN_OK
.endproc

;******************************************************************************
; PREV
; Moves the cursor back one character in the gap buffer.
; OUT:
;  - .A: the character at the new cursor position (if not at the start of buff)
;  - .C: set if we're at the start of the buffer and couldn't move back
.export __src_prev
.proc __src_prev
	jsr __src_start
	bne :+
	jsr atcursor
	sec
	rts

:	; move char from start of gap to the end of the gap
	decw cursorzp
	decw poststartzp

	; switch to the bank that contains the source buffer's data
	lda bank
	sta $9c02

	; move one byte from the start of the gap to the end
	ldy #$00
	lda (cursorzp),y
	sta (poststartzp),y

	cmp #$0d
	bne :+
	decw line

:	; get the character at the new cursor position
	decw cursorzp
	lda (cursorzp),y
	incw cursorzp

	; switch back to main bank
	ldx #FINAL_BANK_MAIN
	stx $9c02
	RETURN_OK
.endproc
.POPSEG;

;******************************************************************************
; HOME
; Moves left until at the start of the source buffer or the start of the line
.export __src_home
.proc __src_home
:	jsr __src_left
	bcc :-
	rts
.endproc

;******************************************************************************
; LINE_END
; Moves right until at the end of the source buffer or the end of the line
.export __src_line_end
.proc __src_line_end
:	jsr __src_right
	bcc :-
	rts
.endproc

;******************************************************************************
; LEFT
; Moves left to the previous character unless it is a newline
; OUT:
;  - .C: set if the source cursor was unmoved
;  - .A: the character at the new source cursor position
.export __src_left
.proc __src_left
	jsr __src_prev
	bcs @nomove
	jsr __src_after_cursor
	cmp #$0d
	bne @done
	jsr __src_next
@nomove:
	sec
	rts
@done:	RETURN_OK
.endproc

;******************************************************************************
; RIGHT
; Moves to the next character unless it is a newline
; OUT:
;  - .C: set if the cursor wasn't moved, clear if it was
;  - .A: the character at the position that was moved to
.export __src_right
.proc __src_right
	jsr __src_end
	beq @endofline

	jsr __src_next
	cmp #$0d
	bne @done
	jsr __src_prev
@endofline:
	sec
	rts
@done:	clc
	rts
.endproc

;******************************************************************************
; RIGHT_REP
; Moves to the next character unless a newline exists AFTER the destination
; OUT:
;  - .C: set if the cursor wasn't moved, clear if it was
;  - .A: the character at the position that was moved to
.export __src_right_rep
.proc __src_right_rep
	jsr __src_before_end
	beq @endofline
	jsr __src_end		; should be impossible in REPLACE
	beq @endofline

	jsr __src_after_cursor	; if we're at end of line, don't move
	cmp #$0d
	beq @endofline
	jsr __src_next
	jsr __src_after_cursor	;if moving would put us at end of line, stay
	bcs @back
	cmp #$0d
	bne @done
@back:	jsr __src_prev
@endofline:
	sec
	rts
@done:	clc
	rts
.endproc

;******************************************************************************
; UP
; Moves the cursor back one line or to the start of the buffer if it is
; already on the first line
; this will leave the cursor on the first newline character encountered while
; going backwards through the source.
; OUT:
;  - .A: the character at the cursor position
;  - .C: set if cursor is at the start of the buffer
.export __src_up
.proc __src_up
	jsr __src_start
	bne @l0
	sec
@beginning:
	rts

@l0:	jsr __src_prev
	bcs @beginning
	cmp #$0d
	bne @l0
	RETURN_OK
.endproc

;******************************************************************************
; DOWN
; Moves the cursor beyond the next RETURN character (or to the end of
; the buffer if there is no such character
; OUT:
;  - .C: set if the end of the buffer was reached (cannot move "down")
.export __src_down
.proc __src_down
	jsr __src_end
	beq @eof
@l0:	jsr __src_next
	cmp #$0d
	beq :+
	jsr __src_end
	bne @l0
@eof:	sec	; end of the buffer
	rts
:	clc
	rts
.endproc

;******************************************************************************
; INSERT
; Adds the character in .A to the buffer at the gap position (gap).
; IN:
;  - .A: the character to insert
.export __src_insert
.proc __src_insert
@len=r0
@src=r2
@dst=r4
	pha
	jsr mark_dirty
	jsr gaplen
	cmpw #0		; is gap closed?
	bne @ins	; no, insert as usual

	; gap is closed, create a new one
	; copy data[poststart] to data[poststart + GAPSIZE]
	ldxy cursorzp
	stxy @src

	inc poststartzp+1
	inc end+1	; increase size by $100
	ldxy poststartzp
	stxy @dst

	; get number of bytes to copy
	ldxy end
	sub16 poststartzp

	lda bank
	sta r7		; destination bank
	jsr fe3::copy

@ins:	pla
	ldy cursorzp+1
	bmi @done	; out of range

	sta24 bank, cursorzp

	cmp #$0d
	bne :+
	incw line
	jsr on_line_inserted
:	incw cursorzp
@done:	rts
.endproc

;******************************************************************************
; INSERT_LINE
; Inserts the given string into the source at the current cursor position.
; IN:
;  - .XY: the string to insert
.export __src_insertline
.proc __src_insertline
@str=zp::tmp12
@offset=zp::tmp14
	stxy @str
	lda #$00
	sta @offset

@l0:	ldy @offset
	lda (@str),y
	beq @done
	jsr __src_insert
	inc @offset
	bne @l0
@done:	rts
.endproc

;******************************************************************************
; REPLACE
; Adds the character in .A to the buffer at the cursor position,
; replacing the character that currently resides there
; IN:
;  - .A: the character to replace the existing one with
; OUT:
;  - .C: set if there is nothing to replace
.export __src_replace
.proc __src_replace
	pha
	jsr mark_dirty
	jsr __src_delete
	pla
	bcc @ok
	rts
@ok:	jsr __src_insert
	jsr __src_left
	RETURN_OK
.endproc

;******************************************************************************
; GAPLEN
; Returns the length of the gap
; OUT:
;  - .XY: the length of the gap
.proc gaplen
	ldxy poststartzp
	sub16 cursorzp
	rts
.endproc

;******************************************************************************
; ATCURSOR
; Returns the character at the cursor position.
; OUT:
;  - .A: the character at the current cursor position
.export __src_atcursor
__src_atcursor:
.proc atcursor
	decw cursorzp
	lda24 bank, cursorzp
	incw cursorzp
	rts
.endproc

;******************************************************************************
; BEFORE_NEWL
; Checks if src::aftercursor is a newline ($0d)
; OUT:
;  - .Z: set if src::aftercursor == $0d
.export __src_before_newl
.proc __src_before_newl
	jsr __src_after_cursor
	cmp #$0d
	rts
.endproc

;******************************************************************************
; AFTER CURSOR
; Returns the character AFTER the cursor position.
; OUT:
;  - .A: the character after the current cursor position
;  - .C: set if there is no character after the cursor
.export __src_after_cursor
.proc __src_after_cursor
	jsr __src_end
	beq @end
	jsr __src_next
	pha
	jsr __src_prev
	pla
	RETURN_OK
@end:	sec		; end of buffer
	rts
.endproc

;******************************************************************************
; REWIND
; Moves the cursor back to the start of the buffer
.export __src_rewind
.proc __src_rewind
@l0:	jsr __src_prev
	bcc @l0
	rts
.endproc

;******************************************************************************
; READLINE
; Reads one line at the cursor positon and advances the cursor
; OUT:
;  - .A: the length of the line
;  - mem::linebuffer: the line that was read will be 0-terminated
;  - .C: set if the end of the source was reached
.export __src_readline
.proc __src_readline
@cnt=r4
	lda #$00
	sta mem::linebuffer
	sta @cnt

	jsr __src_end
	beq @eofdone

@l0:	jsr __src_next
	ldx @cnt
	cmp #$0d
	bne :+
	lda #$00
:	sta mem::linebuffer,x
	beq @done
	inc @cnt
	jsr __src_end
	bne @l0
@eof:	; go back a character; read last byte and null terminate if end of source
	ldx @cnt
	lda #$00
	sta mem::linebuffer,x
@eofdone:
	lda @cnt
	sec
	rts
@done:	txa
	clc
	rts
.endproc

;******************************************************************************
; POPGOTO
; Navigates to the the most recent source position pushed in .YX
.export __src_popgoto
.proc __src_popgoto
	jsr __src_popp
	bcc __src_goto
	rts
.endproc

;******************************************************************************
; GOTO
; Goes to the source position given
; IN:
;  - .XY: the source position to go to (see src::pos, src::pushp, src::popp)
.export __src_goto
.proc __src_goto
@dest=r4
	cmpw cursorzp
	beq @done
	stxy @dest
	bcc @backwards

@forwards:
	jsr __src_end
	beq @done
	jsr __src_next
	lda cursorzp
	cmp @dest
	bne @forwards
	lda cursorzp+1
	cmp @dest+1
	bne @forwards
	rts

@backwards:
	jsr __src_start
	beq @done
	jsr __src_prev
	lda cursorzp
	cmp @dest
	bne @backwards
	lda cursorzp+1
	cmp @dest+1
	bne @backwards
@done:  rts
.endproc

;******************************************************************************
; GET
; Returns the text at the current cursor position in mem::linebuffer
; OUT:
;  - mem::linebuffer: a line of text from the cursor position
;  - .C: set if the end of the buffer was reached as we were reading
.export __src_get
.proc __src_get
	ldxy #mem::linebuffer

	; fall through to __src_get_at
.endproc

;******************************************************************************
; GET_AT
; Returns the text at the current cursor position and stores it to the given
; target location
; IN:
;  - .XY: the target to store the contents of the source to
; OUT:
;  - (.XY): a line of text from the cursor position
;  - .C: set if the end of the buffer was reached as we were reading
.export __src_get_at
.proc __src_get_at
@target=r1
	stxy @target
	ldxy poststartzp
	stxy zp::bankaddr0

	jsr __src_end
	bne :+
	lda #$00
	tay
	sta (@target),y		; init buffer
	sec
	rts

:	ldxy @target
	stxy zp::bankaddr1

	jsr __src_on_last_line	; on last line already?
	bne :+
	ldxy end
	sub16 poststartzp	; bytes to copy
	txa
	pha
	tay			; .Y = bytes to copy
	dey
	lda bank
	jsr fe3::fcopy
	pla
	tay
	bne @done		; branch always

:	ldxy @target
	stxy zp::bankaddr1
	lda bank
	jsr fe3::copyline

@done:	lda #$00
	sta (@target),y
	RETURN_OK
.endproc

;******************************************************************************
; DOWNN
; Advances the source by the number of lines in .YX
;  - .YX: the number of lines that were not read
;  - .C: set if the end was reached before the total lines requested could be reached
.export __src_downn
.proc __src_downn
@cnt=r4
	stxy @cnt
@loop:	ldxy @cnt
	decw @cnt
	cmpw #$0000
	beq @done
	jsr __src_down
	bcc @loop
@done:	ldxy @cnt
	rts
.endproc

;******************************************************************************
; UPN
; Advances the source by the number of lines in .YX
; IN:
;  - .XY: the number of lines to move "up"
; OUT:
;  - .YX: contains the number of lines that were not read
;  - .C: set if the beginning was reached before the total lines requested could be reached
.export __src_upn
.proc __src_upn
@cnt=r4
	stxy @cnt
@loop:	ldxy @cnt
	decw @cnt
	cmpw #$0000
	beq @done
	jsr __src_up
	bcc @loop
@done:	ldxy @cnt
	rts
.endproc

;******************************************************************************_
; ON_LAST_LINE
; Checks if the cursor is on the last line of the active source buffer.
; OUT:
;  - .Z: set if the cursor is on the last line of the active source
.export __src_on_last_line
.proc __src_on_last_line
	ldxy line
	cmpw lines
	rts
.endproc

;******************************************************************************
; MARK DIRTY
; Marks the given buffer as "dirty" by setting its appropriate flag.
; IN:
;  - .A: the buffer ID to flag as DIRTY
.proc mark_dirty
	lda #FLAG_DIRTY
	ldx activesrc
	sta flags,x
	rts
.endproc
