use Win32::Editor::PFE;
use Win32::Clipboard;

$pfe = new Win32::Editor::PFE;

package PSH::PFE;

$PSH::PFE::VERSION = '0.3';

*pfe = \$main::pfe;

sub import {
    $_[1]='^' unless defined $_[1];
    PSH::specials $_[1] => \&PSH::PFE;
}

#print Win32::MsgBox('Do you want to replace this?',3);
# yes = 6
# no  = 7
# cancel = 2

sub QA { local $^W;
 my $from = shift;
 my $clip = Win32::Clipboard::Get();
 print "Replace: ";
 my $to = <STDIN> or return;
 chomp $to;
 print "Options: ";
 my $opt = <STDIN> or return;
 chomp $opt;
 $pfe->CaretEndOfLine(1);
 $pfe->EditCopy();
 local $_ = Win32::Clipboard::Get();
print " s/$from/$to/g;";
 eval "s/\$from/$to/g";
 Win32::Clipboard::Set($_);
 $pfe->Editpaste();
 while ($pfe->CaretDown(1,0)) {
  $pfe->EditSelectLine();
  $pfe->EditCopy() or next;
  $_ = Win32::Clipboard::Get();
  eval "s/\$from/$to/g";
  Win32::Clipboard::Set($_);
  $pfe->Editpaste();
 }
 Win32::Clipboard::Set($clip);
 $pfe->Disconnect();
}

sub QAI { local $^W=0;
 my $from = shift;
 my $clip = Win32::Clipboard::Get();
 print "Replace: ";
 my $to = <STDIN> or return;
 chomp $to;
 $pfe->CaretEndOfLine(1);
 $pfe->EditCopy();
 local $_ = Win32::Clipboard::Get();
print " s/$from/$to/gi;";
 eval "s/\$from/$to/gi";
 Win32::Clipboard::Set($_);
 $pfe->Editpaste();
 while ($pfe->CaretDown(1,0)) {
  $pfe->EditSelectLine();
  $pfe->EditCopy() or next;
  $_ = Win32::Clipboard::Get();
  eval "s/\$from/$to/gi";
  Win32::Clipboard::Set($_);
  $pfe->Editpaste();
 }
 Win32::Clipboard::Set($clip);
 $pfe->Disconnect();
}

sub QF { local $^W=0;
 my $find = shift;
 if ($find) {$_last_find = $find} else {$find = $_last_find};

 print "Options: ";
 my $opt = <STDIN> or return;
 chomp $opt;
 if ($opt =~ s/n//g) {
  $find = quotemeta $find;
 }

 my $clip = Win32::Clipboard::Get();
 my ($pre,$found,$len);

 $bookmarks{LAST} = [$pfe->Pos()];

 $pfe->CaretEndOfLine(1);
 $pfe->EditCopy();
 local $_ = Win32::Clipboard::Get();
#print "OPT $opt\n";
 if (m/^(?$opt)(.*?)($find)/) {
  $found = length($1)+$bookmarks{LAST}->[1];
  $len = length $2;
 } else {
  while ($pfe->CaretDown(1,0)) {
   $pfe->EditSelectLine();
   $pfe->EditCopy();
   $_ = Win32::Clipboard::Get();
   if (m/^(?$opt)(.*?)($find)/) {
    $found = length($1)+1;
    $len = length $2;
    last;
   }
  }
 }
 Win32::Clipboard::Set($clip);
 $pfe->Pos('+0',$found);
 $pfe->CaretRight($len,1);
 $pfe->ComeToForeground();
 $pfe->Disconnect();
}

%bookmarks = (
    HOME => [1,1],
    END  => ["end"],
    home => [1,1],
    end  => ["end"],
);

sub BM {
    my $name = shift;
    $bookmarks{$name} = [$pfe->Pos()];
    $pfe->Disconnect();
}

sub GO {
    my $name = shift;$name =~ s/^\s+(.*)\s+$/\U$1/;
    if ($name =~ m#^[+-]?\d+(?:/C?[+-]?\d+)?$#) {
        $bookmarks{LAST} = [$pfe->Pos($name)];
    } elsif ($bookmarks{$name}) {
        $bookmarks{LAST} = [$pfe->Pos(@{$bookmarks{$name}})];
    } else {
        print 'Not found';
    }
    $pfe->Disconnect();
}

sub O {
    my $name = shift;$name =~ s/^\s+(.*)\s+$/\U$1/;
    if ($name =~ /[\@\$]/) {
        $name =~ tr#\\#/#;
        eval qq{
            package main;
            \$name=qq<$name>;
        }
    }
    my @pos;
    ($name,@pos) = split '\|', $name;
    $name =~ tr#/#\\#;
    if (@pos) {
        $pfe->OpenAt($name,@pos) or print "Failed opening $name at @pos";
    } else {
        $pfe->Open($name) or print "Failed opening $name";
    }
    $pfe->Disconnect();
}

sub OM {
    my $name = shift;$name =~ s/^\s+(.*)\s+$/\U$1/;
    if ($name =~ /[\@\$]/) {
        $name =~ tr#\\#/#;
        eval qq{
            package main;
            \$name=qq<$name>;
        }
    }
    my @pos;
    ($name,@pos) = split '\|', $name;
    $name =~ tr#(/|::)#\\#;
    $name.='.pm' unless $name =~ /\.pm$/i;
    foreach (@INC) {
        if (-e $_.'\\'.$name) {
            $name = $_.'\\'.$name;
            last;
        }
    }
    if (@pos) {
        $pfe->OpenAt($name,@pos) or print "Failed opening $name at @pos";
    } else {
        $pfe->Open($name) or print "Failed opening $name";
    }
    $pfe->Disconnect();
}
*MO = \&OM;

sub OS {
    my $name = shift;$name =~ s/^\s+(.*)\s+$/\U$1/;
    if ($name =~ /[\@\$]/) {
        $name =~ tr#\\#/#;
        eval qq{
            package main;
            \$name=qq<$name>;
        }
    }
    my @pos;
    ($name,@pos) = split '\|', $name;
    $name =~ tr#(/|::)#\\#;
    $name.='.pl' unless $name =~ /\.[^.]+$/i;
    foreach (split ';', $ENV{PATH}) {
        if (-e $_.'\\'.$name) {
            $name = $_.'\\'.$name;
            last;
        }
    }
    unless (-e $name) {
        print "Unable to find the script in PATH\7.\n";
        return;
    }
    if (@pos) {
        $pfe->OpenAt($name,@pos) or print "Failed opening $name at @pos";
    } else {
        $pfe->Open($name) or print "Failed opening $name";
    }
    $pfe->Disconnect();
}
*SO = \&OS;

sub I {
    my $name = shift;$name =~ s/^\s+(.*)\s+$/\U$1/;
    if ($name =~ /[\@\$]/) {
        $name =~ tr#\\#/#;
        eval qq{
            package main;
            \$name=qq<$name>;
        }
    }
    my @pos;
    ($name,@pos) = split '\|', $name;
    $name =~ tr#/#\\#;
    $pfe->FileInsert($name) or print "Failed inserting $name" and return;
    $pfe->Pos(@pos) if (@pos);
    $pfe->Disconnect();
}

sub gt {
    my $clip = Win32::Clipboard::Get();
    my @pos = $pfe->Pos();
    $pfe->EditCopy() or $pfe->EditSelectLine() and $pfe->EditCopy() and $pfe->Pos(@pos);
    my $text = Win32::Clipboard::Get();

#print "read text : $text\n";

    Win32::Clipboard::Set($clip);
    my $var = shift;
    if ($var =~ s/^\s*\$/\$/) {
        eval "package $PSH::package;$var = <<'*EnD_DnE*';\n$text\n*EnD_DnE*\n";
    } elsif ($var =~ s/^\s*\@/\@/) {
        eval "package $PSH::package;$var = split /\n/s, <<'*EnD_DnE*';\n$text\n*EnD_DnE*\n";
    } else {
        eval "package $PSH::package;$var <<'*EnD_DnE*';\n$text\n*EnD_DnE*\n";
    }
    $pfe->Disconnect();
}
*{"PSH::PFE::>"} = \&gt;

sub lt {
    $pfe->EditInsert(eval "package $PSH::package;".$_[0]);
    $pfe->Disconnect();
}
*{"PSH::PFE::<"} = \&lt;

sub AUTOLOAD {print "NOT IMPLEMENTED"};

package PSH;

sub PFE {
    my $cmd = shift;chomp $cmd;
    my ($fun,$param) = split /\s+/, $cmd, 2;
    $fun = uc $fun;
    &{"PSH::PFE::$fun"}($param);
}

=head1 NAME

 PSH::PFE - Programmer's File Editor plugin for PSH.pm

 Version 0.3

=head1 SYNOPSIS

 use PSH;
 use PSH::PFE;
 ...
 PSH::prompt;

 psh$ ^OM File::Find
 psh$ ^QA \$(\w+)
 Replace: \U\$$1
 
=head1 DESCRIPTION

This module installs another special character into PSH, that allows you
to easily send commands to PFE (Programmer's File Editor), to
search&replace, open perl modules and other files etc. etc.

This is only an experimental version so do not expect too much.
It was intended especialy to show you what can be done.

=head1 Ussage

The module doesn't realy provide any functions to your script, it was meant
to be used in pure psh. You add the commands defined here to PSH.pm by either

    use PSH::PFE;
   or
    use PSH::PFE 'character';

This adds the character (by default '^') to PSH's specials and ties
function &PSH::PFE to it. This function simply gets from PSH the rest of
the line after the special character you specified and split's it on the
first whitespace.

It then uses the first part as a name of a function in package PSH::PFE
and passes the rest to it as it's only argument. The function name is
uppercased before ussage!

=head1 Commands

The case of commands is unimportant !

=over 2

=item QA

 psh$ ^QA regexp
 Replace: replacement string

This command searches the rest of the active document after cursor
for the regexp and replaces it with the specified string.

It'as implemented as s/regexp/replacement/g.

=item QAi

 psh$ ^QAi regexp
 Replace: replacement string

This command searches the rest of the active document after cursor for
the regexp and replaces it with the specified string. The match is case
insensitive.

It'as implemented as s/regexp/replacement/gi.

=item >

 psh$ ^> $variable

 psh$ ^> @variable

 psh$ ^> code

Sets the variable to either the selected text or the current line. If
the variable is a scalar it's set to the whole text. If it is an array
it gets the text split into lines. If the parameter to the ^> command
doesn't start with neither $ nor @ it's thought to be a function name.


 ^> $foo   => eval "\$foo = <<'*EnD_DnE*';\n$text\n*EnD_DnE*\n"
 ^> @foo   => eval "\@foo = split /\n/s, <<'*EnD_DnE*';\n$text\n*EnD_DnE*\n"
 ^> foo    => eval "foo <<'*EnD_DnE*';\n$text\n*EnD_DnE*\n"

=item <

 psh$ ^< $variable

 psh$ ^< code

Evaluates the code and sends the result to PFE on the current position
of cursor.

=back

=head2 AUTHOR

Jenda@Krynicky.cz

=cut
