package Padre::Wx::Editor::Emacs;
use strict;
use warnings;

use Padre::Wx::Editor;

our $VERSION = '0.17';


package Padre::Wx::Editor;
use strict;
use warnings;

use List::Util ();

sub setup_emacs_mode {
	my ($self) = @_;

    $self->CmdKeyClear(ord('w'),Wx::wxMOD_ALT());
    $self->CmdKeyClear(ord('w'),Wx::wxMOD_CMD());
    $self->CmdKeyClear(ord('W'),Wx::wxMOD_ALT());
    $self->CmdKeyClear(ord('W'),Wx::wxMOD_CMD());
    $self->CmdKeyClear(ord('s'),Wx::wxMOD_CMD());
    $self->CmdKeyClear(ord('S'),Wx::wxMOD_CMD());

	$self->CmdKeyClear(Wx::WXK_SPACE,Wx::wxMOD_CMD());

	Wx::Event::EVT_KEY_DOWN( $self, sub {
		my ($self, $event) = @_;

		if (not Padre->ide->config->{emacs_mode}) {
			$event->Skip(1);
			return;
		}
		$self->emacs_mode($event);
		return;
	});

	return;
}

# TODO can we somehow remove the event handler when not needed?
# sub remove_emacs_mode {

my %subs;
$subs{CTRL} = {
	ord('X') => sub { # ctrl-x ...
		my $self = shift;
		$self->{emacs_ctrl_x_mode} = 1;
	},

	ord('S') => sub { # ctrl-x ctrl-s
		my $self = shift;
		if ($self->{emacs_ctrl_x_mode} == 1) {
			$self->{emacs_ctrl_x_mode} = 0;
			my $main   = Padre->ide->wx->main_window;
			$main->on_save;
		}
	},

	
	ord('A') => sub { # ctrl-x ...
		my $self = shift;
		$self->goto_beginning_of_line;	
	},
	ord('E') => sub { # ctrl-x ...
		my $self = shift;
		$self->goto_end_of_line;
	},
	Wx::WXK_SPACE => sub { # ctrl-space
		my $self = shift;
		if ($self->{emacs_select_start_mark}) {
			$self->{emacs_select_end_mark} = $self->GetCurrentPos;
		} else {
			$self->{emacs_select_start_mark} = $self->GetCurrentPos;
		}
	},
	ord('G') => sub { # ctrl-g ... stop
		my $self = shift;
		$self->{emacs_ctrl_x_mode} = 0;
	},
	ord('W') => sub { # ctrl-g ... stop
		my $self = shift;
		my $start_pos = $self->{emacs_select_start_mark};
		my $end_pos = $self->{emacs_select_end_mark} || $self->GetCurrentPos;
		$self->SetTargetStart($start_pos);
	    $self->SetTargetEnd($end_pos);
		$self->SetSelection($start_pos, $end_pos);
		Padre::Wx::Editor::text_cut_to_clipboard();
		$self->{emacs_select_start_mark} = undef;
		$self->{emacs_select_end_mark} = undef;
	},
	ord('K') => sub { # ctrl-g ... stop
		my $self = shift;
		my $start_pos = $self->GetCurrentPos;
		my $line = $self->LineFromPosition($start_pos);
		my $end_pos  = $self->GetLineEndPosition($line);
		$self->SetTargetStart($start_pos);
	    $self->SetTargetEnd($end_pos);
		$self->SetSelection($start_pos, $end_pos);
		Padre::Wx::Editor::text_cut_to_clipboard();
		$self->{emacs_select_start_mark} = undef;
		$self->{emacs_select_end_mark} = undef;
	},
	ord('Y') => sub {
		my $self = shift;
		Padre::Wx::Editor::text_paste_from_clipboard();
		$self->{emacs_select_start_mark} = undef;
		$self->{emacs_select_end_mark} = undef;
	},
};

$subs{META} = {
	ord('W') => sub { # copy
		my $self = shift;
		my $start_pos = $self->{emacs_select_start_mark};
		my $end_pos = $self->{emacs_select_end_mark} || $self->GetCurrentPos;
		$self->SetTargetStart($start_pos);
	    $self->SetTargetEnd($end_pos);
		$self->SetSelection($start_pos, $end_pos);
		Padre::Wx::Editor::text_copy_to_clipboard();
		$self->{emacs_select_start_mark} = undef;
		$self->{emacs_select_end_mark} = undef;
	},
};

$subs{CTRL_SHIFT} = {
		ord('-') => sub { # undo
			my $self = shift;
			$self->CmdKeyExecute(Wx::wxSTC_CMD_UNDO);
		},
		ord('_') => sub { # undo
			my $self = shift;
			$self->CmdKeyExecute(Wx::wxSTC_CMD_UNDO);
		}
};
		
sub emacs_mode {
	my ($self, $event) = @_;

	my $mod  = $event->GetModifiers || 0;
	my $code = $event->GetKeyCode;

	$event->Skip(0);

	# remove the bit ( Wx::wxMOD_META) set by Num Lock being pressed on Linux
	$mod = $mod & (Wx::wxMOD_ALT() + Wx::wxMOD_CMD() + Wx::wxMOD_SHIFT());
	
	my $modifier;
	$modifier = 'CTRL' if ($mod == Wx::wxMOD_CMD() );
	$modifier = 'CTRL_SHIFT' if ($mod == Wx::wxMOD_CMD() + Wx::wxMOD_SHIFT());
	$modifier = 'META' if ($mod == Wx::wxMOD_ALT() );

    unless ($modifier && $subs{$modifier}) {
    	$event->Skip(1);
		return;
    }

	if (my $sub = $subs{$modifier}{$code}) {
	    unless (ref $subs{$modifier}{$code} eq 'CODE') {
			warn "Invalid entry in 'subs' hash for code '$code' - expected code ref got : " . ref $subs{$modifier}{$code} ;
		}
		$sub->($self);
		return;
	} else {
		$event->Skip(1);
	} 
	
	return;
}

sub goto_end_of_line {
	my $self = shift;
	my $pos  = $self->GetCurrentPos;
	my $line = $self->LineFromPosition($pos);
	my $end  = $self->GetLineEndPosition($line);
	$self->GotoPos($end);
}

sub goto_beginning_of_line {
	my $self = shift;
	$self->Home;
}

1;
