
; flat editor core
; Copyright (c) 1999-2015, Tomasz Grysztar.
; All rights reserved.

find_line:
	mov	esi,[first_line]
	mov	ecx,1
	mov	edx,[window_line_number]
	cmp	eax,edx
	jae	forward_from_window
	sub	edx,eax
	cmp	edx,eax
	jb	backward_from_window
	jmp	find_forward
    forward_from_window:
	mov	esi,[window_line]
	mov	ecx,edx
    find_forward:
	cmp	ecx,eax
	je	line_found
	cmp	dword [esi],0
	je	line_found
	mov	ebx,esi
    forward_skip_line:
	mov	ebx,[ebx]
	btr	ebx,0
	jc	forward_skip_line
	or	ebx,ebx
	jz	line_found
	mov	esi,ebx
	inc	ecx
	jmp	find_forward
    backward_from_window:
	mov	esi,[window_line]
	mov	ecx,[window_line_number]
    find_backward:
	cmp	ecx,eax
	je	line_found
	cmp	dword [esi+4],0
	je	line_found
	mov	esi,[esi+4]
	dec	ecx
	jmp	find_backward
    line_found:
	retn

get_caret_segment:
	mov	edx,[caret_position]
	mov	esi,[caret_line]
    find_segment:
	cmp	edx,SEGMENT_DATA_LENGTH
	jb	segment_found
	test	byte [esi],1
	jz	segment_found
	mov	esi,[esi]
	dec	esi
	sub	edx,SEGMENT_DATA_LENGTH
	jmp	find_segment
    segment_found:
	retn

check_line_length:
	xor	edx,edx
	mov	ebx,esi
    count_line_segments:
	test	byte [esi],1
	jz	last_line_segment
	mov	esi,[esi]
	dec	esi
	add	edx,SEGMENT_DATA_LENGTH
	jmp	count_line_segments
    last_line_segment:
	lea	edi,[esi+SEGMENT_LENGTH-1]
	mov	al,20h
	mov	ecx,SEGMENT_DATA_LENGTH
	std
	repe	scasb
	cld
	setne	al
	movzx	eax,al
	add	ecx,eax
	jnz	line_segments_ok
	or	edx,edx
	jz	line_segments_ok
	call	store_freed_segment_for_undo
	mov	eax,[esi]
	mov	esi,[esi+4]
	dec	esi
	call	store_segment_for_undo
	mov	[esi],eax
	sub	edx,SEGMENT_DATA_LENGTH
	jmp	last_line_segment
    line_segments_ok:
	add	ecx,edx
	mov	edx,ecx
	cmp	edx,[ebx+8]
	je	line_length_checked
	mov	esi,ebx
	call	store_segment_for_undo
	xchg	[ebx+8],edx
	push	edx
	mov	eax,[ebx+8]
	call	register_length
	pop	eax
	call	unregister_length
    line_length_checked:
	retn
  register_length:
	cmp	eax,[peak_line_length]
	jbe	peak_length_ok
	mov	[peak_line_length],eax
    peak_length_ok:
	or	eax,eax
	jz	ignore_zero_length
	push	ebx
	call	find_lengths_segment
	inc	dword [ebx+SEGMENT_HEADER_LENGTH+eax*4]
	pop	ebx
    ignore_zero_length:
	retn
    find_lengths_segment:
	mov	ebx,[lengths_table]
    try_lengths_segment:
	cmp	eax,SEGMENT_DATA_LENGTH/4
	jb	length_entry_ok
	sub	eax,SEGMENT_DATA_LENGTH/4
	cmp	dword [ebx],0
	je	add_lengths_segment
	mov	ebx,[ebx]
	jmp	try_lengths_segment
    add_lengths_segment:
	push	eax ecx edi
	call	allocate_segment
	jc	memory_shortage
	call	store_allocated_segment_for_undo
	mov	[ebx],eax
	mov	edi,eax
	xor	eax,eax
	stosd
	mov	eax,ebx
	stosd
	mov	eax,[ebx+8]
	add	eax,SEGMENT_DATA_LENGTH/4
	stosd
	add	edi,SEGMENT_HEADER_LENGTH-12
	mov	ecx,SEGMENT_DATA_LENGTH shr 2
	xor	eax,eax
	rep	stosd
	lea	ebx,[edi-SEGMENT_LENGTH]
	pop	edi ecx eax
	jmp	try_lengths_segment
    length_entry_ok:
	retn
  unregister_length:
	or	eax,eax
	jz	ignore_zero_length
	push	ebx
	cmp	eax,[peak_line_length]
	je	unregister_peak_length
	call	find_lengths_segment
	dec	dword [ebx+SEGMENT_HEADER_LENGTH+eax*4]
    length_unregistered:
	pop	ebx
	retn
    unregister_peak_length:
	call	find_lengths_segment
	dec	dword [ebx+SEGMENT_HEADER_LENGTH+eax*4]
	jnz	length_unregistered
    find_reduced_peak:
	or	eax,eax
	jz	try_earlier_lengths_segment
	dec	eax
	cmp	dword [ebx+SEGMENT_HEADER_LENGTH+eax*4],0
	je	find_reduced_peak
	add	eax,[ebx+8]
	mov	[peak_line_length],eax
	jmp	length_unregistered
    try_earlier_lengths_segment:
	mov	ebx,[ebx+4]
	mov	eax,SEGMENT_DATA_LENGTH/4
	or	ebx,ebx
	jnz	find_reduced_peak
	mov	[peak_line_length],ebx
	jmp	length_unregistered

update_window:
	mov	edx,[window_line_number]
	cmp	edx,1
	je	window_vertical_position_ok
	add	edx,[window_height]
	dec	edx
	cmp	edx,[lines_count]
	jle	window_vertical_position_ok
	sub	edx,[lines_count]
    window_vertical_correction:
	mov	esi,[window_line]
	mov	esi,[esi+4]
	or	esi,esi
	jz	window_vertical_position_ok
	mov	[window_line],esi
	dec	[window_line_number]
	dec	edx
	jnz	window_vertical_correction
    window_vertical_position_ok:
	mov	ecx,[peak_line_length]
	cmp	ecx,[caret_position]
	jae	caret_position_ok
	mov	ecx,[caret_position]
    caret_position_ok:
	cmp	[selection_line],0
	je	selection_position_ok
	cmp	ecx,[selection_position]
	jae	selection_position_ok
	mov	ecx,[selection_position]
    selection_position_ok:
	mov	eax,[window_width]
	dec	eax
	cmp	ecx,eax
	jae	edit_space_width_ok
	mov	ecx,eax
    edit_space_width_ok:
	mov	[maximum_position],ecx
	mov	edx,[window_position]
	or	edx,edx
	jz	window_horizontal_position_ok
	add	edx,[window_width]
	inc	ecx
	cmp	edx,ecx
	jbe	window_horizontal_position_ok
	sub	edx,ecx
	sub	[window_position],edx
	jnc	window_horizontal_position_ok
	mov	[window_position],0
    window_horizontal_position_ok:
	retn

let_caret_appear:
	mov	eax,[caret_position]
	cmp	eax,[window_position]
	jl	horizontal_correction
	mov	ecx,[window_width]
	jecxz	last_position_ok
	dec	ecx
    last_position_ok:
	add	ecx,[window_position]
	mov	eax,[caret_position]
	sub	eax,ecx
	jbe	horizontal_ok
	add	eax,[window_position]
    horizontal_correction:
	mov	[window_position],eax
    horizontal_ok:
	mov	esi,[caret_line]
	mov	ecx,[caret_line_number]
	cmp	ecx,[window_line_number]
	jl	vertical_correction
	mov	eax,[window_height]
	or	eax,eax
	jz	vertical_check
	dec	eax
    vertical_check:
	neg	eax
	add	eax,[caret_line_number]
	cmp	[window_line_number],eax
	jge	vertical_ok
	mov	esi,[window_line]
	mov	ecx,[window_line_number]
    vertical_find:
	mov	esi,[esi]
	btr	esi,0
	jc	vertical_find
	inc	ecx
	cmp	ecx,eax
	jl	vertical_find
    vertical_correction:
	mov	[window_line],esi
	mov	[window_line_number],ecx
    vertical_ok:
	retn

move_line_up:
	mov	esi,[caret_line]
	mov	eax,[esi+4]
	or	eax,eax
	jz	cannot_move
	mov	[caret_line],eax
	dec	[caret_line_number]
	clc
	retn
    cannot_move:
	stc
	retn

move_line_down:
	mov	esi,[caret_line]
    find_next_line:
	mov	eax,[esi]
	or	eax,eax
	jz	cannot_move
	btr	eax,0
	jnc	down_ok
	mov	esi,eax
	jmp	find_next_line
    down_ok:
	mov	[caret_line],eax
	inc	[caret_line_number]
	clc
	retn

move_page_up:
	mov	eax,[caret_line_number]
	sub	eax,[window_height]
	ja	pgup_caret_ok
	mov	eax,1
    pgup_caret_ok:
	call	find_line
	mov	[caret_line],esi
	mov	[caret_line_number],ecx
	mov	eax,[window_line_number]
	sub	eax,[window_height]
	ja	pgup_window_ok
	mov	eax,1
	cmp	[window_line_number],eax
	je	moved_ok
    pgup_window_ok:
	call	find_line
	mov	[window_line],esi
	mov	[window_line_number],ecx
	retn

move_page_down:
	mov	eax,[caret_line_number]
	add	eax,[window_height]
	call	find_line
	mov	[caret_line],esi
	mov	[caret_line_number],ecx
	mov	eax,[window_line_number]
	mov	ecx,[window_height]
	add	eax,ecx
	mov	ebx,[lines_count]
	sub	ebx,ecx
	jbe	moved_ok
	inc	ebx
	cmp	eax,ebx
	jb	pgdn_window_ok
	mov	eax,ebx
	cmp	[window_line_number],eax
	je	moved_ok
    pgdn_window_ok:
	call	find_line
	mov	[window_line],esi
	mov	[window_line_number],ecx
    moved_ok:
	retn

move_to_previous_word:
	call	get_caret_segment
	mov	ecx,[caret_position]
	sub	ecx,edx
	cmp	edx,SEGMENT_DATA_LENGTH
	jbe	find_word_to_the_left
	mov	edx,SEGMENT_DATA_LENGTH
    find_word_to_the_left:
	sub	edx,1
	jc	word_in_previous_segment
	mov	al,[esi+SEGMENT_HEADER_LENGTH+edx]
	call	recognize_character
	jnc	find_word_start
	jmp	find_word_to_the_left
    word_in_previous_segment:
	mov	eax,[esi+4]
	or	eax,eax
	jz	previous_word_ok
	mov	esi,eax
	btr	esi,0
	jnc	word_in_previous_line
	mov	edx,SEGMENT_DATA_LENGTH
	sub	ecx,edx
	jmp	find_word_to_the_left
    word_in_previous_line:
	mov	[caret_line],esi
	dec	[caret_line_number]
	mov	edx,SEGMENT_DATA_LENGTH
	xor	ecx,ecx
    find_last_segment:
	test	byte [esi],1
	jz	find_word_to_the_left
	mov	esi,[esi]
	dec	esi
	add	ecx,SEGMENT_DATA_LENGTH
	jmp	find_last_segment
    find_word_start:
	sub	edx,1
	jc	word_on_segment_edge
	mov	al,[esi+SEGMENT_HEADER_LENGTH+edx]
	call	recognize_character
	jc	previous_word_ok
	jmp	find_word_start
    word_on_segment_edge:
	mov	esi,[esi+4]
	btr	esi,0
	jnc	previous_word_ok
	mov	edx,SEGMENT_DATA_LENGTH
	sub	ecx,edx
	jmp	find_word_start
    previous_word_ok:
	add	ecx,edx
	inc	ecx
	mov	[caret_position],ecx
	retn

move_to_next_word:
	mov	ecx,[caret_position]
	sub	ecx,edx
    find_word_end:
	cmp	edx,SEGMENT_DATA_LENGTH
	jae	word_wraps_segment_edge
	mov	al,[esi+SEGMENT_HEADER_LENGTH+edx]
	call	recognize_character
	jc	find_word_to_the_right
	add	edx,1
	jmp	find_word_end
    word_wraps_segment_edge:
	mov	esi,[esi]
	or	esi,esi
	jz	move_to_line_end
	btr	esi,0
	jnc	word_in_next_line
	add	ecx,SEGMENT_DATA_LENGTH
	sub	edx,SEGMENT_DATA_LENGTH
	jmp	find_word_end
    find_word_to_the_right:
	cmp	edx,SEGMENT_DATA_LENGTH
	jae	word_in_next_segment
	mov	al,[esi+SEGMENT_HEADER_LENGTH+edx]
	call	recognize_character
	jnc	next_word_ok
	add	edx,1
	jmp	find_word_to_the_right
    word_in_next_segment:
	mov	esi,[esi]
	or	esi,esi
	jz	move_to_line_end
	btr	esi,0
	jnc	word_in_next_line
	add	ecx,SEGMENT_DATA_LENGTH
	sub	edx,SEGMENT_DATA_LENGTH
	jmp	find_word_to_the_right
    word_in_next_line:
	mov	[caret_line],esi
	inc	[caret_line_number]
	xor	ecx,ecx
	xor	edx,edx
	jmp	find_word_to_the_right
    next_word_ok:
	add	ecx,edx
	mov	[caret_position],ecx
	retn
  move_to_line_end:
	mov	esi,[caret_line]
	mov	eax,[esi+8]
	mov	[caret_position],eax
	retn

get_word_at_caret:
	call	get_caret_segment
	mov	ebx,[caret_position]
	sub	ebx,edx
    find_left_edge:
	or	edx,edx
	jz	left_edge_in_previous_segment
	cmp	edx,SEGMENT_DATA_LENGTH
	ja	left_edge_ok
	mov	al,[esi+SEGMENT_HEADER_LENGTH+edx-1]
	call	recognize_character
	jc	left_edge_ok
	dec	edx
	jmp	find_left_edge
    left_edge_in_previous_segment:
	mov	esi,[esi+4]
	btr	esi,0
	jnc	left_edge_ok
	mov	edx,SEGMENT_DATA_LENGTH
	sub	ebx,edx
	jmp	find_left_edge
    left_edge_ok:
	add	ebx,edx
	call	get_caret_segment
	mov	ecx,[caret_position]
	sub	ecx,edx
    find_right_edge:
	cmp	edx,SEGMENT_DATA_LENGTH
	jae	right_edge_in_next_segment
	mov	al,[esi+SEGMENT_HEADER_LENGTH+edx]
	call	recognize_character
	jc	right_edge_ok
	inc	edx
	jmp	find_right_edge
    right_edge_in_next_segment:
	mov	esi,[esi]
	btr	esi,0
	jnc	right_edge_ok
	xor	edx,edx
	add	ecx,SEGMENT_DATA_LENGTH
	jmp	find_right_edge
    right_edge_ok:
	add	ecx,edx
	sub	ecx,ebx
	mov	edx,ebx
	retn
