##!/usr/bin/perlml
#!/usr/bin/perl

# By Mathias Moe (mathias@jpg.no) for Fr. James Smith, march 2019
# use lib "/home/stfrancisdesales/Data/ICal/Entry/Event/Event.pm";
use Data::ICal;
use Data::ICal::Entry;
use Data::ICal::Entry::Event;
use Data::ICal::TimeZone;

use DateTime;
use DateTime::Format::ICal;
use JSON;
use Storable qw/ store retrieve /;

use Data::Dumper;

# Set files and sources here

my $infile  = "/home/apps1/htdocs/mathias/CalTest/sfds.ics";
my $outfile = "/home/apps1/htdocs/mathias/CalTest/sfds.ics";
my $soursesfile = "/home/apps1/htdocs/mathias/CalTest/sources.data";
my $source = "http://www.rotundasoftware.com/ministry/FSSPATL/schedules/?pointer=last";

my %old = ();
my %evts = ();

print "Starting v.13\n\n";

# my $today = DateTime->now(time_zone => "local")->ymd('');
my $today = DateTime->now(time_zone => 'America/New_York')->ymd('');



# Instanciate the calendar
my $calendar = Data::ICal->new(calname => "SFDS");

# Timezone
my $zone = Data::ICal::TimeZone->new( timezone => 'America/New_York' );

# Read the cache of sources
#my $sources_ref = retrieve "$soursesfile" || \%();

# Storable file needs to exist and be owned by correct user
my %empty = (
    "https://secure.rotundasoftware.com/l/web-terminal/master-calendar?liveWebPostId=454383&id=454383&user=FSSPATL" => { "exists" => 1 },
    "https://secure.rotundasoftware.com/l/web-terminal/master-calendar?liveWebPostId=469307" => { "exists" => 1 }
    );
if (! -e "$soursesfile") {
    store \%empty, "$soursesfile";
}



my $sources_ref;
my $sources_ref = retrieve "$soursesfile";
my %sources = %{$sources_ref};

my $trueSource = `curl -Ls -o /dev/null -w %{url_effective} $source`;

print "Found trueSource: $trueSource\n";

$sources{$trueSource}{exists} = 1;

print "Sources:\n";
print Dumper %sources;

# Loop trough all sources, add events, delete sources that contains no current events

foreach $s (keys(%sources)) {
    my $stillCurrent = 0; # Does this url contain current events

    print "Key $s\n";
    # Data source
    my $data = `curl -s -L "$s"`;

    # Clean up before and after real data -- this is the OLD format
    if ($data =~ m/window\.Baseline\.bootstrapData/) {
	print "OLD format\n";
	$data =~ s/^.*window\.Baseline\.bootstrapData =//s;
	$data =~ s/^(.*?)\;window\.Baseline.*$/$1/s;
    }
    elsif ($data =~ m/window\.App\.bootstrapBoat/) {
	print "NEW format\n";
	$data =~ s/^.*window\.App\.bootstrapBoat = //s;
	$data =~ s/^(.*?);\s*<\/script>.*$/$1/s;
    }
    else {
	print "UNKNOWN format:\n $data\b";
	$data = "{}";
    }

    my $newStartsAt;
    my $calendardata = decode_json($data);
    my @masses = @{$calendardata->{ms}};

    foreach $mass (@masses) {

	# Skip invisible services
	next unless $mass->{serviceId};

	# Date-time is ALMOST ISO-8601
	my $datetime = $mass->{dateTime};
	$datetime =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})\:(\d{2})\:(\d{2})/;
	$dt = DateTime->new(year => $1, month => $2, day => $3, hour => $4, minute => $5, second => $6, nanosecond => 0);

	$end = DateTime->new(year => $1, month => $2, day => $3, hour => $4, minute => $5, second => $6, nanosecond => 0);

	# Used in creating link below
	my $lithurgicaldate = "$1-$2-$3";

	# early exit if event is in the past
	if (int("$1$2$3") < int($today)) { print "Skipping $1$2$3"; next; }

	#   print "Timecheck $1$2$3 >= $today\n";

	$stillCurrent = 1;

	# If name is set, use this else use label
	my $summary = (($mass->{name} =~ m/\w/) ? $mass->{name} : $mass->{label});

	# Add link to Liturgical Year Project
	# $summary .= qq!<a href="https://fsspatl.com/liturgical-year?tmpl=component&print=1&field1=$lithurgicaldate&gsearch=1&moduleId=127&Itemid=165">Read Dom Prosper Guéranger\'s meditations on today\'s liturgy in the [Liturgical Year Project].</a>!;

	# Add hours and minutes to get end
	$end->add( hours => $mass->{duration}->{hours}, minutes => $mass->{duration}->{minutes} );

	my $ts = DateTime->now;
	#       my $uid = $mass->{_id};
	my $uid = $mass->{id};
	$evts{$uid}{dtstart} = DateTime::Format::ICal->format_datetime($dt);
	$evts{$uid}{dtend}   = DateTime::Format::ICal->format_datetime($end);
	$evts{$uid}{dtstamp} = DateTime::Format::ICal->format_datetime($ts);
	$evts{$uid}{summary} = $summary;

	print "Event: $evts{$uid}{dtstart} $evts{$uid}{summary}\n";
    } # end foreach @masses

    if (! $stillCurrent) { print "$s has been deleted as it contains no current events\n". delete ($sources{$s}) }
    else { print "$s is still current\n" }
} # end foreach $s


foreach $e (sort( keys %evts)) {
    my $vevent = Data::ICal::Entry::Event->new();

    $vevent->add_properties(
	summary => $evts{$e}{summary},
	description => "",
	# Dat*e*::ICal is not a typo here
	dtstart   => [$evts{$e}{dtstart},{ TZID => $zone->timezone }],
	dtend     => [$evts{$e}{dtend},{ TZID => $zone->timezone }],
	dtstamp => $evts{$e}{dtstamp},
	uid        => $e
	);
    $calendar->add_entry($vevent);
    #print "Adding: \n";
    #print Dumper $vevent;
    #print "\n----\n";
}



open (FH, '>', "$outfile") or die $!;
print FH $calendar->as_string;
close FH;

# Store the (updated) sources-list
print "Storing sources\n";
store \%sources, "$soursesfile";


# print Dumper %old;
# print "\n---\n";
# print $calendar->as_string;

#############################################
# The library Data::ICal::Entry::Event
# is included here because it could not be
# installed with cPanel and using "use lib"
# don't seem to work. If this script is moved
# to a sane environment, it should probably
# use the library provided by the system instead
#############################################

package Data::ICal::Entry::Event;

use base qw/Data::ICal::Entry/;

=head1 NAME

Data::ICal::Entry::Event - Represents an event in an iCalendar file

=head1 SYNOPSIS

    my $vevent = Data::ICal::Entry::Event->new();
    $vevent->add_properties(
        summary => "my party",
        description => "I'll cry if I want to",
        # Dat*e*::ICal is not a typo here
        dtstart   => Date::ICal->new( epoch => time )->ical,
    );

    $calendar->add_entry($vevent);

    $vevent->add_entry($alarm);

=head1 DESCRIPTION

A L<Data::ICal::Entry::Event> object represents a single event in an
iCalendar file.  (Note that the iCalendar RFC refers to entries as
"components".)  It is a subclass of L<Data::ICal::Entry> and accepts
all of its methods.

=head1 METHODS

    =cut
    
=head2 ical_entry_type

Returns C<VEVENT>, its iCalendar entry name.

    =cut
    
sub ical_entry_type {'VEVENT'}

=head2 mandatory_unique_properties

The C<uid> property is mandatory if C<rfc_strict> was passed to
L<Data::ICal/new>.

    =cut
    
sub mandatory_unique_properties {
    my $self = shift;
    return $self->rfc_strict ? ("uid") : ()
}

=head2 optional_unique_properties

According to the iCalendar standard, the following properties may be
specified at most one time for an event:

        class  created  description  dtstart  geo
        last-modified  location  organizer  priority
        dtstamp  sequence  status  summary  transp
        uid  url  recurrence-id

In addition, C<dtend> and C<duration> may be specified at most once
each, but not both in the same entry (though this restriction is not
enforced).

Or if C<< vcal10 => 1 >>:

        class dcreated completed description dtstart dtend
        last-modified location rnum priority
        sequence status summary transp
        url uid

    =cut
    
sub optional_unique_properties {
    my $self = shift;
    my @ret = $self->rfc_strict ? () : ("uid");
    if (not $self->vcal10) {
        push @ret, qw(
            class  created  description  dtstart  geo
            last-modified  location  organizer  priority
            dtstamp  sequence  status  summary  transp
            url  recurrence-id

            dtend duration
        );
    } else {
        push @ret, qw(
            class dcreated completed description dtstart dtend
            last-modified location rnum priority
            sequence status summary transp
            url
        );
    }
    return @ret;
}

=head2 optional_repeatable_properties

According to the iCalendar standard, the following properties may be
specified any number of times for an event:

        attach  attendee  categories  comment
        contact  exdate  exrule  request-status  related-to
        resources  rdate  rrule

Or if C<< vcal10 => 1 >>:

        aalarm  attach  attendee  categories
        dalarm  exdate  exrule  malarm  palarm  related-to
        resources  rdate  rrule

    =cut
    
sub optional_repeatable_properties {
    my $self = shift;
    if (not $self->vcal10) {
        qw(
            attach  attendee  categories  comment
            contact  exdate  exrule  request-status  related-to
            resources  rdate  rrule
        );
    } else {
        qw(
            aalarm  attach  attendee  categories
            dalarm  exdate  exrule  malarm  palarm  related-to
            resources  rdate  rrule
        );
    }
}

=head1 SEE ALSO

=over 4

=item L<Data::ICal::DateTime>

For date parsing and formatting, including denoting "all day" events,
considering using this module. Because it's a "mix in", you can still
use all the methods here as well as the new date handling methods it
defines.

=back

=head1 AUTHOR

Best Practical Solutions, LLC E<lt>modules@bestpractical.comE<gt>

=head1 LICENCE AND COPYRIGHT

Copyright (c) 2005 - 2015, Best Practical Solutions, LLC.  All rights reserved.

This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself. See L<perlartistic>.

    =cut
    
1;
