The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Kephra::App::EditPanel;
our $VERSION = '0.14';

use strict;
use warnings; 
#
# internal API to config und app pointer
#
my $ref;
my %mouse_commands;
sub _ref     { $ref }
sub _set_ref { $ref = $_[0] if is($_[0]) }
sub _all_ref { Kephra::Document::Data::get_all_ep() }
sub is       { 1 if ref $_[0] eq 'Wx::StyledTextCtrl'}
sub _config  { Kephra::API::settings()->{editpanel} }
# splitter_pos
sub new {
	my $ep = Wx::StyledTextCtrl->new( Kephra::App::Window::_ref() );
	$ep->DragAcceptFiles(1) if Wx::wxMSW();
	return $ep;
}
sub gets_focus { Wx::Window::SetFocus( _ref() ) if is( _ref() ) }

# general settings
sub apply_settings_here {
	my $ep        = shift || _ref() || _create();
	return unless is($ep);
	my $conf      = _config();

	load_font($ep);

	# indicators: caret, selection, whitespaces...
	Kephra::App::EditPanel::Indicator::apply_all_here($ep);

	# Margins on left side
	Kephra::App::EditPanel::Margin::apply_settings_here($ep);

	#misc: scroll width, codepage, wordchars
	apply_autowrap_settings_here($ep);

	$ep->SetScrollWidth($conf->{scroll_width})
		unless $conf->{scroll_width} eq 'auto';

	#wxSTC_CP_UTF8 Wx::wxUNICODE()
	$ep->SetCodePage(65001);#
	set_word_chars_here($ep);

	# internal
	$ep->SetLayoutCache(&Wx::wxSTC_CACHE_PAGE);
	$ep->SetBufferedDraw(1);
	$conf->{contextmenu}{visible} eq 'default' ? $ep->UsePopUp(1) : $ep->UsePopUp(0);

	Kephra::Edit::eval_newline_sub();
	Kephra::Edit::Marker::define_marker($ep);
	connect_events($ep);
	Kephra::EventTable::add_call ( 'editpanel.focus', 'editpanel', sub {
		Wx::Window::SetFocus( _ref() ) unless $Kephra::temp{dialog}{active};
	}, __PACKAGE__ ) if $conf->{auto}{focus};
	Kephra::EventTable::add_call( 'document.text.change', 'update_edit_pos', sub {
		Kephra::Document::Data::attr('edit_pos', _ref()->GetCurrentPos());
	}, __PACKAGE__);
}

sub connect_events {
	my $ep = shift || _ref();
	my $trigger = \&Kephra::EventTable::trigger;
	my $config = _config();
	my $selection;
	my $rectangular_mode;
	my ($dragpos,$droppos);

	# override sci presets
	Wx::Event::EVT_DROP_FILES       ($ep, \&Kephra::File::add_dropped);
	Wx::Event::EVT_STC_START_DRAG   ($ep, -1, sub {
		my ( $ep, $event) = @_;
		$dragpos = $ep->GetCurrentPos();
		$selection = $ep->GetSelectedText();
		$rectangular_mode = $ep->SelectionIsRectangle();
		$event->Skip;
	});
	Wx::Event::EVT_STC_DRAG_OVER    ($ep, -1, sub { $droppos = $_[1]->GetPosition });
	Wx::Event::EVT_STC_DO_DROP      ($ep, -1, sub {
		my ( $ep, $event) = @_;
		$rectangular_mode
		 ? Kephra::Edit::paste_rectangular($selection, $ep, $dragpos, $droppos)
		 : $event->Skip;
	});

	Wx::Event::EVT_ENTER_WINDOW     ($ep,     sub { &$trigger('editpanel.focus')} );
	Wx::Event::EVT_LEFT_DOWN        ($ep,     sub { 
		my ($ep, $event) = @_;
		my $nr = Kephra::App::EditPanel::Margin::in_nr( $event->GetX, $ep );
		if ($nr == -1) { Kephra::Edit::copy() if clicked_on_selection($event) }
		else { Kephra::App::EditPanel::Margin::on_left_click($ep, $event, $nr) } 

		$event->Skip;
	});
	Wx::Event::EVT_MIDDLE_DOWN      ($ep,     sub {
		my ($ep, $event) = @_;
		my $nr = Kephra::App::EditPanel::Margin::in_nr( $event->GetX, $ep );
		# click is above text area
		if ($nr == -1) {
			if ($event->LeftIsDown){
				Kephra::Edit::paste();
				set_caret_on_cursor($event);
			}
			# just middle clicked
			else {
				if ($ep->GetSelectedText){
					if (clicked_on_selection($event, $ep)) {
						Kephra::Edit::Search::set_selection_as_find_item();
						Kephra::Edit::Search::find_next();
					} 
					else { insert_selection_at_cursor($event, $ep) }
				} 
				else { Kephra::Edit::Goto::last_edit() }
			}
		} 
		else { Kephra::App::EditPanel::Margin::on_middle_click($ep, $event, $nr) }
	});
	Wx::Event::EVT_RIGHT_DOWN       ($ep,     sub {
		my ($ep, $event) = @_;
		my $nr = Kephra::App::EditPanel::Margin::in_nr( $event->GetX, $ep );
		if ($nr == -1) {
			if ($event->LeftIsDown){
				Kephra::Edit::_selection_left_to_right($ep)
					? Kephra::Edit::cut()
					: Kephra::Edit::clear();
				set_caret_on_cursor($event);
			} else {
				my $mconf = $config->{contextmenu};
				if ($mconf->{visible} eq 'custom'){
					my $menu_id = $ep->GetSelectedText
						? $mconf->{ID_selection} : $mconf->{ID_normal};
					my $menu = Kephra::App::ContextMenu::get($menu_id);
					$ep->PopupMenu($menu, $event->GetX, $event->GetY) if $menu;
				}
			} 
		} else {Kephra::App::EditPanel::Margin::on_right_click($ep, $event, $nr)}
	});
	#Wx::EVT_SET_FOCUS               ($ep,     sub {});
	Wx::Event::EVT_STC_SAVEPOINTREACHED($ep, -1, \&Kephra::File::savepoint_reached);
	Wx::Event::EVT_STC_SAVEPOINTLEFT($ep, -1, \&Kephra::File::savepoint_left);
	# -DEP
	#Wx::Event::EVT_STC_MARGINCLICK  ($ep, -1, \&Kephra::App::EditPanel::Margin::on_left_click);
	Wx::Event::EVT_STC_CHANGE       ($ep, -1, sub {&$trigger('document.text.change')} );
	Wx::Event::EVT_STC_UPDATEUI     ($ep, -1, sub {
		my ( $ep, $event) = @_;
		my ( $sel_beg, $sel_end ) = $ep->GetSelection;
		my $is_sel = $sel_beg != $sel_end;
		my $was_sel = Kephra::Document::Data::attr('text_selected');
		Kephra::Document::Data::attr('text_selected', $is_sel);
		&$trigger('document.text.select') if $is_sel xor $was_sel;
		&$trigger('caret.move');
	});

	Wx::Event::EVT_KEY_DOWN         ($ep,     sub {
		my ($ep, $event) = @_;
		#$ep = _ref(); 
		my $key = $event->GetKeyCode +
			1000 * ($event->ShiftDown + $event->ControlDown*2 + $event->AltDown*4);
		# reacting on shortkeys that are defined in the Commanlist
		#print "$key\n";
		return if Kephra::CommandList::run_cmd_by_keycode($key);
		# reacting on Enter
		if ($key ==  &Wx::WXK_RETURN) {
			if ($config->{auto}{brace}{indention}) {
				my $pos  = $ep->GetCurrentPos - 1;
				my $char = $ep->GetCharAt($pos);
				if      ($char == 123) {
					return Kephra::Edit::Format::blockindent_open($pos);
				} elsif ($char == 125) {
					return Kephra::Edit::Format::blockindent_close($pos);
				}
			}
			$config->{auto}{indention}
				? Kephra::Edit::Format::autoindent()
				: $ep->CmdKeyExecute(&Wx::wxSTC_CMD_NEWLINE) ;
		}
		# scintilla handles the rest of the shortkeys
		else { $event->Skip }
		#SCI_SETSELECTIONMODE
		#($key == 350){use Kephra::Ext::Perl::Syntax; Kephra::Ext::Perl::Syntax::check()};
	});
}
sub create_mouse_binding {
	my @cmd = qw(left-middle left-right left-selection 
	             middle middle-selected middle-selection
	);

	if (_config()->{control}{use_mouse_function}) {
		my $config = _config()->{control}{mouse_function};
		$mouse_commands{$_} = Kephra::Macro::create_from_cmd_list($config->{$_})
				for @cmd
	}
	else { $mouse_commands{$_} = sub {} for @cmd }
}
sub set_caret_on_cursor {
	my $event = shift;
	my $ep = shift || _ref();
	return unless ref $event eq 'Wx::MouseEvent' and is($ep);
	my $pos = cursor_2_caret_pos($event, $ep);
	$pos = $ep->GetCurrentPos() if $pos == -1;
	$ep->SetSelection( $pos, $pos );
}
sub clicked_on_selection {
	my $event = shift;
	my $ep = shift || _ref();
	return unless ref $event eq 'Wx::MouseEvent' and is($ep);
	my ($start, $end) = $ep->GetSelection();
	my $pos = cursor_2_caret_pos($event, $ep);
	return 1 if $start != $end and $pos >= $start and $pos <= $end;
}
sub insert_selection_at_cursor {
	my $event = shift;
	my $ep = shift || _ref();
	my $pos = cursor_2_caret_pos($event, $ep);
	Kephra::Edit::insert_text($ep->GetSelectedText(), $pos) if $pos > -1;
}
sub cursor_2_caret_pos {
	my $event = shift;
	my $ep = shift || _ref();
	return -1 unless ref $event eq 'Wx::MouseEvent' and is($ep);
	my $pos = $ep->PositionFromPointClose($event->GetX, $event->GetY);
	if ($pos == -1) {
		my $width = Kephra::App::EditPanel::Margin::width($ep)
		          + Kephra::App::EditPanel::Margin::get_text_width();
		my $y = $event->GetY;
		my $line = $ep->LineFromPosition( $ep->PositionFromPointClose($width, $y) );
		$pos = $ep->GetLineEndPosition ($line);
		my $font_size = _config()->{font}{size};
		if ($line == 0 and $y > $font_size + 12) {
			my $lcc = 0;
			$pos = $ep->PositionFromPointClose($width-1, $y);
			while ($pos == -1 and $lcc < $ep->GetLineCount() ){
				$lcc++; # line counter
				$y += $font_size;
				$pos = $ep->PositionFromPointClose($width, $y);
			}
			return -1 if $pos == -1;
			return $ep->PositionFromLine(  $ep->LineFromPosition($pos) - $lcc );
		}
	}
	$pos;
}

sub disconnect_events {
	my $ep = shift || _ref();
	Wx::Event::EVT_STC_CHANGE  ($ep, -1, sub {});
	Wx::Event::EVT_STC_UPDATEUI($ep, -1, sub {});
}

sub set_contextmenu_custom  { set_contextmenu('custom') }
sub set_contextmenu_default { set_contextmenu('default')}
sub set_contextmenu_none    { set_contextmenu('none')   }
sub set_contextmenu {
	my $mode = shift;
	$mode = 'custom' unless $mode;
	my $ep = _ref();
	$mode eq 'default' ? $ep->UsePopUp(1) : $ep->UsePopUp(0);
	_config()->{contextmenu}{visible} = $mode;
}
sub get_contextmenu { _config()->{contextmenu}{visible} }
#
sub set_word_chars { set_word_chars_here($_) for @{_all_ref()} }
sub set_word_chars_here { 
	my $ep = shift || _ref();
	my $conf = _config();
	if ( $conf->{word_chars} ) {
		$ep->SetWordChars( $conf->{word_chars} );
	} else {
		$ep->SetWordChars( '$%&-@_abcdefghijklmnopqrstuvwxyzäöüßABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ0123456789' );
	}
}


# line wrap
sub apply_autowrap_settings { apply_autowrap_settings_here($_) for @{_all_ref()} }
sub apply_autowrap_settings_here {
	my $ep = shift || _ref();
	$ep->SetWrapMode( _config()->{line_wrap} );
	Kephra::EventTable::trigger('editpanel.autowrap');
}

sub get_autowrap_mode { _config()->{line_wrap} == &Wx::wxSTC_WRAP_WORD}
sub switch_autowrap_mode {
	_config()->{line_wrap} = get_autowrap_mode()
		? &Wx::wxSTC_WRAP_NONE
		: &Wx::wxSTC_WRAP_WORD;
	apply_autowrap_settings();
}

# font settings
sub load_font {
	my $ep = shift || _ref();
	my ( $fontweight, $fontstyle ) = ( &Wx::wxNORMAL, &Wx::wxNORMAL );
	my $font = _config()->{font};
	$fontweight = &Wx::wxLIGHT  if $font->{weight} eq 'light';
	$fontweight = &Wx::wxBOLD   if $font->{weight} eq 'bold';
	$fontstyle  = &Wx::wxSLANT  if $font->{style}  eq 'slant';
	$fontstyle  = &Wx::wxITALIC if $font->{style}  eq 'italic';
	my $wx_font = Wx::Font->new( $font->{size}, &Wx::wxDEFAULT, 
		$fontstyle, $fontweight, 0, $font->{family} );
	$ep->StyleSetFont( &Wx::wxSTC_STYLE_DEFAULT, $wx_font ) if $wx_font->Ok > 0;
}
sub change_font {
	my ( $fontweight, $fontstyle ) = ( &Wx::wxNORMAL, &Wx::wxNORMAL );
	my $font_config = _config()->{font};
	$fontweight = &Wx::wxLIGHT  if ( $$font_config{weight} eq 'light' );
	$fontweight = &Wx::wxBOLD   if ( $$font_config{weight} eq 'bold' );
	$fontstyle  = &Wx::wxSLANT  if ( $$font_config{style}  eq 'slant' );
	$fontstyle  = &Wx::wxITALIC if ( $$font_config{style}  eq 'italic' );
	my $oldfont = Wx::Font->new( $$font_config{size}, &Wx::wxDEFAULT, $fontstyle,
		$fontweight, 0, $$font_config{family} );
	my $newfont = Kephra::Dialog::get_font( $oldfont );

	if ( $newfont->Ok > 0 ) {
		($fontweight, $fontstyle) = ($newfont->GetWeight, $newfont->GetStyle);
		$$font_config{size}   = $newfont->GetPointSize;
		$$font_config{family} = $newfont->GetFaceName;
		$$font_config{weight} = 'normal';
		$$font_config{weight} = 'light' if $fontweight == &Wx::wxLIGHT;
		$$font_config{weight} = 'bold' if $fontweight == &Wx::wxBOLD;
		$$font_config{style}  = 'normal';
		$$font_config{style}  = 'slant' if $fontstyle == &Wx::wxSLANT;
		$$font_config{style}  = 'italic' if $fontstyle == &Wx::wxITALIC;
		Kephra::Document::SyntaxMode::reload($_) for @{Kephra::Document::Data::all_nr()};
		Kephra::App::EditPanel::Margin::apply_line_number_width();
	}
}
#
sub zoom_in {
	my $ep = shift || _ref();
	$ep->SetZoom( $ep->GetZoom()+1 ) if $ep->GetZoom() < 45;
}
sub zoom_out {
	my $ep = shift || _ref();
	$ep->SetZoom( $ep->GetZoom()-1 ) if $ep->GetZoom() > -10;
}
sub zoom_normal {
	my $ep = shift || _ref();
	$ep->SetZoom( 0 ) ;
}
#
# auto indention
sub get_autoindention { Kephra::App::EditPanel::_config()->{auto}{indention} }
sub set_autoindention {
	Kephra::App::EditPanel::_config()->{auto}{indention} = shift;
	Kephra::Edit::eval_newline_sub();
}
sub switch_autoindention { set_autoindention( get_autoindention() ^ 1 ) } 
sub set_autoindent_on    { set_autoindention( 1 ) }
sub set_autoindent_off   { set_autoindention( 0 ) }
#
# brace indention
sub get_braceindention { Kephra::App::EditPanel::_config()->{auto}{brace}{indention}}
sub set_braceindention {
	Kephra::App::EditPanel::_config()->{auto}{brace}{indention} = shift;
	Kephra::Edit::eval_newline_sub();
}
sub switch_braceindention { set_braceindention( get_braceindention() ^ 1 ) }
sub set_braceindent_on    { set_braceindention( 1 ) }
sub set_braceindent_off   { set_braceindention( 0 ) }
#
#
sub get_bracecompletion { Kephra::App::EditPanel::_config()->{auto}{brace}{make} }
sub set_bracecompletion {
	Kephra::App::EditPanel::_config()->{auto}{brace}{make} = shift;
}
sub switch_bracecompletion{  set_bracecompletion( get_bracecompletion() ^ 1 ) }
1;
#EVT_STC_CHARADDED EVT_STC_MODIFIED
#wxSTC_CP_UTF8 wxSTC_CP_UTF16 Wx::wxUNICODE()
#wxSTC_WS_INVISIBLE wxSTC_WS_VISIBLEALWAYS
#$ep->StyleSetForeground (wxSTC_STYLE_CONTROLCHAR, Wx::Colour->new(0x55, 0x55, 0x55));
#$ep->CallTipShow(3,"testtooltip\n next line"); #tips
#SetSelectionMode(wxSTC_SEL_RECTANGLE);