#!/usr/bin/perl -w

# Talking IRC Xchat plugin to interface w/ speechd(http://speechio.undef.net/)
# Copyright (C) 2002  Dave O'Neill <dmo@acm.org>

# Based on the original speech.pl, copyright (C) 1999  Joseph Elwell

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

use Data::Dumper;

IRC::print "\0035:: Loaded Talking IRC\003 \n";
IRC::print "\0035:: /speech help for more information \003 \n";
IRC::register ("speech script", ".3", "cleanup", "");
IRC::add_command_handler("speech", "cmd_handler");
IRC::add_message_handler("PRIVMSG", "say_incoming_text");

say_line("Talking I.R.C. loaded.\n");

$word_mapping = {};
$savefile = $ENV{'HOME'}."/.speechmap";
$speech_on = 0;
$prev_speaker = undef;

$handlers = { 
	'on'	=> \&on,
	'off'	=> \&off,
	'set'	=> \&set,
	'unset'	=> \&unset,
	'get'	=> \&get,
	'load'	=> \&load,
	'save'	=> \&save,
	'list'	=> \&list,
	'help'	=> \&help,
};

sub cmd_handler 
{
	my $args = shift;
	$args =~ s/\s+/ /g;
	my ($cmd,@args) = split(/\s/,$args);
	if(exists($handlers->{$cmd})) {
		$handlers->{$cmd}->(@args);
	} else {
		IRC::print "\0035! $cmd is not a valid command.\003 \n";
	}

	return 1;
}

## Say a line of text, with substitutions.
sub say_line 
{
	my $line = shift;	
	
	## Lowercase it all for matching purposes
	$line = lc($line);
	
	## Perform substitutions for pronunciation mappings
	$line =~ s/(\w+)/$word_mapping->{$1} || $1/eg;
	
	open (SPEECH, ">>/dev/speech") || return 0;
	print SPEECH $line;
	close (SPEECH);
}

sub help
{
	IRC::print "\0035Support for /dev/speech in XChat\003 \n";
	IRC::print "\0035Available commands are: \003 \n";
	IRC::print "\0035/speech help\003 \n";
	IRC::print "\0035    - this command\003 \n";
	IRC::print "\0035/speech on\003 \n";
	IRC::print "\0035    - turns on speech support\003 \n";
	IRC::print "\0035/speech off\003 \n";
	IRC::print "\0035    - turns off speech support\003 \n";
	IRC::print "\0035/speech set <lhs> <rhs>\003 \n";
	IRC::print "\0035    - defines a translation from lhs to rhs before speaking.\003 \n";
	IRC::print "\0035/speech unset <lhs>\003 \n";
	IRC::print "\0035    - removes a translation.\003 \n";
	IRC::print "\0035/speech get <lhs>\003 \n";
	IRC::print "\0035    - display the translation for lhs, if any\003 \n";
	IRC::print "\0035/speech list\003 \n";
	IRC::print "\0035    - list all translations\003 \n";
	IRC::print "\0035/speech load [filename]\003 \n";
	IRC::print "\0035    - load translations from filename (default is $savefile)\003 \n";
	IRC::print "\0035/speech save [filename]\003 \n";
	IRC::print "\0035    - save current translations to filename (default is $savefile)\003 \n";
}

## Turn speech on.
sub on 
{
	if($speech_on) {
		say_line("speech is already active.");
	} else {
		$speech_on = 1;
		say_line("speech has been activated.");
	}
}

## Turn speech off
sub off
{
	if($speech_on) {
		$speech_on = 0;
		say_line("speech has been dee-activated.");
	} else {
		say_line("speech is already dee-activated.");
	}
}

sub set
{
	my $lhs = shift;
	my $rhs = join(" ",@_);
	$word_mapping->{lc($lhs)} = lc($rhs);
	IRC::print "\0035$lhs is now mapped to [$rhs]\003 \n";
}

sub unset
{
	my $lhs = shift;
	delete($word_mapping->{lc($lhs)});
	IRC::print "\0035$lhs is now unmapped\003 \n";
}

sub get
{
	my $word = shift;
	$word = lc($word);
	if(exists($word_mapping->{$word})) {
		IRC::print "\0035$word maps to [$word_mapping->{$word}]\003 \n";
	} else {
		IRC::print "\0035! No mapping for $word\003 \n";
	}
}

sub list
{
	foreach my $foo (sort keys %$word_mapping) {
		IRC::print "\0035    $foo maps to [$word_mapping->{$foo}]\003 \n";
	}
}

sub save
{
	my $file = shift || $savefile;
	unless(open(MAP, ">$file")) {
		IRC::print "\0035! Couldn't open $file: $!\003 \n";
		return 0;
	}
	print MAP Data::Dumper->Dump([$word_mapping],[qw(word_mapping)]);
	close (MAP);
	say_line("Mappings saved to $file\n");
	IRC::print "\0035Mappings saved to $file\003 \n";
}

sub load
{
	my $file = shift || $savefile;
	unless(open(MAP, "<$file")) {
		IRC::print "\0035! Couldn't open $file: $!\003 \n";
		return 0;
	}
	local($/);
	undef($/);
	$mappings = <MAP>;
	close MAP;
	eval $mappings;
	if($@) {
		IRC::print "\0035! Error reading from $file: $@\003 \n";
	} else {
		say_line("Mappings loaded from $file\n");
		IRC::print "\0035Mappings loaded from $file\003 \n";
	}
}

sub say_incoming_text
{

	my $line = shift(@_);
	if ($speech_on)
	{
		$is_action = 0;
		
		## GRR. Need a better regexp here, as this one fails on smileys.
 		my ($speaker,$addr,$txt) = $line =~ /:(.*)!(.*@.*) .*:(.*)/;
		if($txt =~ s/ACTION/$speaker/g) {
			$is_action = 1;
		}

		## DMO: $prev_speaker hack removes name-saying for multiple 
		## sequential lines from same person.
		if (($speaker eq $prev_speaker) || $is_action) {
			say_line("$txt\n");
		} else {
			$prev_speaker = $speaker;
			say_line("$speaker says $txt\n");
		}
	}

	return 0;
}

sub cleanup
{
	say_line("talking  i r c  closed.\n");
}
