# This file is part of Email-Reminder.
#
# Email-Reminder is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Email-Reminder is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Email-Reminder; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

package EmailReminder::YearlyEvent;

use strict;
use warnings;
use overload '""' => \&str;
use Date::Manip;
use POSIX;

use EmailReminder::Utils;

use base qw(EmailReminder::Event);

# XML tags
__PACKAGE__->mk_accessors(qw(day month year));

# Global date variables
my $current_time = ParseDate("now");
my $current_date = ParseDate(UnixDate($current_time, "\%x"));
my $current_year = UnixDate($current_time, "\%Y");
my $leap_year    = "1980";

sub str {
    my ($self) = @_;
    return $self->get_type . ':' . $self->get_id . ') ' . $self->get_name . ' - ' . $self->get_date;
}

# Hard-coded value for this event's type (class method)
sub get_type
{
    return 'yearly';
}

# Number of fields this event adds to its parent (class method)
sub get_nb_fields
{
    my ($self) = @_;
    return $self->SUPER::get_nb_fields() + 2;
}

sub valid_day
{
    my ($class, $new_value) = @_;

    return 1 unless defined $new_value;
    $new_value = int($new_value);
    return $new_value if ($new_value >= 1 and $new_value <= 31);
    return 1;
}

sub valid_month
{
    my ($class, $new_value) = @_;

    return 1 unless defined $new_value;
    $new_value = int($new_value);
    return $new_value if ($new_value >= 1 and $new_value <= 12);
    return 1;
}

sub valid_year
{
    my ($class, $new_value) = @_;
    return 0 unless defined $new_value;
    return int($new_value);
}

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

    my $day   = $self->get_day;
    my $month = $self->get_month;
    my $year  = $self->get_year;

    # Hack to support events that don't have a starting year
    my $actual_date;
    if ($year > 0) {
        $self->{"MISSING_YEAR"} = 0;
        $actual_date = UnixDate(ParseDate($year ."-".$month."-".$day),
                                "\%Y-\%m-\%d");
    }
    elsif (($month > 0) && ($day > 0)) {
        $self->{"MISSING_YEAR"} = 1;
        my $full_date = UnixDate(ParseDate($leap_year."-".$month."-".$day), 
                                 "\%Y-\%m-\%d");
        $actual_date = UnixDate(ParseDate($full_date), "\%m-\%d");
    }

    return $actual_date;
}

sub set_date
{
    my ($self, $new_value) = @_;
    my $date;

    # Normalize the date entered by the user
    my @slash_parts = split(/\//, $new_value);
    my @dash_parts = split(/-/, $new_value);
    if (@slash_parts == 2) {
        $date = $new_value;
        if ($slash_parts[0] <= 12) {
            # Looks like a MM/DD date, don't parse further
            $date =~ s/(.*)\/(.*)/$1-$2/g;
        } else {
            # Looks like a DD/MM date, don't parse further
            $date =~ s/(.*)\/(.*)/$2-$1/g;
        }
    }
    elsif (@dash_parts == 2) {
        $date = $new_value;
        if ($dash_parts[0] > 12) {
            # Looks like a DD-MM date, don't parse further
            $date =~ s/(.*)-(.*)/$2-$1/g;
        }
    }
    else {
        # Try to parse the date in whatever form the user typed it
        $date = UnixDate(ParseDate($new_value), "\%Y-\%m-\%d");
    }    
    
    if (defined($date)) {
        my @parts = split /-/, $date;
        my $day = pop(@parts);
        my $month = pop(@parts);
        my $year = undef;
        $year = pop(@parts) if @parts;

        return ($self->set_day($day) and $self->set_month($month) and $self->set_year($year));
    } else {
        return 0;
    }
}

sub get_original_date
{
    my ($self) = @_;
    my $date_string = $self->get_date();
    return unless $date_string;

    $date_string = $leap_year."-".$date_string if $self->{"MISSING_YEAR"};
    return ParseDate($date_string);
}

sub get_subject
{
    my $self = shift;
    return $self->get_name();
}

sub get_message_body
{
    my $self = shift;

    # event details
    my $when      = $self->{"WHEN"} || '';
    my $name      = $self->get_name();
    my $occurence = $self->get_occurence();
    my $th        = EmailReminder::Utils::get_th($occurence);
    my $event     = defined($occurence) ? "${occurence}$th $name" : $name;
    
    my $message = <<"MESSAGEEND";
I just want to remind you of the following event $when:

$event
MESSAGEEND

    return $message;
}

# Returns the occurence number of this event (starts at 1)
sub get_occurence
{
    my $self = shift;

    my $exact_age;
    unless ($self->{"MISSING_YEAR"}) {
        my $original_date = $self->get_original_date();
        return unless $original_date;

        my $delta = DateCalc($original_date, $current_date, 1);
        $exact_age = Delta_Format($delta, 5, '%yd') + 1;
    }

    return defined($exact_age) ? ceil($exact_age) : undef;
}

# Returns 1 if the event will occur in X days (X is a param)
sub will_occur
{
    my $self = shift;
    my $modifier = shift;
    
    # Apply the modifier to the event date
    my $modified_date = $self->get_original_date();
    return 0 unless $modified_date;

    if ($modifier) {
        $modified_date = DateCalc($modified_date, " - $modifier days");
    }
    return 0 unless $modified_date;
    
    my $current_occurence_date = ParseDate(UnixDate($modified_date, "$current_year-\%m-\%d"));
    if (Date_Cmp($current_date, $current_occurence_date) == 0) {
        return 1;
    } else {
        # If an event is scheduled on Feb. 29th, remind on the 28th
        if (UnixDate($modified_date, "\%m-\%d") eq "02-29" and 
            UnixDate($current_date, "\%m-\%d") eq "02-28") {
            return 1;
        } else {
            return 0;
        }
    }
}

1;
