package Mail::SpamAssassin::Client;

use IO::Socket;

my $EOL = "\015\012";
my $BLANK = $EOL x 2;
my $PROTOVERSION = 'SPAMC/1.3';

sub new {
  my ($class, $args) = @_;

  $class = ref($class) || $class;

  my $self = {};

  # with a sockets_path set then it makes no sense to set host and port
  if ($args->{socketpath}) {
    $self->{socketpath} = $args->{socketpath};
  }
  else {
    $self->{port} = $args->{port};
    $self->{host} = $args->{host};
  }

  if ($args->{username}) {
    $self->{username} = $args->{username};
  }

  bless($self, $class);

  $self;
}

sub process {
  my ($self, $msg) = @_;

  my $remote = $self->_create_connection();

  return 0 unless ($remote);

  my $msgsize = length($msg.$EOL);

  print $remote "PROCESS $PROTOVERSION$EOL";
  print $remote "Content-length: $msgsize$EOL";
  print $remote "User: $self->{username}$EOL" if ($self->{username});
  print $remote "$EOL";
  print $remote $msg;
  print $remote "$EOL";

  my $line = <$remote>;
  my ($version, $resp_code, $resp_msg) = $self->_parse_response_line($line);

  # XXX - eventually will probably want some better error codes here
  return 0 unless ($resp_code == 0);

  $line = <$remote>;
  my $content_length;
  if ($line =~ /Content-length: (\d+)/) {
    $content_length = $1;
    $line = <$remote>;
  }
  my ($isspam, $score, $threshold);
print $line;
  if ($line =~ m!Spam: (\S+) ; (\S+) / (\S+)!) {
    $isspam = $1;
    $score = $2 + 0;
    $threshold = $3 + 0;
  }
  print "Content Length: $content_length\n";
  print "$isspam -- $score/$threshold\n";

  while(<$remote>) {
    print;
  }

  return 1;
}

sub check {
  my ($self, $msg) = @_;

  my $remote = $self->_create_connection();

  # XXX - need to check value of remote

  my $msgsize = length($msg.$EOL);

  print $remote "CHECK $PROTOVERSION$EOL";
  print $remote "Content-length: $msgsize$EOL";
  print $remote "User: $self->{username}$EOL" if ($self->{username});
  print $remote "$EOL";
  print $remote $msg;
  print $remote "$EOL";

  while(<$remote>) {
    print;
  }

  return 1;
}

sub learn {
  my ($self) = @_;

  my $remote = $self->_create_connection();

  return 0 unless ($remote);

  my $msgsize = length($msg.$EOL);

  print $remote "LEARN $PROTOVERSION$EOL";
  print $remote "Content-length: $msgsize$EOL";
  print $remote "User: $self->{username}$EOL" if ($self->{username});
  print $remote "Learn-type: 0$EOL";
  print $remote "$EOL";
  print $remote $msg;
  print $remote "$EOL";

  my $line = <$remote>;
print STDERR "--> $line\n";
  my ($version, $resp_code, $resp_msg) = $self->_parse_response_line($line);

  # XXX - eventually will probably want some better error codes here
  return 0 unless ($resp_code == 0);

  return 1;
}

sub ping {
  my ($self) = @_;
  my $pong_p = 0;

  my $remote = $self->_create_connection();

  return 0 unless ($remote);

  print $remote "PING $PROTOVERSION$EOL";
  print $remote "$EOL";

  my $line = <$remote>;
  close $remote;

  my ($version, $resp_code, $resp_msg) = $self->_parse_response_line($line);

  return 0 unless ($resp_msg eq 'PONG');

  return 1;
}

sub _create_connection {
  my ($self) = @_;

  my $remote;

  if ($self->{socketpath}) {
    $remote = IO::Socket::UNIX->new( Peer => $self->{socketpath},
				     Type => SOCK_STREAM,
				   );
  }
  else {
    $remote = IO::Socket::INET->new( Proto     => "tcp",
				     PeerAddr  => $self->{host},
				     PeerPort  => $self->{port},
				   );
  }

  unless ($remote) {
    print "Failed to create connection to spamd daemon: $!\n";
    return undef;
  }

  $remote;
}

sub _parse_response_line {
  my ($self, $line) = @_;

  return split(/\s+/, $line);
}

1;
