# Some elements borrowed from ideas developed by shabble@freenode(https://github.com/shabble/irssi-docs/wiki )
#
# You can change what intercept.pl considers a linestart by setting
# /set intercept_linestart to a regular expression that fits your needs.
# For most, a simple whitespace or . pattern will stop most accidental
# inputs.
#
# You can also tell which patterns should be ignored, for example
# /set intercept_exceptions s/\w+/[\w\s\d]+/ wouldn't consider
# s/word a mistyped command if it is followed by a slash, string of
# valid characters and a final slash.
# You can enter several patterns separated by a space.

use strict;
use warnings;
use Data::Dumper;
use Carp qw( croak );
use Irssi;
use Data::Munge 'list2re';

use vars qw($VERSION %IRSSI);

$VERSION = "0.3";
%IRSSI = (
              authors     => "Jari Matilainen",
              contact     => 'vague!#irssi@libera.chat on irc',
              name        => "intercept",
              description => "Intercept misprinted commands and offer to remove the first character before sending it on",
              license     => "Public Domain",
              url         => "https://irssi.org",
              changed     => "04 Mar 16:00:00 CET 2022",
             );

my $active = 0;
my $permit_pending = 0;
my $pending_input = {};
my $verbose = 0;
my $isword = 0;
my $cmdregexp = list2re map {$_->{cmd}} Irssi::commands();

sub script_is_loaded {
  return exists($Irssi::Script::{$_[0] . '::'});
}

if (script_is_loaded('uberprompt')) {
  app_init();
}
else {
  print "This script requires 'uberprompt.pl' in order to work. "
      . "Attempting to load it now...";

  Irssi::signal_add('script error', 'load_uberprompt_failed');
  Irssi::command("script load uberprompt.pl");

  unless(script_is_loaded('uberprompt')) {
    load_uberprompt_failed("File does not exist");
  }
  app_init();
}

sub load_uberprompt_failed {
  Irssi::signal_remove('script error', 'load_uberprompt_failed');

  print "Script could not be loaded. Script cannot continue. "
      . "Check you have uberprompt.pl installed in your scripts directory and "
      . "try again.  Otherwise, it can be fetched from: ";
  print "https://github.com/shabble/irssi-scripts/raw/master/"
      . "prompt_info/uberprompt.pl";

  croak "Script Load Failed: " . join(" ", @_);
}

sub sig_send_text {
  my ($data, $server, $witem) = @_;

  if($permit_pending == 1) {
    $pending_input = {};
    $permit_pending = 0;
    Irssi::signal_continue(@_);
  }
  elsif($permit_pending == 2) {
    my $regexp = Irssi::settings_get_str('intercept_linestart');
    $pending_input = {};
    $permit_pending = 0;
    Irssi::signal_stop();
    $data =~ s/^$regexp//;

    if(ref $witem && $witem->{type} eq 'CHANNEL') {
      $witem->command($data);
    }
    else {
      $server->command($data);
    }
  }
  elsif($permit_pending == 3) {
    $pending_input = {};
    $permit_pending = 0;
    $isword = 0;
    Irssi::signal_stop();

    if(ref $witem && $witem->{type} eq 'CHANNEL') {
      $witem->command($data);
    }
    else {
      $server->command($data);
    }
  }
  else {
    (my $cmdchars = Irssi::settings_get_str('cmdchars')) =~ s/(.)(.)/$1|$2/;
    my @exceptions = split / /, Irssi::settings_get_str('intercept_exceptions');

    foreach(@exceptions) {
      return if($data =~ m{$_}i);
    }

    my ($first) = split ' ', $data;

    my $regexp = Irssi::settings_get_str('intercept_linestart');
    $regexp =~ s/(^[\^])|([\$]$)//g;
    if($data =~ /^($regexp)($cmdchars)/i) {
      my $text = "You have " . ($1 eq ' '?'a space':$1) . " infront of your cmdchar '$2', is this what you wanted? [y/F/c]";
      $pending_input = {
                         text     => $data,
                         server   => $server,
                         win_item => $witem,
                       };

      Irssi::signal_stop();
      require_confirmation($text);
    }
    elsif($data =~ /^\s*($cmdregexp)\b/i) {
      my $text = "The first word, '$1', looks like a command, is this what you wanted? [y/F/c]";
      $isword = 1;
      $pending_input = {
                         text     => $data,
                         server   => $server,
                         win_item => $witem,
                       };

      Irssi::signal_stop();
      require_confirmation($text);
    }
  }
}

sub sig_gui_keypress {
  my ($key) = @_;

  return if not $active;

  my $char = chr($key);

  # we support f, F, enter for Fix.
  if($char =~ m/^f?$/i) {
    $permit_pending = 2 + $isword;
    Irssi::signal_stop();
    Irssi::signal_emit('send text',
                        $pending_input->{text},
                        $pending_input->{server},
                        $pending_input->{win_item});
    $active = 0;
    set_prompt('');
  }
  elsif($char =~ m/^y$/i) {
    # y or Y for send as is
    $permit_pending = 1;
    Irssi::signal_stop();
    Irssi::signal_emit('send text',
                        $pending_input->{text},
                        $pending_input->{server},
                        $pending_input->{win_item});
    $active = 0;
    set_prompt('');
  }
  elsif ($char =~ m/^c$/i or $key == 3 or $key == 7) {
    # we support c, C, Ctrl-C, and Ctrl-G for don't send
    Irssi::signal_stop();
    set_prompt('');
    $permit_pending = 0;
    $active         = 0;
    $pending_input  = {};
  }
  else {
    Irssi::signal_stop();
    return;
  }
}

sub app_init {
  Irssi::signal_add_first("send text"       => \&sig_send_text);
  Irssi::signal_add_first('gui key pressed' => \&sig_gui_keypress);
  Irssi::signal_add("commandlist new"       =>
                     sub {
                       $cmdregexp = list2re map {$_->{cmd}} Irssi::commands();
                     });
  Irssi::settings_add_str('Intercept', 'intercept_exceptions', 's/\w+/[\w\s\d]+/');
  Irssi::settings_add_str('Intercept', 'intercept_linestart', '\s');
}

sub require_confirmation {
  $active = 1;
  set_prompt(shift);
}

sub set_prompt {
  my ($msg) = @_;
  $msg = ': ' . $msg if length $msg;
  Irssi::signal_emit('change prompt', $msg, 'UP_INNER');
}

sub _debug {
  return unless $verbose;

  my ($msg, @params) = @_;
  my $str = sprintf($msg, @params);
  print $str;
}
