package NetHirc::Shell;

use strict;
use warnings;

use POE qw(Kernel);

use NetHirc::Util;

use constant SSFE_TAG => '@ssfe@';

my %parsetable = ();


sub new
{
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {
	'cmdchar'	=>	'/',
	'happyslash'	=>	0,
    };
    return bless $self, $class;
}

sub run_once
{
    my $self = shift;
    my $input = shift;
    if (substr($input, 0, 6) eq SSFE_TAG)
    {
	$poe_kernel->post('nethirc_terminal', 'ssfe', 1);
	return;
    }
    my $line = $self->precmd($input);
    my ($cmd, @args) = $self->parseline($line);
    return unless $cmd;
    debug('s', "cmd: %s", $cmd);
    debug('s', "args: %s", join(' ', @args));
    $poe_kernel->post('nethirc_adventurer', "cmd_$cmd", @args);
}

sub precmd
{
    my $self = shift;
    my $line = shift;
    return $line unless $line;
    my $cmdchar = $self->{'cmdchar'};
    if (substr($line, 0, 1) ne $cmdchar)
    {
	$line = "say $line";
    }
    $line =~ s,^$cmdchar,,;
    return $line;
}

sub emptycommand
{
    debug('s', "emptycommand");
    $poe_kernel->post('nethirc_adventurer', 'complain');
}


sub parseline
{
    my $self = shift;
    my $line = shift;
    debug('s', "parseline: $line");
    unless ($line)
    {
	$self->emptycommand();
	return undef;
    }
    for my $k (keys %parsetable)
    {
	if ($line =~ $k)
	{
	    debug('s', "$k match: $line");
	    return $parsetable{$k}->($line);
	}
    }
    return ('error', split(' ', $line, 2));
}

sub parse_zero
{
    my $line = shift;
    debug('s', "parse_zero: $line");
    return (split(' ', $line, 2))[0];
}

sub parse_zero_splat
{
    my $line = shift;
    debug('s', "parse_zero_splat: $line");
    return split(' ', $line);
}

sub parse_one
{
    my $line = shift;
    debug('s', "parse_one: $line");
    my @pieces = (split(' ', $line, 3))[0,1];
    if (@pieces < 2)
    {
	return ('argcount', @pieces);
    }
    return @pieces;
}

sub parse_one_trailer
{
    my $line = shift;
    debug('s', "parse_one_trailer: $line");
    my @pieces = split(' ', $line, 3);
    if (@pieces < 2)
    {
	return ('argcount', @pieces);
    }
    return @pieces;
}

sub parse_trailer
{
    my $line = shift;
    debug('s', "parse_trailer: $line");
    return split(' ', $line, 2);
}

sub parse_two
{
    my $line = shift;
    debug('s', "parse_two: $line");
    my @pieces = (split(' ', $line, 4))[0..2];
    if (@pieces < 3)
    {
	return ('argcount', @pieces);
    }
    return @pieces;
}

sub parse_two_trailer
{
    my $line = shift;
    debug('s', "parse_two_trailer: $line");
    my @pieces = split(' ', $line, 4);
    if (@pieces < 3)
    {
	return ('argcount', @pieces);
    }
    return @pieces;
}

my %argtable = (
    '0' => [ \&parse_zero, qw( debug empty next ) ],
    '0t' => [ \&parse_trailer, qw( away say echo me my quit quote wallops yoda pigl mirror shuffle pipe system ) ],
    '*' => [ \&parse_zero_splat, qw( cmdchar disconnect info ison join list lusers mode motd names part server time userhost users version who whois whowas inventory help log count bigbrother query ) ],
    '1' => [ \&parse_one, qw( admin nick ping source ) ],
    '1t' => [ \&parse_one_trailer, qw( describe msg notice topic pipemsg ) ],
    '2' => [ \&parse_two, qw( invite ) ],
    '2t' => [ \&parse_two_trailer, qw( ctcp kick ) ],
);


for my $i (keys %argtable)
{ 
    my $v = $argtable{$i};
    my $parser = $v->[0];
    my @cmds = @{$v}[1..$#$v];
    my @sortcmds = sort { (length $b <=> length $a) || ($a cmp $b) } @cmds;
    my $pattern = sprintf('^(?:%s)(\s|$)', join('|', @sortcmds));
    $parsetable{qr/$pattern/} = $parser;
}

1;
__END__


syntax highlighted by Code2HTML, v. 0.9.1