package Padre::Plugin::Parrot;
use strict;
use warnings;
use 5.008;

our $VERSION = '0.26';

use Padre::Wx ();

use base 'Padre::Plugin';

my $parrot;

# TODO get documentation from parrot/src/ops/*.ops and parrot/docs/pdds/pdd19_pir.pod

=head1 NAME

Padre::Plugin::Parrot - Experimental Padre plugin for Parrot

=head1 SYNOPSIS

This Plugin provides several features

=over 4

=item *

Syntax highlighting via the PGE parse tree for languages using PCT - the Parrot Compiler Toolkit

=item *

Syntax highlighting of PIR and PASM files using Perl 5 regular expressions

=item *

Embedding of Parrot to allow extending Padre using languages running in Parrot

=back

After installation you need to enable the plugin via the Plugin Manager of Padre.
Once that is done there should be a menu option Plugins/Parrot with several submenus.

About is just some short explanation

The other menu options will count the number of characters in the current document
using the current Perl 5 interpreter or PASM running on top of Parrot.
Later we add other implementations running on top of Parrot.

The syntax highlighting provided by this module can be enabled on a perl file-type
(actually mime-type) base in the Edit/Preferences/Mime-types dialog.

=head1 INSTALLATION

This whole plugin is quite experimental. So is the documentation. 
I hope the plugin can work with released and installed versions of Parrot as well but
I have never tried that. Let me outline how I install the dependencies.

It is quite simple though it has several steps in it.

Later we'll make this more simple.

I start with Rakudo (the implementation of Perl 6 on Parrot).

=head2 Install Rakudo

 $ cd $HOME
 $ mkdir work
 $ cd work
 $ git clone git://github.com/rakudo/rakudo.git
 $ cd rakudo
 $ perl Configure.pl --gen-parrot
 $ make

=head2 Configure env variables

Configure PARROT_DIR to point to the root of parrot
Configure RAKUDO_DIR to point to the directory where rakudo was checked out.
(I have these in the .bashrc)

 $ export PARROT_DIR=$HOME/work/rakudo/parrot
 $ export RAKUDO_DIR=$HOME/work/rakudo


Once this is done if you run Padre now you can enable Parrot/PGE highlighting of
Perl 6 files via the Edit/Preferences/Mime-types dialog.

=head2 Adding Cardinal (Ruby) highlighting

In order to support Ruby highlighting one needs to configure the CARDINAL_DIR 
environment variable to point to the place where the cardinal.pbc can be located.

  $ cd $HOME/work
  $ git clone git://github.com/cardinal/cardinal.git
  $ export CARDINAL_DIR=$HOME/work/cardinal         # add this also to .bashrc
  $ cd $PARROD_DIR
  $ mkdir languages
  $ cd language
  $ ln -s $CARDINAL_DIR
  $ cd cardinal
  $ perl Configure.pl
  $ make

Once this is done if you run Padre now you can enable Parrot/PGE highlighting of
Ruby files via the Edit/Preferences/Mime-types dialog.

=head2 Embedding Parrot

=head3 Configure LD_LIBRARY_PATH (also in .bashrc)

 $ export LD_LIBRARY_PATH=$PARROT_DIR/blib/lib/
 
=head3 Build Parrot::Embed

  $ cd $PARROT_DIR/ext/Parrot-Embed/
  ./Build realclean
  perl Build.PL
  ./Build
  ./Build test

The test will give a warning like this, but will pass:

 Parrot VM: Can't stat no file here, code 2.
 error:imcc:syntax error, unexpected IDENTIFIER
	in file 'EVAL_2' line 1

Now if you run Padre and enable Padre::Plugin::Parrot 
it will have an embedded Parrot interpreter that can run
code written in PIR. (See the Plugins/Parrot/Count Characters...)
menu options.


=head1 Related Tickets in Parrot

L<https://trac.parrot.org/parrot/ticket/77>
L<https://trac.parrot.org/parrot/ticket/74>
L<https://trac.parrot.org/parrot/ticket/76>
L<https://trac.parrot.org/parrot/ticket/79>
L<https://trac.parrot.org/parrot/ticket/77>

=head1 Adding more highlightings

In order to add more syntax highlighters one needs to

=over 4

=item 1)

make sure the relevant language can compile to a pbc file

=item 2)

add and entry to the @config variable.

=item 3)

add color codes to the missing tokens in L<Padre::Plugin::Parrot::ColorizeTask>

=back

=head1 TODO

=over 4

=item *

Eliminate the need for environment variables

=item *

Make the installations more simple, make sure it can work with released and installed versions of Parrot, Rakudo etc.

=item *

Allow the addition and configuration of more .pbc files (or executables) to @config (and keep it
in the Padre config database).

=item *

Separate the token lists for the various languages
L<Padre::Plugin::Parrot::ColorizeTask>

=item *

Automatically colorize any file type if it does not have a specified token to colors table.

=back


=head1 COPYRIGHT

Copyright 2008-2009 Gabor Szabo. L<http://szabgab.com/>

=head1 LICENSE

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl 5 itself.

=cut

my $pod = <<'POD';

=head1 Parrot

Some text
L<$PARROT_DIR/docs/intro.pod>

=cut

POD

sub padre_interfaces {
	return 'Padre::Plugin' => 0.41;
}

sub plugin_name {
	'Parrot';
}

sub menu_plugins_simple {
	my $self = shift;
	return $self->plugin_name => [
		'About' => sub { $self->about },
		'PIR 2 PBC' => sub { $self->pir2pbc },

		#'Help'                                        => \&show_help,

		"Count characters using Perl5"                  => \&on_try_perl5,
		"Count characters using PIR in embedded Parrot" => \&on_try_pir,
	];
}

sub registered_documents {
	'application/x-pasm' => 'Padre::Document::PASM', 'application/x-pir' => 'Padre::Document::PIR',;
}

# TODO, Planning the syntax highlighting feature:
# -------------------------------
# let the user regiser 
# mime-type, Path/to/language.pge, Name, Description?
# or
# mime-type, Path/to/language.exe, Name, Description?

# Though as this is only for personal use on the users own computer
# for now, we don't really need a description field but maybe the user
# wants to add comments.
# Name must be ASCII string without 
# We can recognize if this is a .pge file or an executable 
# (.exe on windows nothing on Unix) but we might also provide a check-box
# so the user can configure this.

# We ave this information in a database or config file
# We read this information at load time and based on this change the 
# provided_highlighters and highlighting_mime_types functions
#
# With the module name being Padre::Plugin::HL::Name  (using the Name the user gave us)
# the module is virtual, only exists in memory

my @highlighters = (
	['Padre::Document::PIR',  'PIR in Perl 5',  'PIR syntax highlighting with Perl 5 regular expressions'],
	['Padre::Document::PASM', 'PASM in Perl 5', 'PASM syntax highlighting with Perl 5 regular expressions'],
	['Padre::Plugin::Parrot', 'Parrot PGE',     'Using the PGE engine for highlighting'],
);

my %highlighter_mimes = (
	'Padre::Document::PIR'  => ['application/x-pir'],
	'Padre::Document::PASM' => ['application/x-pasm'],
);

# [mime-type,    path-to-pbc-or-exe,  'NameWithoutSpace', 'Description'] 
my @config;
if ($ENV{RAKUDO_DIR}) {
	push @config, ['application/x-perl6', "$ENV{RAKUDO_DIR}/perl6.pbc",       'Perl6', 'Perl 6 via Parrot and perl6.pbc'];
}
if ($ENV{CARDINAL_DIR}) {
	push @config, ['application/x-ruby',  "$ENV{CARDINAL_DIR}/cardinal.pbc",  'Ruby',  'Ruby via Cardinal on Parrot and cardinal.pbc'];
}

use Padre::Plugin::Parrot::HL;
foreach my $e (@config) {
	my ($mime_type, $path, $name, $description) = @$e;
	next if not -e $path;
	# TODO check other values as well

	my $pbc	= ($path =~ /\.pbc$/ ? 1 : 0);
	my $module = 'Parrot::Plugin::HL::' . ($pbc ? 'PBC::' : '') . $name;
	my $display_name = "Parrot/" . ($pbc ? 'PBC' : 'EXE') . "/$name";
	{
		# create virtual namespace and colorize() function.
		# maybe I only need to create 
		
		my $sub = sub { return ($pbc, $path) };
		my $isa = $module . '::ISA';
		my $function = $module . '::pbc_path';
		no strict 'refs';
		@$isa = ('Padre::Plugin::Parrot::HL');
		*{$function} = $sub;
	}
	push @highlighters, [$module, $display_name, $description];
	$highlighter_mimes{$module} = [$mime_type];
}

sub provided_highlighters {
	return @highlighters;
}

sub highlighting_mime_types {
	return %highlighter_mimes;
}

sub plugin_enable {
	my $self = shift;

	return if not $ENV{PARROT_DIR};

	return 1 if $main::parrot;    # avoid crash when duplicate calling

	local @INC = (
		"$ENV{PARROT_DIR}/ext/Parrot-Embed/blib/lib",
		"$ENV{PARROT_DIR}/ext/Parrot-Embed/blib/arch",
		"$ENV{PARROT_DIR}/ext/Parrot-Embed/_build/lib",
		@INC
	);

	# for now we keep the parrot interpreter in a script-global
	# in $main as if we try to reload the Plugin the embedded parrot will
	# blow up. TODO: we should be able to shut down the Parrot interpreter
	# when the plugin is disabled.
	eval {
		require Parrot::Embed;
		$main::parrot = Parrot::Interpreter->new;
	};
	if ($@) {
		warn $@;
		return;
	}

	return 1;
}

sub on_try_perl5 {
	my ($main) = @_;

	my $doc = Padre::Current->document;
	my $str = "No file is open";
	if ($doc) {
		$str = "Number of characters in the current file: " . length( $doc->text_get );
	}

	Wx::MessageBox( "From Perl 5. $str", "Worksforme", Wx::wxOK | Wx::wxCENTRE, $main );
	return;
}

sub on_try_pir {
	my ($main) = @_;

	my $parrot = $main::parrot;
	if ( not $parrot ) {
		Wx::MessageBox( "Parrot is not available", "No luck", Wx::wxOK | Wx::wxCENTRE, $main );
		return;
	}

	my $code = <<END_PIR;
.sub on_try_pir
	.param string code

	.local int count
	count = length code

	.return( count )
.end
END_PIR

	my $eval = $parrot->compile($code);
	my $sub  = $parrot->find_global('on_try_pir');

	my $doc = Padre::Current->document;
	my $str = "No file is open";
	if ($doc) {
		my $pmc = $sub->invoke( 'PS', $doc->text_get );
		$str = "Number of characters in the current file: " . $pmc->get_string;
	}

	Wx::MessageBox( "From Parrot using PIR: $str", "Worksforme", Wx::wxOK | Wx::wxCENTRE, $main );
	return;
}

sub pir2pbc {
	my $main = Padre->ide->wx->main;
	my $doc = Padre::Current->document;
	return if not $doc;
	my $filename = $doc->filename;
	return if not $filename or $filename !~ /\.pir$/i;
	$doc->pir2pbc;
}

sub about {
	my ($main) = @_;

	my $about = Wx::AboutDialogInfo->new;
	$about->SetName(__PACKAGE__);
	$about->SetDescription( "This plugin currently provides a naive syntax highlighting for PASM files\n"
			. "If you have Parrot compiled on your system it can also provide execution of\n"
			. "PASM files\n" );
	$about->SetVersion($VERSION);
	Wx::AboutBox($about);
	return;
}

sub show_help {
	my $main = Padre->ide->wx->main;

	#print "$main->{help}\n";
	if ( $ENV{PARROT_DIR} ) {
		my $path = File::Spec->catfile( $ENV{PARROT_DIR}, 'docs' );
		my $doc = Padre::Document->new;
		$doc->{original_content} = $pod;

		#my $doc = Padre::Document->new( filename => $path );
		$doc->set_mimetype('application/x-pod');
		$main->{help}->help($doc);
	} else {
		$main->{help}->help('Padre::Plugin::Parrot');
	}

	$main->{help}->SetFocus;
	$main->{help}->Show(1);
}

1;

# Copyright 2008 Gabor Szabo.
# LICENSE
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl 5 itself.

#pasm:
# brace_highlight: 00ffff
# colors:
#  PASM_KEYWORD:     7f0000
#  PASM_REGISTER:    7f0044
#  PASM_LABEL:       aa007f
#  PASM_STRING:      00aa7f
#  PASM_COMMENT:     0000aa
#  PASM_POD:         0000ff
#
