package Parp::Utils;

=head1 NAME

Parp::Utils - various utility routines

=head1 DESCRIPTION

The end user need only concern herself with the documented routines.
The rest are used internally.

=head1 ROUTINES

=cut

use strict;
use warnings;

use Data::Dumper;
use Fcntl qw(:seek);
use File::Path;

use Parp::Config qw(config);
use Parp::Options qw(opt delivery_mode);

use base 'Exporter';
our @EXPORT_OK = qw(init_logfile diagnose vprint log_rule log_header log_mode
                    error fatal check_file_dir i2month month2i
                    lock_ident unlock_ident lock_file unlock_file);

sub init_logfile {
  my $log = config->log_file;
  die "No log_file specified in config\n" unless $log;
  check_file_dir($log);
  open(LOG, ">>$log") or die "Couldn't open log file `$log' for writing: $!\n";
  select((select(LOG), $| = 1)[0]);
}

sub close_log {
  close LOG if fileno LOG;
}

sub logging_to_file { delivery_mode() }

sub log_to_file {
  my (@msgs) = @_;
  init_logfile() if ! fileno LOG;
  seek LOG, 0, SEEK_END;
  print LOG @msgs;
}

=head2 diagnose(@messages)

Records filtering diagnostics in some way, usually by logging to the
log file.

=cut

sub diagnose (@) {
  my (@msgs) = @_;
  log_to_file(@msgs) if logging_to_file();
  print @msgs if opt('verbose') && ! logging_to_file();
}

=head2 vprint(@messages)

Same as diagnose, but messages are also printed when user specifies
the C<-v> option.

=cut

sub vprint (@) {
  my (@msgs) = @_;
  log_to_file(@msgs) if logging_to_file();
  print @msgs if opt('verbose');
}

my $WIDTH = 78;

sub log_mode {
  my $msg = join '', @_;
  $msg =~ s/^/### /gm;
  diagnose "###\n$msg\n###\n\n";
}

sub log_rule {
  diagnose $_[0] x $WIDTH, "\n";
}

sub check_file_dir {
  my ($file) = @_;

  my $umask = 0700;

  my ($dir) = $file =~ m!(.*)/!;
  return unless $dir;

  unless (-d $dir) {
    mkpath([$dir], 0, $umask);
    vprint sprintf "Created directory $dir with umask %04o.\n", $umask
      if fileno(LOG);
  }
}

sub log_header {
  my ($m) = @_;
  
  vprint "Parp-ID: ", $m->parp_id, "\n";

  diagnose $m->full_from    if $m->from;
  diagnose $m->full_to      if $m->to;
  diagnose $m->full_cc      if $m->cc;
  diagnose $m->full_subject if $m->subject;

  if ($m->parp_id) {
  }
  elsif (1) {
    error("Parp-ID not defined",
          "\$m:\n", Dumper($m),
         );
  }
  # the following cases should never happen
  elsif ($m->id && $m->id ne '<>') {
    vprint $m->full_id;
  }
  elsif ($m->date) {
    vprint $m->full_date;
  }
  elsif ($m->subject) {
    vprint $m->full_subject;
  }
  elsif ($m->from) {
    vprint $m->full_from;
  }
  
  diagnose "\n";
}

=head2 fatal($exit_code, @error_messages)

Die from a fatal error.

=cut

sub fatal {
  my $exit_code = shift;
  error(@_);
  exit $exit_code;
}

=head2 error($error, @context)

Generate an error but continue execution.

=cut

sub error {
  my ($error, @context) = @_;

  my $long_message = long_message($error, @context);
  $error =~ s/\.?\n?$//;

  if (logging_to_file()) {
    diagnose $long_message;
#   print $long_message if opt('verbose');

    my $log = config->log_file;
    warn "parp: $error",
         (@context && fileno LOG)
           ? "; see $log for more info.\n" : ".\n";
  }
  else {
    warn $long_message;
  }

  email_error($long_message);
}

sub long_message {
  my ($error, @context) = @_;

  my $long_message = <<EOF;

*************************************************************************

An error occurred:

EOF

  $long_message .= $error;
  $long_message .= @context ? ":\n\n" . join('', @context) : '.';

  $long_message .= <<EOF;

*************************************************************************
EOF

  return $long_message;
}

sub email_error {
  my ($long_message) = @_;

  my $errors = config->errors_folder;
  if ($errors and open(ERRORS, ">>$errors")) {
    my $date = localtime;
    my ($username, $realname) = (getpwuid $<)[0, 6];
    print ERRORS <<"End of report";
From $username\@localhost $date
From: "parp e-mail filter" <$username\@localhost>
To: "$realname" <$username\@localhost>
Date: $date
Subject: parp experienced an error

$long_message

End of report

    close ERRORS;
  }
}

my @months = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
my %month2i = map { $months[$_] => $_ } 0 .. 11;
my %i2month = map { $_ => $months[$_] } 0 .. 11;

sub month2i { $month2i{$_[0]} }
sub i2month { $i2month{$_[0]} }

END {
  close_log();
}

1;

