The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
package Padre::Wx::Panel::Debugger;

use 5.010;
use strict;
use warnings;
no if $] > 5.017010, warnings => 'experimental::smartmatch';

use utf8;
use Padre::Util              ();
use Padre::Constant          ();
use Padre::Wx                ();
use Padre::Wx::Util          ();
use Padre::Wx::Icon          ();
use Padre::Wx::Role::View    ();
use Padre::Wx::FBP::Debugger ();
use Padre::Breakpoints       ();
use Padre::Logger;
use Debug::Client 0.20 ();

our $VERSION = '1.00';
our @ISA     = qw{
	Padre::Wx::Role::View
	Padre::Wx::FBP::Debugger
};

use constant {
	BLANK      => qq{},
	RED        => Wx::Colour->new('red'),
	DARK_GREEN => Wx::Colour->new( 0x00, 0x90, 0x00 ),
	BLUE       => Wx::Colour->new('blue'),
	GRAY       => Wx::Colour->new('gray'),
	DARK_GRAY  => Wx::Colour->new( 0x7f, 0x7f, 0x7f ),
	BLACK      => Wx::Colour->new('black'),
};


#######
# new
#######
sub new {
	my $class = shift;
	my $main  = shift;
	my $panel = shift || $main->right;

	# Create the panel
	my $self = $class->SUPER::new($panel);

	$self->set_up;

	return $self;
}

###############
# Make Padre::Wx::Role::View happy
###############

sub view_panel {
	'right';
}

sub view_label {
	Wx::gettext('Debugger');
}


sub view_close {
	$_[0]->main->show_debugger(0);
}

sub view_icon {
	Padre::Wx::Icon::find('actions/morpho3');
}

###############
# Make Padre::Wx::Role::View happy end
###############


#######
# Method set_up
#######
sub set_up {
	my $self = shift;
	my $main = $self->main;

	$self->{debug_client_version} = $Debug::Client::VERSION;
	$self->{debug_client_version} =~ s/^(\d.\d{2}).*/$1/;

	$self->{client}           = undef;
	$self->{file}             = undef;
	$self->{save}             = {};
	$self->{trace_status}     = 'Trace = off';
	$self->{var_val}          = {};
	$self->{local_values}     = {};
	$self->{global_values}    = {};
	$self->{set_bp}           = 0;
	$self->{fudge}            = 0;
	$self->{local_variables}  = 0;
	$self->{global_variables} = 0;

	#turn off unless in project
	$self->{show_global_variables}->Disable;
	$self->{show_local_variables}->Disable;

	# $self->{show_local_variables}->SetValue(1);
	# $self->{local_variables} = 1;
	$self->{show_local_variables}->SetValue(0);
	$self->{local_variables} = 0;

	# Setup the debug button icons
	$self->{debug}->SetBitmapLabel( Padre::Wx::Icon::find('actions/morpho2') );
	$self->{debug}->Enable;

	$self->{step_in}->SetBitmapLabel( Padre::Wx::Icon::find('actions/step_in') );
	$self->{step_in}->Hide;

	$self->{step_over}->SetBitmapLabel( Padre::Wx::Icon::find('actions/step_over') );
	$self->{step_over}->Hide;

	$self->{step_out}->SetBitmapLabel( Padre::Wx::Icon::find('actions/step_out') );
	$self->{step_out}->Hide;

	$self->{run_till}->SetBitmapLabel( Padre::Wx::Icon::find('actions/run_till') );
	$self->{run_till}->Hide;

	$self->{display_value}->SetBitmapLabel( Padre::Wx::Icon::find('stock/code/stock_macro-watch-variable') );
	$self->{display_value}->Hide;

	$self->{quit_debugger}->SetBitmapLabel( Padre::Wx::Icon::find('actions/red_cross') );
	$self->{quit_debugger}->Enable;

	$self->{list_action}->SetBitmapLabel( Padre::Wx::Icon::find('actions/4c-l') );
	$self->{list_action}->Disable;

	$self->{dot}->SetBitmapLabel( Padre::Wx::Icon::find('actions/dot') );
	$self->{dot}->Disable;

	$self->{view_around}->SetBitmapLabel( Padre::Wx::Icon::find('actions/76-v') );
	$self->{view_around}->Disable;

	$self->{stacktrace}->SetBitmapLabel( Padre::Wx::Icon::find('actions/54-t') );
	$self->{stacktrace}->Disable;

	$self->{module_versions}->SetBitmapLabel( Padre::Wx::Icon::find('actions/4d-m') );
	$self->{module_versions}->Disable;

	$self->{all_threads}->SetBitmapLabel( Padre::Wx::Icon::find('actions/45-e') );
	$self->{all_threads}->Disable;

	$self->{trace}->Disable;
	$self->{evaluate_expression}->SetBitmapLabel( Padre::Wx::Icon::find('actions/pux') );
	$self->{evaluate_expression}->Disable;
	$self->{expression}->SetValue(BLANK);
	$self->{expression}->Disable;

	$self->{running_bp}->SetBitmapLabel( Padre::Wx::Icon::find('actions/bub') );
	$self->{running_bp}->Disable;

	$self->{sub_names}->SetBitmapLabel( Padre::Wx::Icon::find('actions/53-s') );
	$self->{sub_names}->Disable;

	$self->{display_options}->SetBitmapLabel( Padre::Wx::Icon::find('actions/6f-o') );
	$self->{display_options}->Disable;

	$self->{watchpoints}->SetBitmapLabel( Padre::Wx::Icon::find('actions/wuw') );
	$self->{watchpoints}->Disable;
	$self->{raw}->SetBitmapLabel( Padre::Wx::Icon::find('actions/raw') );
	$self->{raw}->Disable;

	# Setup columns names and order here
	my @column_headers = qw( Variable Value );
	my $index          = 0;
	for my $column_header (@column_headers) {
		$self->{variables}->InsertColumn( $index++, Wx::gettext($column_header) );
	}

	# Tidy the list
	Padre::Wx::Util::tidy_list( $self->{variables} );

	return;
}

#######
# Composed Method,
# display any relation db
#######
sub update_variables {
	my $self              = shift;
	my $var_val_ref       = shift;
	my $local_values_ref  = shift;
	my $global_values_ref = shift;
	my $editor            = $self->current->editor;

	# clear ListCtrl items
	$self->{variables}->DeleteAllItems;

	my $index = 0;
	my $item  = Wx::ListItem->new;
	foreach my $var ( keys %{$var_val_ref} ) {

		$item->SetId($index);
		$self->{variables}->InsertItem($item);
		$self->{variables}->SetItemTextColour( $index, BLACK );

		$self->{variables}->SetItem( $index,   0, $var );
		$self->{variables}->SetItem( $index++, 1, $var_val_ref->{$var} );
	}

	if ( $self->{local_variables} == 1 ) {
		foreach my $var ( keys %{$local_values_ref} ) {

			$item->SetId($index);
			$self->{variables}->InsertItem($item);
			$self->{variables}->SetItemTextColour( $index, BLUE );

			$self->{variables}->SetItem( $index,   0, $var );
			$self->{variables}->SetItem( $index++, 1, $local_values_ref->{$var} );
		}
	}
	if ( $self->{global_variables} == 1 ) {
		foreach my $var ( keys %{$global_values_ref} ) {

			$item->SetId($index);
			$self->{variables}->InsertItem($item);
			$self->{variables}->SetItemTextColour( $index, DARK_GRAY );

			$self->{variables}->SetItem( $index,   0, $var );
			$self->{variables}->SetItem( $index++, 1, $global_values_ref->{$var} );
		}
	}

	# Tidy the list
	Padre::Wx::Util::tidy_list( $self->{variables} );

	return;
}


#######
# sub debug_perl
#######
sub debug_perl {
	my $self = shift;
	my $arg_ref = shift || { debug => 1 };

	my $main     = $self->main;
	my $current  = $self->current;
	my $document = $current->document;
	my $editor   = $current->editor;

	# test for valid perl document
	if ( !$document || $document->mimetype !~ m/perl/ ) {
		return;
	}

	# display panels
	$main->show_debugoutput(1);

	if ( $self->{client} ) {
		$main->error( Wx::gettext('Debugger is already running') );
		return;
	}
	unless ( $document->isa('Padre::Document::Perl') ) {
		$main->error( Wx::gettext('Not a Perl document') );
		return;
	}

	# Apply the user's save-on-run policy
	# TO DO: Make this code suck less
	my $config = $main->config;
	if ( $config->run_save eq 'same' ) {
		$main->on_save;
	} elsif ( $config->run_save eq 'all_files' ) {
		$main->on_save_all;
	} elsif ( $config->run_save eq 'all_buffer' ) {
		$main->on_save_all;
	}

	#TODO I think this is where the Fup filenames are comming from, see POD in main
	# Get the filename
	# my $filename = defined( $document->{file} ) ? $document->{file}->filename : undef;

	#changed due to define is deprecated in perl 5.15.7
	my $filename;
	if ( defined $document->{file} ) {
		$filename = $document->{file}->filename;
	} else {
		$filename = undef;
	}

	# TODO: improve the message displayed to the user
	# If the document is not saved, simply return for now
	return unless $filename;

	#TODO how do we add debug options at startup such as threaded mode

	# Set up the debugger
	my $host = '127.0.0.1';
	my $port = 24642 + int rand(1000); # TODO make this configurable?
	SCOPE: {
		local $ENV{PERLDB_OPTS} = "RemotePort=$host:$port";
		my ( $cmd, $ref ) = $document->get_command($arg_ref);

		#TODO: consider pushing the chdir into run_command (as there is a hidden 'cd' in it)
		my $dir = Cwd::cwd;
		chdir $arg_ref->{run_directory} if ( exists( $arg_ref->{run_directory} ) );
		$main->run_command($cmd);
		chdir $dir;
	}

	# Bootstrap the debugger
	# require Debug::Client;
	$self->{client} = Debug::Client->new(
		host => $host,
		port => $port,
	);

	#ToDo remove when Debug::Client 0.22 is released.
	if ( $self->{debug_client_version} eq '0.20' ) {
		$self->{client}->listener;
	}
	$self->{file} = $filename;

	#Now we ask where are we
	#ToDo remove when Debug::Client 0.22 is released.
	if ( $self->{debug_client_version} eq '0.20' ) {
		$self->{client}->get;
	}
	$self->{client}->get_lineinfo;

	my $save = ( $self->{save}->{$filename} ||= {} );

	if ( $self->{set_bp} == 0 ) {

		# get bp's from db and set b|B (remember it's a toggle) hence we do this only once
		$self->_get_bp_db;
		$self->{set_bp} = 1;
	}

	unless ( $self->_set_debugger ) {
		$main->error( Wx::gettext('Debugging failed. Did you check your program for syntax errors?') );
		$self->debug_quit;
		return;
	}

	return 1;
}

#######
# sub _set_debugger
#######
sub _set_debugger {
	my $self    = shift;
	my $main    = $self->main;
	my $current = $self->current;
	my $editor  = $current->editor or return;
	my $file    = $self->{client}->{filename} or return;
	my $row     = $self->{client}->{row} or return;

	# Open the file if needed
	if ( $editor->{Document}->filename ne $file ) {
		$main->setup_editor($file);
		$editor = $main->current->editor;
		if ( $self->main->{breakpoints} ) {
			$self->main->{breakpoints}->on_refresh_click;
		}

		# we only want to do this if we are loading other files in this packages of ours
		$self->_bp_autoload();
	}

	$editor->goto_line_centerize( $row - 1 );

	#### TODO this was taken from the Padre::Wx::Syntax::start() and changed a bit.
	# They should be reunited soon !!!! (or not)

	$editor->MarkerDeleteAll(Padre::Constant::MARKER_LOCATION);
	$editor->MarkerAdd( $row - 1, Padre::Constant::MARKER_LOCATION );

	# update variables and output
	$self->_output_variables;

	return 1;
}

#######
# sub running
#######
sub running {
	my $self = shift;
	my $main = $self->main;

	unless ( $self->{client} ) {

		return;
	}

	return !!$self->current->editor;
}

#######
# sub debug_quit
#######
sub debug_quit {
	my $self = shift;
	my $main = $self->main;
	$self->running or return;

	# Clean up the GUI artifacts
	$self->current->editor->MarkerDeleteAll( Padre::Constant::MARKER_LOCATION() );

	# Detach the debugger
	$self->{client}->quit;
	delete $self->{client};

	$self->{trace_status} = 'Trace = off';
	$self->{trace}->SetValue(0);
	$self->{trace}->Disable;
	$self->{evaluate_expression}->Disable;
	$self->{expression}->Disable;
	$self->{stacktrace}->Disable;

	$self->{module_versions}->Disable;
	$self->{all_threads}->Disable;
	$self->{list_action}->Disable;
	$self->{dot}->Disable;
	$self->{view_around}->Disable;

	$self->{running_bp}->Disable;

	# $self->{add_watch}->Disable;
	# $self->{delete_watch}->Disable;
	$self->{raw}->Disable;
	$self->{watchpoints}->Disable;
	$self->{sub_names}->Disable;
	$self->{display_options}->Disable;

	$self->{step_in}->Hide;
	$self->{step_over}->Hide;
	$self->{step_out}->Hide;
	$self->{run_till}->Hide;
	$self->{display_value}->Hide;

	$self->{show_global_variables}->Disable;
	$self->{show_local_variables}->Disable;

	$self->{var_val}       = {};
	$self->{local_values}  = {};
	$self->{global_values} = {};
	$self->update_variables( $self->{var_val}, $self->{local_values}, $self->{global_values} );

	$self->{debug}->Show;

	# $self->show_debug_output(0);
	$main->show_debugoutput(0);
	return;
}

sub update_debug_user_interface {
	my $self = shift;
	my $output = shift;
	my $main = $self->main;

	my $module = $self->{client}->module || BLANK;
	$self->{client}->get_lineinfo;

	if ( $module eq '<TERMINATED>' ) {
		TRACE('TERMINATED') if DEBUG;
		$self->{trace_status} = 'Trace = off';
		$main->{debugoutput}->debug_status( $self->{trace_status} );
		$self->debug_quit;
		return;
	}

	if ( ! $output ) {
		#ToDo remove when Debug::Client 0.22 is released.
		if ( $self->{debug_client_version} eq '0.20' ) {
			$output = $self->{client}->buffer;
		} else {
			 $output = $self->{client}->get_buffer;
		}
	}
	$main->{debugoutput}->debug_output( $output );
	$self->_set_debugger;
}

#######
# Method debug_step_in
#######
sub debug_step_in {
	my $self = shift;

	my @list_request;
	eval { @list_request = $self->{client}->step_in(); };
	$self->update_debug_user_interface;

	return;
}

#######
# Method debug_step_over
#######
sub debug_step_over {
	my $self = shift;
	my $main = $self->main;

	my @list_request;
	eval { @list_request = $self->{client}->step_over(); };
	$self->update_debug_user_interface;

	return;
}

#######
# Method debug_step_out
#######
sub debug_step_out {
	my $self = shift;
	my $main = $self->main;

	my @list_request;
	eval { @list_request = $self->{client}->step_out(); };
	$self->update_debug_user_interface;

	return;
}

#######
# Method debug_run_till
#######
sub debug_run_till {
	my $self  = shift;
	my $param = shift;
	my $main  = $self->main;

	my @list_request;
	eval { @list_request = $self->{client}->run($param); };
	$self->update_debug_user_interface;

	return;
}

#######
# sub display_trace
# TODO this is yuck!
#######
sub _display_trace {
	my $self = shift;
	my $main = $self->main;

	$self->running or return;
	my $trace_on = ( @_ ? ( $_[0] ? 1 : 0 ) : 1 );

	if ( $trace_on == 1 && $self->{trace_status} eq 'Trace = on' ) {
		return;
	}

	if ( $trace_on == 1 && $self->{trace_status} eq 'Trace = off' ) {

		# $self->{trace_status} = $self->{client}->_set_option('frame=6');
		$self->{trace_status} = $self->{client}->toggle_trace();
		$main->{debugoutput}->debug_status( $self->{trace_status} );
		return;
	}

	if ( $trace_on == 0 && $self->{trace_status} eq 'Trace = off' ) {
		return;
	}

	if ( $trace_on == 0 && $self->{trace_status} eq 'Trace = on' ) {

		# $self->{trace_status} = $self->{client}->_set_option('frame=1');
		$self->{trace_status} = $self->{client}->toggle_trace();
		$main->{debugoutput}->debug_status( $self->{trace_status} );
		return;
	}

	return;
}


####### v1
#TODO Debug -> menu when in trunk
#######
sub debug_perl_show_value {
	my $self = shift;
	my $main = $self->main;
	$self->running or return;

	my $text = $self->_debug_get_variable or return;

	my $value = eval { $self->{client}->get_value($text) };
	if ($@) {
		$main->error( sprintf( Wx::gettext("Could not evaluate '%s'"), $text ) );
		return;
	}

	$self->main->message("$text = $value");

	return;
}

####### v1
# sub _debug_get_variable $line
#######
sub _debug_get_variable {
	my $self = shift;
	my $document = $self->current->document or return;

	my ( $location, $text ) = $document->get_current_symbol;

	if ( not $text or $text !~ m/^[\$@%\\]/smx ) {
		$self->main->error(
			sprintf(
				Wx::gettext(
					"'%s' does not look like a variable. First select a variable in the code and then try again."),
				$text
			)
		);
		return;
	}
	return $text;
}

####### v1
# Method display_value
#######
sub display_value {
	my $self = shift;
	$self->running or return;

	my $variable = $self->_debug_get_variable or return;

	$self->{var_val}{$variable} = BLANK;

	# $self->update_variables( $self->{var_val} );
	$self->_output_variables;

	return;
}

#######
# Method quit
#######
sub quit {
	my $self = shift;
	if ( $self->{client} ) {
		$self->debug_quit;
	}
	return;
}

#######
# Composed Method _output_variables
#######
sub _output_variables {
	my $self     = shift;
	my $document = $self->current->document;
	$self->{current_file} = $document->filename;

	foreach my $variable ( keys %{ $self->{var_val} } ) {
		my $value;
		eval { $value = $self->{client}->get_value($variable); };
		if ($@) {

			#ignore error
		} else {
			my $search_text = 'Use of uninitialized value';
			unless ( $value =~ m/$search_text/ ) {
				$self->{var_val}{$variable} = $value;
			}
		}
	}

	# only get local variables if required
	if ( $self->{local_variables} == 1 ) {
		$self->get_local_variables;
	}

	# Only enable global variables if we are debuging in a project
	# why dose $self->{project_dir} contain the root when no magic file present
	#TODO trying to stop debug X & V from crashing
	my @magic_files = qw { Makefile.PL Build.PL dist.ini };
	my $in_project  = 0;
	require File::Spec;
	foreach (@magic_files) {
		if ( -e File::Spec->catfile( $self->{project_dir}, $_ ) ) {
			$in_project = 1;
		}
	}
	if ($in_project) {

		$self->{show_global_variables}->Enable;

		if ( $self->{current_file} =~ m/pm$/ ) {
			$self->get_global_variables;

		} else {
			$self->{show_global_variables}->Disable;

			# get ride of stale values
			$self->{global_values} = {};
		}
	}

	$self->update_variables( $self->{var_val}, $self->{local_values}, $self->{global_values} );

	return;
}

#######
# Composed Method get_variables
#######
sub get_local_variables {
	my $self = shift;

	my $auto_values = $self->{client}->get_y_zero;

	$auto_values =~ s/^([\$\@\%]\w+)/:;$1/xmg;

	my @auto = split m/^:;/xm, $auto_values;

	#remove ghost at begining
	shift @auto;

	# This is better I think, it's quicker
	$self->{local_values} = {};

	foreach (@auto) {

		$_ =~ m/(.*) = (.*)/sm;

		if ( defined $1 ) {
			if ( defined $2 ) {
				$self->{local_values}->{$1} = $2;
			} else {
				$self->{local_values}->{$1} = BLANK;
			}
		}
	}

	return;
}

#######
# Composed Method get_variables
#######
sub get_global_variables {
	my $self = shift;

	my $var_regex   = '!(INC|ENV|SIG)';
	my $auto_values = $self->{client}->get_x_vars($var_regex);

	$auto_values =~ s/^((?:[\$\@\%]\w+)|(?:[\$\@\%]\S+)|(?:File\w+))/:;$1/xmg;

	my @auto = split m/^:;/xm, $auto_values;

	#remove ghost at begining
	shift @auto;

	# This is better I think, it's quicker
	$self->{global_values} = {};

	foreach (@auto) {

		$_ =~ m/(.*)(?: = | => )(.*)/sm;

		if ( defined $1 ) {
			if ( defined $2 ) {
				$self->{global_values}->{$1} = $2;
			} else {
				$self->{global_values}->{$1} = BLANK;
			}
		}
	}

	return;
}

#######
# Internal method _setup_db connector
#######
sub _setup_db {
	my $self = shift;

	# set padre db relation
	$self->{debug_breakpoints} = ('Padre::DB::DebugBreakpoints');

	return;
}

#######
# Internal Method _get_bp_db
# display relation db
#######
sub _get_bp_db {
	my $self     = shift;
	my $editor   = $self->current->editor;
	my $document = $self->current->document;

	$self->_setup_db();

	$self->{project_dir}  = $document->project_dir;
	$self->{current_file} = $document->filename;

	TRACE("current file from _get_bp_db: $self->{current_file}") if DEBUG;

	my $sql_select = 'ORDER BY filename ASC, line_number ASC';
	my @tuples     = $self->{debug_breakpoints}->select($sql_select);

	for ( 0 .. $#tuples ) {

		# if ( $tuples[$_][1] =~ m/^$self->{current_file}$/ ) {
		if ( $tuples[$_][1] eq $self->{current_file} ) {

			if ( $self->{client}->set_breakpoint( $tuples[$_][1], $tuples[$_][2] ) == 1 ) {
				$editor->MarkerAdd( $tuples[$_][2] - 1, Padre::Constant::MARKER_BREAKPOINT() );
			} else {
				$editor->MarkerAdd( $tuples[$_][2] - 1, Padre::Constant::MARKER_NOT_BREAKABLE() );

				#wright $tuples[$_][3] = 0
				Padre::DB->do( 'update debug_breakpoints SET active = ? WHERE id = ?', {}, 0, $tuples[$_][0], );
			}

		}

	}

	#TODO tidy up
	# no more bleading BP's
	for ( 0 .. $#tuples ) {

		if ( $tuples[$_][1] =~ m/^$self->{project_dir}/ ) {
			if ( $tuples[$_][1] ne $self->{current_file} ) {
				if ( $self->{client}->__send("f $tuples[$_][1]") !~ m/^No file matching/ ) {
					unless ( $self->{client}->set_breakpoint( $tuples[$_][1], $tuples[$_][2] ) ) {
						Padre::DB->do( 'update debug_breakpoints SET active = ? WHERE id = ?', {}, 0, $tuples[$_][0], );
					}
				}
			}
		}
	}
	if ( $self->main->{breakpoints} ) {
		$self->main->{breakpoints}->on_refresh_click();
	}

	#let's do some boot n braces
	$self->{client}->__send("f $self->{current_file}");
	return;
}

#######
# Composed Method, _bp_autoload
# for an autoloaded file (current) display breakpoints in editor if any
#######
sub _bp_autoload {
	my $self     = shift;
	my $current  = $self->current;
	my $editor   = $current->editor;
	my $document = $current->document;

	$self->_setup_db;

	#TODO is there a better way
	$self->{current_file} = $document->filename;
	my $sql_select = "WHERE filename = ?";
	my @tuples = $self->{debug_breakpoints}->select( $sql_select, $self->{current_file} );

	for ( 0 .. $#tuples ) {

		TRACE("show breakpoints autoload: self->{client}->set_breakpoint: $tuples[$_][1] => $tuples[$_][2]") if DEBUG;

		# autoload of breakpoints and mark file
		if ( $self->{client}->set_breakpoint( $tuples[$_][1], $tuples[$_][2] ) == 1 ) {
			$editor->MarkerAdd( $tuples[$_][2] - 1, Padre::Constant::MARKER_BREAKPOINT() );
		} else {
			$editor->MarkerAdd( $tuples[$_][2] - 1, Padre::Constant::MARKER_NOT_BREAKABLE() );

			#wright $tuples[$_][3] = 0
			Padre::DB->do( 'update debug_breakpoints SET active = ? WHERE id = ?', {}, 0, $tuples[$_][0], );
			if ( $self->main->{breakpoints} ) {
				$self->main->{breakpoints}->on_refresh_click();
			}
		}

	}

	return;
}

#######
# Event Handler _on_list_item_selected
# equivalent to p|x the varaible
#######
sub _on_list_item_selected {
	my $self          = shift;
	my $event         = shift;
	my $main          = $self->main;
	my $index         = $event->GetIndex + 1;
	my $variable_name = $event->GetText;

	#ToDo Changed to use current internal hashes instead of asking perl5db for value, this also gets around a bug with 'File::HomeDir has tied variables' clobbering x @rray giving an empty array
	my $variable_value;
	my $black_size = keys %{ $self->{var_val} };
	my $blue_size  = keys %{ $self->{local_values} };

	given ($index) {
		when ( $_ <= $black_size ) {
			$variable_value = $self->{var_val}->{$variable_name};
			chomp $variable_value;
			$main->{debugoutput}->debug_output_black( $variable_name . ' = ' . $variable_value );
		}
		when ( $_ <= ( $black_size + $blue_size ) ) {
			$variable_value = $self->{local_values}->{$variable_name};
			chomp $variable_value;
			$main->{debugoutput}->debug_output_blue( $variable_name . ' = ' . $variable_value );
		}
		default {
			$variable_value = $self->{global_values}->{$variable_name};
			chomp $variable_value;
			$main->{debugoutput}->debug_output_dark_gray( $variable_name . ' = ' . $variable_value );
		}
	}

	return;
}


###############################################
# event handler top row
#######
# sub on_debug_clicked
#######
sub on_debug_clicked {
	my $self = shift;

	$self->debug_perl;
	$self->update_debugger_buttons_on;
}

#######
# sub update_debugger_buttons_on
#######
sub update_debugger_buttons_on {
	my $self    = shift;
	my $arg_ref = shift;

	my $main = $self->main;

	return unless $self->{client};

	$self->{quit_debugger}->Enable;

	# $self->show_debug_output(1);
	$main->show_debugoutput(1);
	$self->{step_in}->Show;
	$self->{step_over}->Show;
	$self->{step_out}->Show;
	$self->{run_till}->Show;
	$self->{display_value}->Show;

	$self->{show_local_variables}->Enable;

	$self->{trace}->Enable;
	$self->{evaluate_expression}->Enable;
	$self->{expression}->Enable;
	$self->{stacktrace}->Enable;

	$self->{module_versions}->Enable;
	$self->{all_threads}->Enable;
	$self->{list_action}->Enable;
	$self->{dot}->Enable;
	$self->{view_around}->Enable;

	$self->{running_bp}->Enable;

	# $self->{add_watch}->Enable;
	# $self->{delete_watch}->Enable;
	$self->{raw}->Enable;
	$self->{watchpoints}->Enable;
	$self->{sub_names}->Enable;
	$self->{display_options}->Enable;

	$self->{debug}->Hide;
	$main->aui->Update;
	if ( $main->{debugoutput} ) {
		$main->{debugoutput}->debug_output( $self->{client}->get_h_var('h') );
		if ($arg_ref) {
			$main->{debugoutput}->debug_launch_options('To see all Debug Launch Parameters see menu');
		}
	}

	#let's reload our breakpoints
	# $self->_get_bp_db();
	$self->{set_bp} = 0;

	return;
}
#######
# sub step_in_clicked
#######
sub on_step_in_clicked {
	my $self = shift;

	TRACE('step_in_clicked') if DEBUG;
	$self->debug_step_in();

	return;
}
#######
# sub step_over_clicked
#######
sub on_step_over_clicked {
	my $self = shift;

	TRACE('step_over_clicked') if DEBUG;
	$self->debug_step_over();

	return;
}
#######
# sub step_out_clicked
#######
sub on_step_out_clicked {
	my $self = shift;

	TRACE('step_out_clicked') if DEBUG;
	$self->debug_step_out();

	return;
}
#######
# sub run_till_clicked
#######
sub on_run_till_clicked {
	my $self = shift;

	TRACE('run_till_clicked') if DEBUG;
	$self->debug_run_till();

	return;
}
#######
# sub display_value
#######
sub on_display_value_clicked {
	my $self = shift;

	TRACE('display_value') if DEBUG;
	$self->display_value();

	return;
}
#######
# sub quit_debugger_clicked
#######
sub on_quit_debugger_clicked {
	my $self = shift;
	my $main = $self->main;

	TRACE('quit_debugger_clicked') if DEBUG;
	$self->debug_quit;

	$main->show_debugoutput(0);

	return;
}


###############################################
# show
#######
# event on_show_local_variables_checked
#######
sub on_show_local_variables_checked {
	my ( $self, $event ) = @_;

	if ( $event->IsChecked ) {
		$self->{local_variables} = 1;
	} else {
		$self->{local_variables} = 0;
	}
	$self->_output_variables;
	return;
}
#######
# event on_show_global_variables_checked
#######
sub on_show_global_variables_checked {
	my ( $self, $event ) = @_;

	if ( $event->IsChecked ) {
		$self->{global_variables} = 1;
	} else {
		$self->{global_variables} = 0;
	}
	$self->_output_variables;
	return;
}


#################################################
# Output Options
#######
# sub trace_clicked
#######
sub on_trace_checked {
	my ( $self, $event ) = @_;

	if ( $event->IsChecked ) {
		$self->_display_trace(1);
	} else {
		$self->_display_trace(0);
	}

	return;
}
#######
# Event on_dot_clicked .
#######
sub on_dot_clicked {
	my $self = shift;
	my $main = $self->main;

	$main->{debugoutput}->debug_output( $self->{client}->show_line() );

	#reset editor to dot location
	$self->_set_debugger;

	return;
}
#######
# Event on_view_around_clicked v
#######
sub on_view_around_clicked {
	my $self = shift;
	my $main = $self->main;

	$main->{debugoutput}->debug_output( $self->{client}->show_view() );

	return;
}
#######
# Event handler on_list_action_clicked L
#######
sub on_list_action_clicked {
	my $self = shift;
	my $main = $self->main;

	$main->{debugoutput}->debug_output( $self->{client}->show_breakpoints() );

	return;
}

#######
# Event handler on_running_bp_set_clicked b|B
#######
sub on_running_bp_clicked {
	my $bp_action_ref = Padre::Breakpoints->set_breakpoints_clicked;

	return;
}
sub update_debugger_breakpoint {
	my $self     = shift;
	my $bp_action_ref = shift;
	my $main     = $self->main;
	my $editor   = $self->current->editor;
	my $document = $self->current->document;
	$self->{current_file} = $document->filename;
	
	if ( $self->{client} ) {
		if ( $bp_action_ref->{action} eq 'add' ) {
			my $result = $self->{client}->set_breakpoint( $self->{current_file}, $bp_action_ref->{line} );
			if ( $result == 0 ) {

				# print "not breakable\n";
				$editor->MarkerAdd( $bp_action_ref->{line} - 1, Padre::Constant::MARKER_NOT_BREAKABLE() );
				$self->_setup_db;
				Padre::DB->do(
					'update debug_breakpoints SET active = ? WHERE filename = ? AND line_number = ?', {}, 0,
					$self->{current_file}, $bp_action_ref->{line},
				);
				if ( $self->main->{breakpoints} ) {
					$self->main->{breakpoints}->on_refresh_click();
				}

			}
		}
		if ( $bp_action_ref->{action} eq 'delete' ) {
			$self->{client}->remove_breakpoint( $self->{current_file}, $bp_action_ref->{line} );
		}
		$main->{debugoutput}->debug_output( $self->{client}->__send('L b') );
	}

	return;
}
#######
# Event handler on_module_versions_clicked M
#######
sub on_module_versions_clicked {
	my $self = shift;
	my $main = $self->main;

	$main->{debugoutput}->debug_output( $self->{client}->__send('M') );

	return;
}
#######
# Event handler on_stacktrace_clicked T
#######
sub on_stacktrace_clicked {
	my $self = shift;
	my $main = $self->main;

	$main->{debugoutput}->debug_output( $self->{client}->get_stack_trace );

	return;
}
#######
# Event handler on_all_threads_clicked E
#######
sub on_all_threads_clicked {
	my $self = shift;
	my $main = $self->main;

	$main->{debugoutput}->debug_output( $self->{client}->__send_np('E') );

	return;
}
#######
# Event handler on_display_options_clicked o
#######
sub on_display_options_clicked {
	my $self = shift;
	my $main = $self->main;

	$main->{debugoutput}->debug_output( $self->{client}->get_options );

	return;
}


#######
# Event handler on_evaluate_expression_clicked p|x
#######
sub on_evaluate_expression_clicked {
	my $self = shift;
	my $main = $self->main;

	if ( $self->{client}->get_stack_trace =~ /ANON/ ) {
		$main->{debugoutput}->debug_output(
			' You appear to be inside an __ANON__, suggest you use "Show Local Variables" to view contents');
		return;
	}

	if ( $self->{expression}->GetValue() eq "" ) {
		$main->{debugoutput}->debug_output( '$_ = ' . $self->{client}->get_value );
	} else {
		$main->{debugoutput}->debug_output(
			$self->{expression}->GetValue . " = " . $self->{client}->get_value( $self->{expression}->GetValue ) );
	}

	return;
}
#######
# Event handler on_sub_names_clicked S
#######
sub on_sub_names_clicked {
	my $self = shift;
	my $main = $self->main;

	$main->{debugoutput}->debug_output( $self->{client}->list_subroutine_names( $self->{expression}->GetValue ) );

	return;
}
#######
# Event handler on_watchpoints_clicked w|W
#######
sub on_watchpoints_clicked {
	my $self = shift;
	my $main = $self->main;

	if ( $self->{expression}->GetValue ne "" ) {
		if ( $self->{expression}->GetValue eq "*" ) {
			$main->{debugoutput}->debug_output( $self->{client}->__send( 'W ' . $self->{expression}->GetValue ) );

			return;
		}

		# this is nasty, there must be a better way
		my $exp = "\\" . $self->{expression}->GetValue;

		if ( $self->{client}->__send('L w') =~ m/$exp/gm ) {
			my $del_watch = $self->{client}->__send( 'W ' . $self->{expression}->GetValue );
			if ($del_watch) {
				$main->{debugoutput}->debug_output($del_watch);
			} else {
				$main->{debugoutput}->debug_output( $self->{client}->__send('L w') );
			}

			return;
		} else {

			$self->{client}->__send( 'w ' . $self->{expression}->GetValue );
			$main->{debugoutput}->debug_output( $self->{client}->__send('L w') );

			return;
		}
	} else {
		$main->{debugoutput}->debug_output( $self->{client}->__send('L w') );
	}

	return;
}

#######
# Event handler on_raw_clicked raw
#######
sub on_raw_clicked {
	my $self = shift;
	my $main = $self->main;

	my $output;
	if ( $self->{expression}->GetValue =~ m/^h.?(\w*)/s ) {
		$output = $self->{client}->get_h_var($1) ;
	} else {

		$output = $self->{client}->__send_np( $self->{expression}->GetValue );
	}
	$self->update_debug_user_interface($output);

	return;
}

#######
# Event handler on_stacktrace_clicked i
#######
# sub on_nested_parents_clicked {
# my $self = shift;
# my $main = $self->main;

# # 	$main->{debugoutput}->debug_output( $self->{client}->__send('i') );

# # 	return;
# }
#######
# Event handler on_running_bp_delete_clicked B
#######
# sub on_running_bp_delete_clicked {
# my $self = shift;

# # 	return;
# }
#######
# Event handler on_add_watch_clicked w
#######
# sub on_add_watch_clicked {
# my $self = shift;
# my $main = $self->main;

# # 	if ( $self->{expression}->GetValue() ne "" ) {

# # 		$main->{debugoutput}->debug_output( $self->{client}->__send( 'w ' . $self->{expression}->GetValue() ) );
# }

# # 	#reset expression
# $self->expression->SetValue(BLANK);
# return;
# }
#######
# Event handler on_delete_watch_clicked W
#######
# sub on_delete_watch_clicked {
# my $self = shift;
# my $main = $self->main;

# # 	if ( $self->{expression}->GetValue() ne "" ) {

# # 		$main->{debugoutput}->debug_output( $self->{client}->__send( 'W ' . $self->{expression}->GetValue() ) );
# }

# # 	#reset expression
# $self->expression->SetValue(BLANK);
# return;
# }

#######
# Event handler on_launch_options - launch the debugger over-riding its auto-choices
#######
sub on_launch_options {
	my $self     = shift;
	my $main     = $self->main;
	my $current  = $self->current;
	my $document = $current->document;
	my $editor   = $current->editor;

	my $filename;
	if ( defined $document->{file} ) {
		$filename = $document->{file}->filename;
	}

	# TODO: improve the message displayed to the user
	# If the document is not saved, simply return for now
	return unless $filename;

	my ( $cmd, $arg_ref ) = $document->get_command( { debug => 1 } );

	require Padre::Wx::Dialog::DebugOptions;
	my $dialog = Padre::Wx::Dialog::DebugOptions->new(
		$main,
	);

	$dialog->perl_interpreter->SetValue( $arg_ref->{perl} );
	$dialog->perl_args->SetValue( $arg_ref->{perl_args} );
	$dialog->find_script->SetValue( $arg_ref->{script} );
	$dialog->run_directory->SetValue( $arg_ref->{run_directory} );
	$dialog->script_options->SetValue( $arg_ref->{script_args} );

	$dialog->find_script->SetFocus;

	if ( $dialog->ShowModal == Wx::ID_CANCEL ) {
		return;
	}
	$arg_ref->{perl}          = $dialog->perl_interpreter->GetValue();
	$arg_ref->{perl_args}     = $dialog->perl_args->GetValue();
	$arg_ref->{script}        = $dialog->find_script->GetValue();
	$arg_ref->{run_directory} = $dialog->run_directory->GetValue();
	$arg_ref->{script_args}   = $dialog->script_options->GetValue();
	$dialog->Destroy;

	#save history for next time (when we might just hit run!
	{
		my $history = $main->lock( 'DB', 'refresh_recent' );

		#save which script the user selected to run for this document
		Padre::DB::History->create(
			type => "run_script_" . File::Basename::fileparse($filename),
			name => $arg_ref->{script},
		);
		my $script_base = File::Basename::fileparse( $arg_ref->{script} );

		Padre::DB::History->create(
			type => 'run_directory_' . $script_base,
			name => $arg_ref->{run_directory},
		);
		Padre::DB::History->create(
			type => "run_script_args_" . $script_base,
			name => $arg_ref->{script_args},
		);
		Padre::DB::History->create(
			type => "run_perl_" . $script_base,
			name => $arg_ref->{perl},
		);
		Padre::DB::History->create(
			type => "run_perl_args_" . $script_base,
			name => $arg_ref->{perl_args},
		);
	}

	#now run the debugger with the new command
	$self->debug_perl($arg_ref);

	# p $arg_ref;
	$self->update_debugger_buttons_on($arg_ref);

	return;
}


1;

__END__

=pod

=head1 NAME

Padre::Plugin::Debug::Panel::Debugger - Interface to the Perl debugger.

=head1 DESCRIPTION

Padre::Wx::Debugger provides a wrapper for the generalised L<Debug::Client>.

It should really live at Padre::Debugger, but does not currently have
sufficient abstraction from L<Wx>.

=head1 METHODS

=head2 new

Simple constructor.

=head2 debug_perl

  $main->debug_perl;

Run current document under Perl debugger. An error is reported if
current is not a Perl document.

Returns true if debugger successfully started.

=cut

# Copyright 2008-2013 The Padre development team as listed in Padre.pm.
# LICENSE
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl 5 itself.