# 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::EventList;

# Holds all user information and events.
#
# Events are stored in the proper EventStore and they can be accessed
# using this class.  The main XML parsing and generation happen here.

use strict;
use warnings;

use Email::Valid;
use XML::DOM;

use EmailReminder::AnniversaryEvent;
use EmailReminder::AnniversaryStore;
use EmailReminder::BirthdayEvent;
use EmailReminder::BirthdayStore;
use EmailReminder::MonthlyEvent;
use EmailReminder::MonthlyStore;
use EmailReminder::Utils;
use EmailReminder::WeeklyEvent;
use EmailReminder::WeeklyStore;
use EmailReminder::YearlyEvent;
use EmailReminder::YearlyStore;

# XML tags, attributes and values

my $EMAIL_TAG       = 'email';
my $EVENT_TAG       = 'event';
my $EVENTS_TAG      = 'events';
my $FIRST_NAME_TAG  = 'first_name';
my $LAST_NAME_TAG   = 'last_name';
my $USER_TAG        = 'email-reminder_user';

my $TYPE_ATTR       = 'type';

sub new
{
    my ($class, $filename, $create) = @_;
    
    my $self = { "NEXT_EVENT_ID" => 0,
                 "LOADED_FILENAME" => $filename,
                 "XML_DOC" => undef,
                 "EVENTS_NODE" => undef,
                 "STORES" => {},
             };

    bless $self, $class;
    
    $self->process_file($create);
    
    return $self;
}

sub process_file
{
    my ($self, $create, $readonly) = @_;

    # Make sure the config file exists and is readable
    my $filename = $self->{LOADED_FILENAME};
    unless (-e $filename) {
        die "File '$filename' does not exist.\n";
    }
    unless (-r $filename) {
        die "File '$filename' exists but is not readable.\n";
    }
    if (!$readonly && !(-w $filename)) {
        warn "WARNING: File '$filename' is not writable, your changes will be lost!\n";
    }

    # Start parsing the XML file
    my $parser = XML::DOM::Parser->new();
    my $doc;
    eval {
        $doc = $parser->parsefile($filename);
    };
    unless (defined($doc)) {
	die "File '$filename' is an invalid XML file. Fix it or delete it.\n";
    }
    $self->{XML_DOC} = $doc;

    # Read user info
    my $user = $doc->getElementsByTagName($USER_TAG)->item(0);   
    unless (defined($user)) {
	die "File '$filename' is an invalid XML file. Fix it or delete it.\n";
    }
    $self->{USER_NODE} = $user;

    # Read events
    my $events = $doc->getElementsByTagName($EVENTS_TAG)->item(0);
    return unless defined($events);
    $self->{EVENTS_NODE} = $events;

    foreach my $event_node ($events->getElementsByTagName($EVENT_TAG)) {
	my $type = $event_node->getAttribute($TYPE_ATTR);
	my $event = $self->create_event($type, $event_node);
        next unless defined($event);

        # Add to proper EventStore
        my $store = $self->get_model($type);
        $store->add_event($event);
    }
    return 1;
}

sub create_event
{
    my ($self, $type, $event_node) = @_;

    if (!defined($event_node))
    {
        $event_node = $self->{XML_DOC}->createElement($EVENT_TAG);
        $event_node->setAttribute($TYPE_ATTR, $type);
        
        my $events = $self->{EVENTS_NODE};
        unless (defined($events))
        {
            $events = $self->{XML_DOC}->createElement($EVENTS_TAG);
            $self->{USER_NODE}->appendChild($events);
            $self->{EVENTS_NODE} = $events;
        }
        $events->appendChild($event_node);
    }

    my $event;
    my $id = $self->{NEXT_EVENT_ID}++;

    if ($type eq EmailReminder::BirthdayEvent->get_type()) {
        $event = EmailReminder::BirthdayEvent->new($event_node, $id);
    } 
    elsif ($type eq EmailReminder::AnniversaryEvent->get_type()) {
        $event = EmailReminder::AnniversaryEvent->new($event_node, $id);
    } 
    elsif ($type eq EmailReminder::MonthlyEvent->get_type()) {
        $event = EmailReminder::MonthlyEvent->new($event_node, $id);
    }
    elsif ($type eq EmailReminder::WeeklyEvent->get_type()) {
        $event = EmailReminder::WeeklyEvent->new($event_node, $id);
    }
    elsif ($type eq EmailReminder::YearlyEvent->get_type()) {
        $event = EmailReminder::YearlyEvent->new($event_node, $id);
    }

    return $event;
}

sub get_model
{
    my ($self, $type) = @_;

    my $store = $self->{STORES}->{$type};
    unless (defined($store)) {
        if ($type eq EmailReminder::AnniversaryEvent->get_type()) {
            $store = EmailReminder::AnniversaryStore->new();
        } 
        elsif ($type eq EmailReminder::BirthdayEvent->get_type()) {
            $store = EmailReminder::BirthdayStore->new();
        } 
        elsif ($type eq EmailReminder::MonthlyEvent->get_type()) {
            $store = EmailReminder::MonthlyStore->new();
        }
        elsif ($type eq EmailReminder::WeeklyEvent->get_type()) {
            $store = EmailReminder::WeeklyStore->new();
        }
        elsif ($type eq EmailReminder::YearlyEvent->get_type()) {
            $store = EmailReminder::YearlyStore->new();
        }

        $store->init();
        $self->{STORES}->{$type} = $store;
    }

    return $store;
}

sub get_events
{
    my $self = shift;

    my @events = ();
    foreach my $store (values(%{$self->{STORES}})) {
        push(@events, @{$store->get_events()});
    }

    return @events;
}

# View/edit user properties

sub _get_user_fname {
    my $self = shift;
    return EmailReminder::Utils::get_node_value($self->{USER_NODE}, $FIRST_NAME_TAG) || '';
}

sub _get_user_lname {
    my $self = shift;
    return EmailReminder::Utils::get_node_value($self->{USER_NODE}, $LAST_NAME_TAG) || '';
}

sub get_user_name
{
    my $self = shift;

    my $fname = $self->_get_user_fname;
    my $lname = $self->_get_user_lname;

    if (!$fname && !$lname) {
        # Get name from UNIX password file
        my @pwinfo = getpwuid($>);
        my $fullname = $pwinfo[6]; 
        $fullname =~ s/[^0-9A-Za-z_\- ]//g;
        my @name_parts = split(/ /, $fullname);
        
        $fname = $name_parts[0];
        $lname = $name_parts[-1] if @name_parts > 1;
    } elsif (!$fname) {
        $fname = $lname;
        $lname = '';
    }

    return ($fname, $lname);
}

sub get_user_email
{
    my $self = shift;
    return EmailReminder::Utils::get_node_value($self->{USER_NODE}, $EMAIL_TAG) || "";
}

1;
