#
# disagree - irssi plugin to randomly disagree 
# with someone's heartfelt sentiment
#
# author: cj_ <cjones@gruntle.org>
#

use strict;
use Irssi;

my $NAME    = "disagree";
my $VERSION = "0.5";


# default settings
my $defaultDisagreeDensity = 100; # percentage of time we will disagree
my $defaultDisagreeWait    = 0;	  # how often after last disagree to go again
my $defaultDisagree        = 1;	  # whether we default to on or off


# register settings with Irssi
Irssi::settings_add_bool($NAME, "disagree", $defaultDisagree);
Irssi::settings_add_int($NAME, "disagree_density", $defaultDisagreeDensity);
Irssi::settings_add_int($NAME, "disagree_wait", $defaultDisagreeWait);

# these define the multiple types of face-parts
my @brow	= qw(\\* > d 8 O);
my @eyes	= qw(= : ; 8 B);
my @mouth	= qw(< > ( ) { } \\[ \\]);
my @nose	= qw(- = ~ ` ' v \\^);


######################
# regexp definitions #
######################

# define the outter boundaries
my $START	= '(?:^|\s)';
my $END		= '(?:\s|$)';  

# the face components
my $BROW	= '[' . join('', @brow)  . ']';
my $NOSE	= '[' . join('', @nose)  . ']';
my $EYES	= '[' . join('', @eyes)  . ']';
my $MOUTH	= '[' . join('', @mouth) . ']';

# nose is special: it can be missing, pinnochio-ed, or a river of tears
$NOSE .= "*";

# brow is optional
$BROW .= "?";

# the assembled faces, precompiled for performance
my $SMILE	= qr/$START ($BROW $EYES) ($NOSE) ($MOUTH) $END/x;	# :)
my $LAUGH	= qr/$START ($BROW $EYES) ($NOSE) D $END/x;		# :D
my $HORROR	= qr/$START D ($NOSE) ($BROW $EYES) $END/x;		# D:


# mouth pieces and their counter-parts,
# for easy flipping
my $flip_map = {
	">" => "<",
	"<" => ">",
	"(" => ")",
	")" => "(",
	"{" => "}",
	"}" => "{",
	"[" => "]",
	"]" => "[",
};

my $lastDisagree = 0;	# global for caching last disagree time
srand(time());		# seed randominator


sub signal_handler_public {
	# check if we have this enabled
	return unless (Irssi::settings_get_bool("disagree"));

	my ($server, $msg, $nick, $host, $chan) = @_;

	# callback to output text to
	# the origin chan
	my $display = sub {
		my $text = shift;

		# check if response is allowed
		if ( allowResponse() ) {
			$server->command("msg $chan $text");

			# remember when we last disagreed
			$lastDisagree = time();
		}
	};

	# these regexp's are precompiled.  see above for
	# how they are constructed.
	if	($msg =~ $SMILE)	{
		# for smilies, just flip the mouth :)
		$display->( $1 . $2 . flip($3) );
	} elsif	($msg =~ $LAUGH)	{
		# for laugh, need to reverse the orientation :D
		$display->( "D" . $2 . $1 );
	} elsif	($msg =~ $HORROR)	{
		# for horror, need to reverse the orientation D:
		$display->( $2 . $1 . "D" );
	}
}

sub flip {
	# flip mouth pieces to their verticle twin
	my $char = shift;

	if (exists $flip_map->{$char}) {
		return $flip_map->{$char};
	} else {
		return $char;
	}
}

sub allowResponse {
	# to prevent channel abuse, define a waiting period
	# after a succesful trigger before it can go off again.
	# to turn off, set to 0.
	my $wait = (
		Irssi::settings_get_int("disagree_wait") 
		|| $defaultDisagreeWait
	);

	if ($wait) {
		my $intervalSinceLastRun = time() - $lastDisagree;

		# a waiting period is defined, and it
		# hasn't been that long yet.  return false
		unless ($intervalSinceLastRun > $wait) {
			return 0;
		}
	}

	# since this is insanely annoying, only respond to a
	# defined percentage.  default is 100 :)
	my $allow = (
		Irssi::settings_get_int("disagree_density")
		|| $defaultDisagreeDensity
	);

	# sanity check
	if ($allow < 0 or $allow > 100) {
		return 0;
	}

	my $rand = int(rand(0) * 100) + 1;
	unless ($rand >= 1 and $rand <= $allow) {
		return;
	}

	# passed all hurdles
	return 1;
}


# register public signal handler with Irssi
Irssi::signal_add("message public", \&signal_handler_public);
Irssi::print("loaded $NAME $VERSION");



