# Copyright 2002-2006, Fred Steinberg, Brown Bear Software

package MailSubscription;
use strict;

use Calendar::Date;
use Calendar::EventFormatter;
use Calendar::I18N;
use Calendar::Mail::MailSender;
use Calendar::MasterDB;
use Calendar::User;

# 'whichOnes' is which subs to process:
#    - c:  by category
#    - e:  by specific event
#    - a:  all in calendar
sub new {
    my $class = shift;
    my %args = (verbose     => undef,
                baseURL     => undef,     # URL to Calcium script
                defaultDays => 3,
                whichOnes   => undef,     # which subs to process, def. all
                dryRun      => undef,     # for test, debug
                deliverTo   => undef,     # for test, debug
                @_);
    bless \%args, $class;
}

sub sendForToday {
    my $self = shift;

    $self->{mPrefs} = MasterDB->new->getPreferences;
    $self->{calAnonOffsets} = {};

    my $whichOnes = $self->{whichOnes} || 'cea';

    my $today = Date->new;

    my @calendars = MasterDB->getAllCalendars;

    foreach my $calName (@calendars) {
        my $db    = Database->new ($calName);
        my $prefs = $db->getPreferences;

        next unless $prefs->RemindersOn; # skip cals that are off

        $self->{calAnonOffsets}->{$calName} = $prefs->DefaultTimezone || 0;

        my $numDays = $prefs->RemindDays || $self->{defaultDays};
        my @numDays = split ' ', $numDays;

        my %eventsByAddress;        # each address gets 1 message w/possibly
                                    #  many events

        # Get reminder mail addresses
        my @toAll = ($whichOnes =~ /a/) ? $prefs->getRemindAllAddresses : ();
        my $byCat = ($whichOnes =~ /c/) ? $prefs->getRemindByCategory   : {};

        foreach my $days (@numDays) {
            my $theDate = $today + $days;

            my @events = $db->getApplicableEvents ($theDate, $prefs);

            if ($self->{verbose}) {
                my $m = "Calendar: '$calName'; $days days in advance " .
                           "- $theDate;";
                my $e = 'event' . (@events == 1 ? '' : 's');
                warn "$m ", @events+0, " $e found.\n";
            }

            next unless @events;

            foreach my $event (@events) {
                my %sendTo;

                my @addresses;

                # people who want all events
                @addresses = @toAll;

                # people who want events in any of these categories
                if ($event->getCategoryList) {
                    foreach my $cat (keys %$byCat) {
                        push @addresses, @{$byCat->{$cat}}
                            if $event->inCategory ($cat);
                    }
                }

                # people who want this particular event for this calendar (as
                #  opposed to every calendar this event might be included into)
                if ($whichOnes =~ /e/) {
                    my $addrs = $event->getSubscribers ($calName);
                    if ($addrs) {
                        push @addresses, split ',', $addrs;
                    }
                }

                next unless @addresses;

                # skip it if it's a daily repeating event and this isn't
                # the first day
                if ($event->isRepeating and
                    $event->repeatInfo->frequency == 1   and
                    $event->repeatInfo->period =~ /day/i and
                    $event->repeatInfo->startDate != $theDate) {
                    warn "skipping non-first daily repeating event\n"
                        if $self->{verbose};
                    next;
                }

                foreach (@addresses) {
                    $sendTo{lc($_)}++;
                }

                foreach my $addr (keys %sendTo) {
                    $eventsByAddress{$addr} ||= {};
                    $eventsByAddress{$addr}->{"$theDate"} ||= [];
                    push @{$eventsByAddress{$addr}->{"$theDate"}}, $event;
                }
            }
        }
        $self->_sendSubscriptions ($calName, $prefs, \%eventsByAddress);
        warn "\n" if $self->{verbose};
    }
    return;
}

sub _sendSubscriptions {
    my ($self, $calName, $prefs, $theHash) = @_;
    return unless ($calName and $prefs and keys %$theHash);

    my $i18n = I18N->new ($prefs->Language);

    my %mailParams = (From => $prefs->MailFrom
                              || (defined ($self->{mPrefs}->MailFrom) &&
                                           $self->{mPrefs}->MailFrom)
                              || "calcium\@$ENV{SERVER_NAME}",
                      SMTP => $self->{mPrefs}->MailSMTP,
                      );

    foreach my $addr (keys %$theHash) {
        # if this address has a user associated with it, use that tz offset
        # otherwise, use default offset for the calendar
        my $offset;
        if (my $user = User->userFromAddress ($addr)) {
            $offset = $user->timezone || 0;
        } else {
            $offset = $self->{calAnonOffsets}->{$calName} || 0;
        }

        my ($firstEvent, $firstDate);         # for custom subject:

        my $count = 0;
        my ($textmail, $htmlmail) = $self->_subMailHeadings;
        my $numDates = keys %{$theHash->{$addr}};
        foreach my $date (sort map {sprintf "%4s/%02s/%02s", split '/'}
                               keys %{$theHash->{$addr}}) {
            $date = Date->new ($date);
            my $events = $theHash->{$addr}->{$date};
            my $numToday = @$events;
            $count += $numToday;
            foreach (@$events) {
                $firstEvent ||= $_;
                $firstDate  ||= $date;
                my $event = $_->copy; # copy so timezone changes don't stick
                my $dateChange = $event->adjustForTimezone ($date, $offset);
                my $theDate = $date + $dateChange;
                my ($text, $html) = $event->formatForMail ($theDate, undef,
                                                           $prefs, $i18n);
#                $_->TZoffset (undef); # must clear for other addresses

                $textmail .= "$text";
                $htmlmail .= $html;

                if (--$numToday) { # if any more following
                    $textmail .= "----\n";
                    $htmlmail .= '<hr width="5%" align="left">';
                }
            }
            if (--$numDates) { # if any more following
                $textmail .= "----\n";
                $htmlmail .= '<hr width="15%" align="left">';
            }
            $textmail .= "\n";
            $htmlmail .= '<br>';
        }
        my ($textfoot, $htmlfoot) = $self->_subMailFootings ($calName, $addr,
                                                             $prefs, $i18n);
        $textmail .= $textfoot;
        $htmlmail .= $htmlfoot;

        if ($self->{verbose}) {
            warn "  Mailing to $addr\n";
            warn "    TZ Offset: $offset hours\n" if $offset;
        }
        next if $self->{dryRun};

        $addr = $self->{deliverTo} if $self->{deliverTo};
        #print "  Mailing to $addr: \n\n$textmail\n\n$htmlmail\n\n";

        $mailParams{To} = $addr;
        my $mailer = MailSender->new (%mailParams);
        my $subj = $prefs->SubscribeSubject;
        if ($subj) {
            $subj = $firstEvent->expandString ($subj, $firstDate, $i18n);
            $subj =~ s/[\r\n]/ /g; # remove newlines or mail gets munged
        } else {
            $subj = ($count > 1) ? 'Upcoming Events from Calendar: '
                                 : 'Upcoming Event from Calendar: ';
            $subj = $i18n->get ($subj) . "'$calName'";
        }
        my $format = $prefs->MailFormat || 'both';
        my %contents = (text => $textmail,
                        html => $htmlmail);
        if (lc ($format) eq 'text') {
            delete $contents{html};
        } elsif (lc ($format) eq 'html') {
            delete $contents{text};
        }
        my $ok = $mailer->send ($subj, \%contents);
        warn "Mail notification failed! " . $mailer->error . "\n"
            unless $ok;
    }
}

sub _subMailHeadings {
    my $self = shift;
    my $text = '';
    my $html =
          qq {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"} .
          "\n" .
          qq {        "http://www.w3.org/TR/REC-html40/loose.dtd">} .
          "\n" .
          "<html><head><title>Calcium Subscription Mail</title></head><body>" .
          "\n";
    return ($text, $html);
}

sub _subMailFootings {
    my ($self, $calName, $email, $prefs, $i18n) = @_;
    my $text = '';
    my $html = '';
    if ($self->{baseURL}) {
        my (@labels, @urls, @links);

        my $view   = $i18n->get ('View the calendar:');
        my $change = $i18n->get ('Change your subscription:');
        my $link   = $i18n->get ('Subscription Settings');

        my $max = length (length ($change) > length ($view) ? $change : $view);

        push @labels, sprintf ("%-*s\n", $max, $view);
        push @labels, sprintf ("%-*s\n", $max, $change);

        push @urls,   "$self->{baseURL}?CalendarName=$calName";
        push @links,  qq {<a href="$urls[0]">$calName</a>};

        push @urls,   "$self->{baseURL}?CalendarName=$calName&" .
                          "Op=OptionSubscribe&EmailAddress=$email";
        push @links,  qq {<a href="$urls[1]">$link</a>};

        $html .= "<table>";
        foreach my $i (0..(@labels - 1)) {
            $text .= "$labels[$i] $urls[$i]\n";
            $html .= "<tr><td>$labels[$i]</td><td>$links[$i]</td></tr>";
        }
        $html .= "</table>";

    }
    if (my $sig = $prefs->MailSignature || $self->{mPrefs}->MailSignature) {
        $text .= "----\n$sig";
        $html .= '<hr width="25%" align="left"/><p>' .
                 Event::_escapeThis ($sig, undef, 1) . '</p>';
    }
    $html .= qq {</body></html>};
    return ($text, $html);
}

1;
