package Kephra::Edit::Search;
our $VERSION = '0.31';

use strict;
use warnings;

# internal and menu functions about find and replace text
# drag n drop target class

# internal functions
my ($find_item, $found_pos, $old_pos, $replace_item);
my $flags;
my $history_refresh;
my @find_history;
my @replace_history;
sub _config    { Kephra::API::settings()->{search} }
sub _attributes{ _config()->{attribute} }
sub _history   { _config()->{history}   }
sub _find_pos  { $found_pos }

sub _refresh_search_flags {
	my $attr = _attributes();
	$flags = 0;

	$flags |= &Wx::wxSTC_FIND_MATCHCASE
		if defined $attr->{match_case} and $attr->{match_case};
	if ( defined $attr->{match_whole_word} and $attr->{match_whole_word} ) {
		$flags |= &Wx::wxSTC_FIND_WHOLEWORD
	} else {
		$flags |= &Wx::wxSTC_FIND_WORDSTART
			if $attr->{match_word_begin} and $attr->{match_word_begin};
	}
	$flags |= &Wx::wxSTC_FIND_REGEXP
		if defined $attr->{match_regex} and $attr->{match_regex};
}


sub load_search_data {
	my $file = Kephra::Config::filepath( _config()->{data_file} );
	my $config_tree = Kephra::Config::File::load($file);

	set_find_item( $config_tree->{find}{item}  || '' );
	set_replace_item( $config_tree->{replace}{item}  || '' );
	if (defined $config_tree->{find}{history}){
		if (ref $config_tree->{find}{history} eq 'ARRAY'){
				 @find_history = @{ $config_tree->{find}{history} };
		} else { $find_history[0] = $config_tree->{find}{history} }
	}
	if (defined $config_tree->{replace}{history}){
		if (ref $config_tree->{replace}{history} eq 'ARRAY'){
				 @replace_history = @{ $config_tree->{replace}{history} };
		} else { $replace_history[0] = $config_tree->{replace}{history} }
	}
	my $history = _history();

	# remove dups and cut to the configured length
	if ( $history->{use} ) {
		my ( %seen1, %seen2 );
		my @uniq = grep { !$seen1{$_}++ } @find_history;
		@find_history = splice @uniq, 0, $history->{length};
		@uniq = grep { !$seen2{$_}++ } @replace_history;
		@replace_history = splice @uniq, 0, $history->{length};
	} else {
		@find_history    = ();
		@replace_history = ();
	}
	# search item is findable
	$found_pos = 0;
	Kephra::EventTable::trigger
		('find.item.history.changed', 'replace.item.history.changed');
	Kephra::Edit::Marker::restore_bookmarks( $config_tree->{bookmark} );
}

sub save_search_data {
	my $file = Kephra::Config::filepath( _config()->{data_file} );
	my $config_tree = Kephra::Config::File::load($file);
	$config_tree->{find}{item}       = get_find_item();
	$config_tree->{find}{history}    = get_find_history();
	$config_tree->{replace}{item}    = get_replace_item();
	$config_tree->{replace}{history} = get_replace_history();
	$config_tree->{bookmark}         = Kephra::Edit::Marker::get_bookmark_data();
	Kephra::Config::File::store($file, $config_tree);
	Kephra::Edit::Marker::store();
}
#
sub get_find_item { $find_item  || ''}
sub set_find_item {
	my $old = $find_item;
	my $new = shift;
	if (defined $new and (not defined $old or $new ne $old)){
		$find_item = $new;
		$found_pos = -1;
		Kephra::EventTable::trigger('find.item.changed');
	}
}

sub set_selection_as_find_item {
	set_find_item( Kephra::App::EditPanel::_ref()->GetSelectedText )
}

sub item_findable       { _exist_find_item() }
sub _exist_find_item    { (defined $find_item and $find_item) ? 1 : 0 }
sub _exist_replace_item { (defined $replace_item and $replace_item) ? 1 : 0 }

sub get_replace_item { $replace_item || ''}
sub set_replace_item {
	my $old = $replace_item;
	my $new = shift;
	if (defined $new and (not defined $old or $new ne $old)){
		$replace_item = $new;
		Kephra::EventTable::trigger('replace.item.changed');
	}
}

sub set_selection_as_replace_item {
	set_replace_item( Kephra::App::EditPanel::_ref()->GetSelectedText )
}
sub get_find_history    { \@find_history    }
sub get_replace_history { \@replace_history }
sub refresh_find_history {
	my $found_match = shift;
	my $find_item   = get_find_item();
	my $history     = _history();
	return unless $history->{use};
	# check if refresh needed
	return if $history->{remember_only_matched} and not $found_match;

	if ($find_item and $find_history[0] ne $find_item) {
		for ( 0 .. $#find_history ) {      # delete the dup
			if ( $find_history[$_] eq $find_item ) {
				@find_history = @find_history[ 0 .. $_-1, $_+1 .. $#find_history ];
				last;
			}
			pop @find_history if $_ == $history->{length} - 1;
		}
		unshift @find_history, $find_item; # insert new history item
		Kephra::EventTable::trigger('find.item.history.changed');
	}
}
sub refresh_replace_history {
	my $replace_item = get_replace_item();
	my $history      = _history();

	if ($replace_item) {
		for ( 0 .. $#replace_history ) {
			if ( $replace_history[$_] eq $replace_item ) {
				@replace_history = @replace_history[ 0 .. $_-1, $_+1 .. $#replace_history ];
				last;
			}
			pop @replace_history if $_ == $history->{length} - 1;
		}
		unshift @replace_history, $replace_item;
		Kephra::EventTable::trigger('replace.item.history.changed');
	}
}

sub _caret_2_sel_end {
	my $ep = Kephra::App::EditPanel::_ref();
	my $pos       = $ep->GetCurrentPos;
	my $sel_start = $ep->GetSelectionStart;
	my $sel_end   = $ep->GetSelectionEnd;
	if ( $pos != $sel_end ) {
		$ep->SetCurrentPos($sel_end);
		$ep->SetSelectionStart($sel_start);
	}
}

#
sub set_range{ _attributes()->{in} = shift }
sub get_range{ _attributes()->{in}         }

sub get_attribute{
	my $attr = shift;
	if ($attr eq 'match_case'      or 
		$attr eq 'match_word_begin'or
		$attr eq 'match_whole_word'or
		$attr eq 'match_regex'     or
		$attr eq 'auto_wrap'       or
		$attr eq 'incremental'       ) {
		_attributes()->{$attr}
	}
}

sub switch_attribute{
	my $attr = shift;
	if ($attr eq 'match_case'      or 
		$attr eq 'match_word_begin'or
		$attr eq 'match_whole_word'or
		$attr eq 'match_regex'     or
		$attr eq 'auto_wrap'       or
		$attr eq 'incremental'       ) {
		unless (defined _attributes->{$attr}) 
			 { _attributes->{$attr}  = 1 }
		else { _attributes->{$attr} ^= 1 }
		_refresh_search_flags() if substr($attr, 0, 1) eq 'm';
	}
}

# find helper function
sub replace_selection  {
	Kephra::App::EditPanel::_ref()->ReplaceSelection( get_replace_item() )
}

sub _find_next  {
	my $ep = Kephra::App::EditPanel::_ref();
	$ep->SearchAnchor;
	$found_pos = $ep->SearchNext( $flags, get_find_item() );
	Kephra::EventTable::trigger('find');
	return $found_pos;
}

sub _find_prev {
	my $ep = Kephra::App::EditPanel::_ref();
	$ep->SearchAnchor;
	$found_pos = $ep->SearchPrev( $flags, get_find_item() );
	Kephra::EventTable::trigger('find');
	return $found_pos;
}

sub _find_first {
	Kephra::Edit::Goto::_pos(0);
	_find_next();
}

sub _find_last  {
	Kephra::Edit::Goto::_pos(-1);
	_find_prev();
}


sub first_increment {
	my $ep = Kephra::App::EditPanel::_ref();
	if ( _exist_find_item() ) {
		Kephra::Edit::_save_positions;
		if ( _find_first() > -1 ) {
			#_caret_2_sel_end();
			Kephra::Edit::_center_caret;
			return 1;
		}
	}
	Kephra::Edit::Goto::_pos( $old_pos ) if defined $old_pos;
	return 0;
}

#sub next_increment {}
# find related menu calls
sub find_all {
#Kephra::Dialog::msg_box(&Wx::wxUNICODE(), '');
	my $ep = Kephra::App::EditPanel::_ref();
	if ( _exist_find_item() ) {
		my $search_result = _find_first();
		my ($sel_start, $sel_end);
		#Kephra::Dialog::msg_box( , '');
		#$ep->IndicatorSetStyle(0, &Wx::wxSTC_INDIC_TT );
		#$ep->IndicatorSetForeground(0, &Wx::Colour->new(0xff, 0x00, 0x00));
		$ep->IndicatorSetStyle(1, &Wx::wxSTC_INDIC_TT );
		$ep->IndicatorSetForeground(1, &Wx::Colour->new(0xff, 0x00, 0x00));
		# ^= &Wx::wxSTC_INDIC_STRIKE;
		$ep->SetSelection(0,0);
		return 0 if $search_result == -1;
		while ($search_result > -1){
			($sel_start, $sel_end) = $ep->GetSelection;
			Kephra::Edit::Goto::_pos( $sel_end );
			$ep->StartStyling($sel_start, 224);#224
			$ep->SetStyleBytes($sel_end - $sel_start, 128);
			$search_result = _find_next();
		}
		Kephra::Edit::Goto::_pos( $sel_end );
		$ep->Colourise( 0, $sel_end);
		return 1;
	} else {
		Kephra::Edit::Goto::_pos( $old_pos ) if defined $old_pos;
		return 1;
	}
}

sub find_prev {
	my $ep    = Kephra::App::EditPanel::_ref();
	my $attr  = _attributes();
	my $return = -1;
	if ( _exist_find_item() ) {
		Kephra::Edit::_save_positions;
		Kephra::Edit::Goto::_pos( $ep->GetSelectionStart - 1 );
		$return = _find_prev();
		if ( $return == -1 ) {
			if ( get_range() eq 'document' ) {
				$return = _find_last() if $attr->{auto_wrap};
			} elsif ( get_range() eq 'open_docs' ) {
				$Kephra::temp{dialog}{control} = 1;
				my $begin_doc = Kephra::Document::Data::current_nr();
				while ( $return == -1 ) {
					Kephra::Edit::_restore_positions;
					last if ( ( Kephra::Document::Data::current_nr() == 0 )
						 and !$attr->{auto_wrap} );
					Kephra::Document::Change::tab_left();
					Kephra::Edit::_save_positions();
					$return = _find_last();
					last
						if ( Kephra::Document::Data::current_nr() == $begin_doc );
				}
				$Kephra::temp{dialog}{control} = 0;
			}
		}
		if ( $return == -1 ) { Kephra::Edit::_restore_positions() }
		else { _caret_2_sel_end(); Kephra::Edit::_center_caret() }
		refresh_find_history($return);
	}
	$return;
}

sub find_next {
	my $ep   = Kephra::App::EditPanel::_ref();
	my $attr =  _attributes();
	my $return = -1;

	if ( _exist_find_item() ) {
		Kephra::Edit::_save_positions();
		Kephra::Edit::Goto::_pos( $ep->GetSelectionEnd );
		$return = _find_next();
		if ( $return == -1 ) {
			if ( get_range() eq 'document' ) {
				$return = _find_first() if $attr->{auto_wrap};
			} elsif ( get_range() eq 'open_docs' ) {
				$Kephra::temp{dialog}{control} = 1;
				my $begin_doc = Kephra::Document::Data::current_nr();
				while ( $return == -1 ) {
					Kephra::Edit::_restore_positions();
					last if Kephra::Document::Data::current_nr()
							== Kephra::Document::Data::last_nr()
						 and not $attr->{auto_wrap};
					Kephra::Document::Change::tab_right();
					Kephra::Edit::_save_positions();
					$return = _find_first();
					last if ( Kephra::Document::Data::current_nr() == $begin_doc );
				}
				$Kephra::temp{dialog}{control} = 0;
			}
		}
		if ( $return == -1 ) { Kephra::Edit::_restore_positions() }
		else { _caret_2_sel_end(); Kephra::Edit::_center_caret(); }
		refresh_find_history($return);
	}
	$return;
}

sub fast_back {
	my $ep   = Kephra::App::EditPanel::_ref();
	my $attr = _attributes();
	my $return    = -1;
	if (_exist_find_item()) {
		for ( 1 .. $attr->{fast_steps} ) {
			Kephra::Edit::_save_positions();
			Kephra::Edit::Goto::_pos( $ep->GetSelectionStart - 1 );
			$return = _find_prev();
			if ( $return == -1 ) {
				if ( get_range() eq 'document' ) {
					$return = _find_last() if $attr->{auto_wrap};
				} elsif ( get_range() eq 'open_docs' ) {
					$Kephra::temp{dialog}{control} = 1;
					my $begin_doc = Kephra::Document::Data::current_nr();
					while ( $return == -1 ) {
						Kephra::Edit::_restore_positions();
						last if Kephra::Document::Data::current_nr() == 0
							and not $attr->{auto_wrap};
						Kephra::Document::Change::tab_left();
						Kephra::Edit::_save_positions();
						$return = _find_last();
						last if Kephra::Document::Data::current_nr() == $begin_doc;
					}
					$Kephra::temp{dialog}{control} = 0;
				}
			}
			refresh_find_history($return) if ( $_ == 1 );
			if ( $return == -1 ) { Kephra::Edit::_restore_positions(); last; }
			else { _caret_2_sel_end(); Kephra::Edit::_center_caret(); }
		}
	}
}

sub fast_fore {
	my $ep   = Kephra::App::EditPanel::_ref();
	my $attr = _attributes();
	my $return    = -1;
	if (_exist_find_item()) {
		for ( 1 .. $attr->{fast_steps} ) {
			Kephra::Edit::_save_positions();
			Kephra::Edit::Goto::_pos( $ep->GetSelectionEnd );
			$return = _find_next();
			if ( $return == -1 ) {
				if ( get_range() eq 'document' ) {
					$return = _find_first() if $attr->{auto_wrap};
				} elsif ( get_range() eq 'open_docs' ) {
					$Kephra::temp{dialog}{control} = 1;
					my $begin_doc = Kephra::Document::Data::current_nr();
					while ( $return == -1 ) {
						Kephra::Edit::_restore_positions();
						last if Kephra::Document::Data::current_nr()
								== Kephra::Document::Data::last_nr()
							and not $attr->{auto_wrap};
						Kephra::Document::Change::tab_right();
						Kephra::Edit::_save_positions();
						$return = _find_first();
						last if Kephra::Document::Data::current_nr() == $begin_doc;
					}
					$Kephra::temp{dialog}{control} = 0;
				}
			}
			refresh_find_history($return) if $_ == 1;
			if ( $return == -1 ) { Kephra::Edit::_restore_positions(); last; }
			else { _caret_2_sel_end(); Kephra::Edit::_center_caret(); }
		}
	}
}

sub find_first {
	my $menu_call = shift;
	my $ep = Kephra::App::EditPanel::_ref();
	my $attr = _attributes();
	my ( $sel_begin, $sel_end ) = $ep->GetSelection;
	my $pos = $ep->GetCurrentPos;
	my $len = _exist_find_item();
	my $return;
	if ( _exist_find_item() ) {
		Kephra::Edit::_save_positions();
		if ($menu_call
		and $sel_begin != $sel_end
		and $sel_end - $sel_begin > $len ) {
			set_range('selection') 
		}
		if ( get_range() eq 'selection' ) {
			Kephra::Edit::Goto::_pos($sel_begin);
			$return = _find_next();
			if ($return > -1 and $ep->GetCurrentPos + $len <= $sel_end) {
				Kephra::Edit::_center_caret();
			} else {
				Kephra::Edit::_restore_positions();
				$return = -1;
			}
		} else {
			$return = _find_first();
			if ( get_range() eq 'open_docs'
			and ($sel_begin == $ep->GetSelectionStart or $return == -1 ) ){
				$Kephra::temp{dialog}{control} = 1;
				$return = -1;
				my $begin_doc = Kephra::Document::Data::current_nr();
				while ( $return == -1 ) {
					Kephra::Edit::_restore_positions();
					last if Kephra::Document::Data::current_nr() == 0
						and not $attr->{auto_wrap};
					Kephra::Document::Change::tab_left();
					Kephra::Edit::_save_positions();
					$return = _find_first();
					last if ( Kephra::Document::Data::current_nr() == $begin_doc );
				}
				$Kephra::temp{dialog}{control} = 0;
			}
			if ( $return > -1 ) {
				_caret_2_sel_end();
				Kephra::Edit::_center_caret();
			} else {
				Kephra::Edit::_restore_positions();
			}
		}
		refresh_find_history($return);
	}
	$return;
}

sub find_last {
	my $menu_call = shift;
	my $ep = Kephra::App::EditPanel::_ref();
	my $attr = _attributes();
	my ( $sel_begin, $sel_end ) = $ep->GetSelection;
	my $pos = $ep->GetCurrentPos;
	my $len = _exist_find_item();
	my $return;
	if (_exist_find_item()) {
		Kephra::Edit::_save_positions();
		if ($menu_call
			and $sel_begin != $sel_end
			and $sel_end - $sel_begin > $len) {
			set_range('selection');
		}
		if ( get_range() eq 'selection' ) {
			Kephra::Edit::Goto::_pos($sel_end);
			$return = _find_prev();
			if ($return > -1 and $ep->GetCurrentPos >= $sel_begin) {
				Kephra::Edit::_center_caret();
			} else {
				Kephra::Edit::_restore_positions();
				$return = -1;
			}
		} else {
			$return = _find_last();
			if (get_range() eq 'open_docs'
				and ($sel_begin == $ep->GetSelectionStart or $return == -1) ){
				$Kephra::temp{dialog}{control} = 1;
				$return = -1;
				my $begin_doc = Kephra::Document::Data::current_nr();
				while ( $return == -1 ) {
					Kephra::Edit::_restore_positions();
					last if Kephra::Document::Data::current_nr()
							== Kephra::Document::Data::last_nr()
						and not $attr->{auto_wrap};
					Kephra::Document::Change::tab_right();
					Kephra::Edit::_save_positions();
					$return = _find_last();
					last if ( Kephra::Document::Data::current_nr() == $begin_doc );
				}
				$Kephra::temp{dialog}{control} = 0;
			}
			if ( $return > -1 ) {
				_caret_2_sel_end();
				Kephra::Edit::_center_caret();
			} else {
				Kephra::Edit::_restore_positions();
			}
		}
		refresh_find_history($return);
	}
	$return;
}

  # replace
sub replace_back {
	my $ep = Kephra::App::EditPanel::_ref();
	if ( $ep->GetSelectionStart != $ep->GetSelectionEnd ) {
		replace_selection();
		refresh_replace_history();
		find_prev();
	}
}

sub replace_fore {
	my $ep = Kephra::App::EditPanel::_ref();
	if ( $ep->GetSelectionStart != $ep->GetSelectionEnd ) {
		replace_selection();
		refresh_replace_history();
		find_next();
	}
}

sub replace_all {
	my $menu_call = shift;
	my $ep = Kephra::App::EditPanel::_ref();
	my ($sel_begin, $sel_end ) = $ep->GetSelection;
	my $line           = $ep->GetCurrentLine;
	my $len            = _exist_find_item();
	my $replace_string = get_replace_item();
	#if ($len) { # forbid replace with nothing
		if (    $menu_call
		    and $sel_begin != $sel_end 
			and $sel_end - $sel_begin > $len ) {
			_attributes()->{in} = 'selection';
		}
		if ( get_range() eq 'selection' ) {
			$ep->BeginUndoAction;
			Kephra::Edit::Goto::_pos($sel_begin);
			while ( _find_next() > -1 ) {
				last if ( $ep->GetCurrentPos + $len >= $sel_end );
				$ep->ReplaceSelection($replace_string);
			}
			$ep->EndUndoAction;
		} elsif ( get_range() eq 'document' ) {
			$ep->BeginUndoAction;
			Kephra::Edit::Goto::_pos(0);
			while ( _find_next() > -1 ) {
				$ep->ReplaceSelection($replace_string);
			}
			$ep->EndUndoAction;
		} elsif ( get_range() eq 'open_docs' ) {
			my $begin_doc = Kephra::Document::Data::current_nr();
			do {
				{
					Kephra::Edit::_save_positions();
					$ep->BeginUndoAction;
					Kephra::Edit::Goto::_pos(0);
					while ( _find_next() > -1 ) {
						$ep->ReplaceSelection($replace_string);
					}
					$ep->EndUndoAction;
					Kephra::Edit::_restore_positions();
				}
			} until ( Kephra::Document::Change::tab_right() == $begin_doc );
		}
		$ep->GotoLine($line);
		refresh_replace_history;
		Kephra::Edit::_keep_focus();
	#} # end of don't replace nothing
}

sub replace_confirm {
	if (_exist_find_item()) {
		my $ep = Kephra::App::EditPanel::_ref();
		my $attr = _attributes();
		my $line = $ep->LineFromPosition( $ep->GetCurrentPos );
		my $len  = _exist_find_item();
		my $sel_begin = $ep->GetSelectionStart;
		my $sel_end   = $ep->GetSelectionEnd;
		my $answer    = &Wx::wxYES;
		my $menu_call = shift;

		set_range('selection')
			if $menu_call
			and $sel_begin != $sel_end
			and $sel_end - $sel_begin > $len;

		if (get_range() eq 'selection') {
			sniff_selection( $ep, $sel_begin, $sel_end, $len, $line );
		} elsif (get_range() eq 'document') {
			sniff_selection( $ep, 0, $ep->GetTextLength, $len, $line );
		} elsif (get_range() eq 'open_docs') {
			my $begin_doc = Kephra::Document::Data::current_nr();
			do {
				{
					next if $answer == &Wx::wxCANCEL;
					Kephra::Edit::_save_positions();
					$answer = sniff_selection
						( $ep, 0, $ep->GetTextLength, $len, $line );
					Kephra::Edit::_restore_positions();
				}
			} until ( Kephra::Document::Change::tab_right() == $begin_doc );
		}
	}

	sub sniff_selection {
		my ( $ep, $sel_begin, $sel_end, $len, $line ) = @_;
		my $l10n = Kephra::Config::Localisation::strings()->{dialog}{search}{confirm};
		my $answer;
		Kephra::Edit::Goto::_pos($sel_begin);
		$ep->BeginUndoAction();
		while ( _find_next() > -1 ) {
			last if $ep->GetCurrentPos + $len >= $sel_end;
			Kephra::Edit::_center_caret();
			$answer = Kephra::Dialog::get_confirm_3
				($l10n->{text}, $l10n->{title}, 100, 100);
			last if $answer == &Wx::wxCANCEL;
			if ($answer == &Wx::wxYES) {replace_selection()}
			else                    {$ep->SetCurrentPos( $ep->GetCurrentPos + 1 )}
		}
		$ep->EndUndoAction;
		Kephra::Edit::Goto::_pos( $ep->PositionFromLine($line) );
		Kephra::Edit::_center_caret();
		$answer;
	}
	refresh_replace_history();
	Kephra::Edit::_keep_focus();
}

1;

=head1 NAME

Kephra::Edit::Search - find and replace functions

=head1 DESCRIPTION

=cut