#!/usr/bin/perl -w

use strict;

use FindBin;
use lib "$FindBin::RealBin/../lib";
use Curses::UI;

use Curses;
use Net::POP3;
use Mail::Header;
use Mail::Address;

my $cui        = new Curses::UI ( -clear_on_exit => 1 );
my $pop3       = undef;
my $connection = undef;

# We do not want STDERR to clutter our screen.
open STDERR, ">/dev/null";

# ----------------------------------------------------------------------
# setup(): Setup the connection
# ----------------------------------------------------------------------

sub check_connection($;)
{
	my $buttons = shift;
	my $conwin  = $buttons->parent;
	my $cui     = $conwin->root;

	foreach my $key ('username','password','host','port') 
	{
		my $obj = $conwin->getobj($key);
		my $value = $obj->get;
		$connection->{$key} = $value;
		# TODO: focus back to entry does not seem to
		# move the cursor with it.
		if ($value =~ /^\s*$/) {
			$cui->error("Missing value for $key field");
			$obj->focus;
			return;
		}
	}

	return 1;
}

sub setup_connection()
{
	my $conwin = $cui->add(
		'connection_window', 'Window',
		-border => 1,
		-ipad => 2,
		-height => 15,
		-width => 60,
		-centered => 1,
		-title => "POP3 connection",
	);	 

	$conwin->add(
		'host_label', 'Label',	
		-x => 0, -y => 0, -width => 13,
		-textalignment => 'right',
		-text => 'POP3 host :',
	);

	$conwin->add(
		'host', 'TextEntry',
		-x => 14, -y => 0,
		-text => 'pop',
	);

	$conwin->add(
		'port_label', 'Label',	
		-x => 0, -y => 2, -width => 13,
		-textalignment => 'right',
		-text => 'POP3 port :',
	);

	$conwin->add(
		'port', 'TextEntry',
		-x => 14, -y => 2,
		-regexp => '/^\d*$/',	
		-text => '110',
	);

	$conwin->add(
		'username_label', 'Label',	
		-x => 0, -y => 4, -width => 13,
		-textalignment => 'right',
		-text => 'Username :',
	);

	$conwin->add(
		'username', 'TextEntry',
		-x => 14, -y => 4,
		-text => getpwuid($>),
	);

	$conwin->add(
		'password_label', 'Label',	
		-x => 0, -y => 6, -width => 13,
		-textalignment => 'right',
		-text => 'Password :',
	);

	$conwin->add(
		'password', 'TextEntry',
		-x => 14, -y => 6,
		-password => '*',
		-text => '',
	)->focus;

	my $buttons = $conwin->add(
		'buttons', 'Buttonbox',
		-x => 14, -y => 8,
		-buttons => [
		    { -label => '< Connect >',
		      -onpress => sub {
		          my $this = shift;
		          if (check_connection($this)) {
			      if (pop3_connect()) {
			          $this->parent->loose_focus;
			      }
			  }
		      },
		    },
		    { -label => '< Quit >',
		      -onpress => sub {exit} },
		],
	);

	$conwin->modalfocus;
	$cui->delete('connection_window')
}

# ----------------------------------------------------------------------
# pop3_connect(): Connect to the POP3 server and exit if it fails
# ----------------------------------------------------------------------

sub pop3_connect()
{
	$cui->progress(
		-message => "Connecting to the POP3 server...",
		-max => 4,
		-pos => 1,
	);

	my $error = 0;

	$pop3 = Net::POP3->new(
		$connection->{host},
		Port => $connection->{port},
		Timeout => 0,
	);

	if (not $pop3) {
		$error++;
		$cui->error("Could not connect to "
		           ."$connection->{host}:$connection->{port}");
	}

	$cui->setprogress(2, "Sending username...");
	if (not $error and not defined $pop3->user($connection->{username})) {
		$error++;
		my $err = $pop3->message(); chomp $err;
		$cui->error("Sending USER failed:\n$err");
	}
	
	$cui->setprogress(3, "Sending password...");
	if (not $error and not defined $pop3->pass($connection->{password})) {
		$error++;
		my $err = $pop3->message(); chomp $err;
		$cui->error("Sending PASS failed:\n$err");
	}

	if (not $error) {
		$cui->setprogress(4, "Connection successful!");
		sleep 1;
	}

	$cui->noprogress;

	return !$error;
}

# ----------------------------------------------------------------------
# The inbox screen
# ----------------------------------------------------------------------

sub build_inbox()
{
	my $list = $pop3->list();
	my @ids = sort {$a<=>$b} keys %$list;

	my $msg = "Retrieving headers";
	$cui->progress(
		-max => scalar(@ids),
		-message => $msg,
	);

	my @values = ();
	my %labels = ();
	my $progress_pos = 0;
	foreach my $n (@ids)
	{
		my $lines = $pop3->top($n, 0);
		my $header = new Mail::Header($lines);

		# Add value
		push @values, $n;

		# Add label
		my $subject = $header->get('Subject');
		my $from    = $header->get('From');
	 	my $addr = new Mail::Address($from);	
		my $name = substr($addr->name, 0, 15);
		$labels{$n} = sprintf("%4d", $n)
			    . " | "
			    . sprintf("%15s", $name)
			    . " | "
                            . $header->get('Subject');
	
		$cui->setprogress(
			++$progress_pos,
			$msg . ": message $progress_pos of " . scalar(@ids)
		);
	}
	$cui->noprogress;

	my $listwin = $cui->add('list_window', 'Window');
	my $ml = $listwin->add(
		'message_list', 'Listbox',
		-values => \@values,
		-labels => \%labels,
		-vscrollbar => 1,
		-border => 1,
		-ipad   => 1,
		-title  => '<ENTER> view message <CTR+Q> Quit from program',
	);
	
	$ml->set_binding(sub{exit(0)}, "\cC", "\cQ");
	$ml->set_routine('option-select', \&view_message);
}

# ----------------------------------------------------------------------
# view_message(): callback routine for the inbox list
# ----------------------------------------------------------------------

sub view_message()
{
	my $this = shift;	

	# Get the selected message id.
	$this->{-selected} = $this->{-ypos};
	my $id = $this->get;
	$this->{-selected} = undef;
	
	# Retrieve the message from the POP3 server.
	$cui->status("Retrieving message $id from the POP3 server...");
	my $lines = $pop3->get($id);		
	unless (ref $lines)
	{
		# Maybe the connection went away. Reconnect and try again.
		$pop3->close;
		unless (pop3_connect()) 
		{
			$cui->error("Fatal error: Could not reconnect\n"
			          . "to the POP3 server.");
	 		exit(1);
		}
		$lines = $pop3->get($id);		
	}
	unless (ref $lines)
	{
		$cui->error("Failed to retrieve message $id\n"
		           ."from the POP3 server.\n"
                           ."Even after reconnecting");
		exit_program();
	}
	$cui->nostatus;

	# Create the viewer window.
	my $viewwin = $cui->add('view_window', 'Window');
	my $tv = $viewwin->add(
		'textviewer', 'TextViewer',
		-text => join("", @$lines),
		-vscrollbar => 1,
		-wrapping => 1,
		-border => 1,
		-ipad   => 1,
		-title  => '<ENTER> return to inbox <CTRL+Q>: Quit from program',
	);

	$viewwin->set_binding(sub{ shift()->loose_focus }, KEY_ENTER());
	$viewwin->set_binding(sub{exit(0)}, "\cC", "\cQ");
	$viewwin->modalfocus;
	$cui->delete('view_window');
	$cui->draw;
}

# ----------------------------------------------------------------------
# Clean exit
# ----------------------------------------------------------------------

END { $pop3->quit if defined $pop3 }

# ----------------------------------------------------------------------
# The main program
# ----------------------------------------------------------------------

setup_connection();
build_inbox();
$cui->mainloop;

