package MailScanner::POP3;

use MailScanner;
use base MailScanner;

use strict;
use warnings;

use Mail::POP3Client;

use Class::Std::Utils;
{
	my %pop3_of;
	
	sub new
	{
		my ( $class ) = @_;

        my $new_object = bless \do{ my $anon_scalar }, $class;
                
		# defer instantiation of Mail::POP3Client until connect time

        return $new_object;
	}
	
	sub DESTROY
	{
		delete $pop3_of{ ident shift @_ };
	}
	
	sub connect
	{
		my ( $self, $hostname, $port, $ssl, $user, $password, $error_ref ) = @_;
# TODO: Test SSL on non-standard port
		my $pop3 = new Mail::POP3Client(
			USER 	 => $user,
			PASSWORD => $password,
			HOST 	 => $hostname,
			PORT 	 => $port,
			USESSL   => $ssl
		);
		
		# as per Mail::POP3Client docs, check for number of messages to
		# see if connection succeeded
		my $msg_count = $pop3->Count();
		
		if ( $msg_count == -1 ) {
			$$error_ref = "Connection failed: " . $pop3->Message();
			return 0;
		}
		
		$pop3_of{ ident $self } = $pop3;
		
		return 1;
	}
	
	sub disconnect
	{
		my ( $self ) = @_;
		
		$self->pop3_()->close();
	}

	sub getCandidateMessageNumbers
	{
		my ( $self, $config, $message_number_to_uid_ref, $error_ref ) = @_;

		my $message_count = $self->pop3_()->Count();

		if ( !$self->pop3_()->Alive() || $message_count == -1 ) {
			$$error_ref = "Connection dropped: " . $self->pop3_()->Message();
			return undef;
		}
		
		my @uids = $self->pop3_()->Uidl();
		
		if ( @uids ) {
			my %processed_uids = map { $_ => 1 } @{ $config->{ ProcessedUIDs } };
			my @message_numbers;
			my $index = 0;
						
			foreach my $uid ( @uids ) {
				if ( $uid && !$processed_uids{ $uid } ) {
					push @message_numbers, $index;
					$message_number_to_uid_ref->{ $index } = $uid;
				}
				
				$index++;
			}
			
			return @message_numbers;
		}

		return ( 0..( $message_count - 1 ) );
	}
	
	sub getMessageHeaders
	{
		my ( $self, $message_number, $req_headers_ref, $error_ref ) = @_;
		
		if ( !$self->pop3_()->Alive() ) {
			$$error_ref = "Connection dropped: " . $self->pop3_()->Message();
			return undef;
		}
		
		my @header_lines = $self->pop3_()->Head( $message_number );
		my $headers_ref  = {};

		if ( ! scalar @header_lines ) {
			@header_lines = $self->pop3_()->Retrieve( $message_number );
		}
		
		if ( ! scalar @header_lines ) {
			$$error_ref = $self->pop3_()->Message();
			return undef;
		}
		
		for my $key ( @$req_headers_ref ) {
			for my $line ( @header_lines ) {
				if ( $line =~ m/^$key: (.+)/i ) {
					$headers_ref->{ $key } = [ $1 ];
					last;
				}
			}
		}
		
		return $headers_ref;
	}
	
	sub getRawMessage
	{
		my ( $self, $message_number, $error_ref ) = @_;

		if ( !$self->pop3_()->Alive() ) {
			$$error_ref = "Connection dropped: " . $self->pop3_()->Message();
			return undef;
		}
		
		return scalar $self->pop3_()->Retrieve( $message_number );
	}

	sub deleteMessage
	{
		my ( $self, $message_number ) = @_;
		
		$self->pop3_()->Delete( $message_number );
	}
	
	sub pop3_
	{
		return $pop3_of{ ident shift @_ };
	}
}

1;
