#!/usr/bin/perl

use strict;
use warnings;

use POSIX;
use HTTP::DAV;
use Data::ICal::DateTime;
use Class::Inspector;

if ( $#ARGV  != 1 ) {
    print "This script must be run with 2 arguments\n";
    print "./Accept-ben.pl uid cn\n";
    exit
}



my $isconflict;
my $resource    = $ARGV[0];
my $resource_cn    = $ARGV[1];
my $d       = HTTP::DAV->new();
my $tmp_dir     = "/tmp/cal_$resource";




##Make temp directory to store ICS files if it doesn't already exist
if (!-d $tmp_dir)
{
    mkdir $tmp_dir;
}


sub auth
{
    my $url = shift;
    $d->credentials(
                    -user  => "nothing",
                    -pass  => "crypt",
                    -url   => $url,
                    -realm => "SOGo"
                   );
    $d->open(-url => $url);
}


sub pstat
{
    my $ev  = shift;
    my $att = scalar @{$ev->properties()->{attendee}} - 1;
    my $cn;
    my $loc;
    foreach my $i (0 .. $att)
    {
        $cn = $ev->properties()->{attendee}->[$i]->parameters->{CN};
        if ($cn eq "$resource_cn")
        {
            $loc = $i;
        }
    }
    return $ev->properties()->{'attendee'}->[$loc]->parameters()->{'PARTSTAT'};

}


sub set_pstat
{
    my ($ev, $val) = @_;

    my $att = scalar @{$ev->properties()->{attendee}} - 1;
    my $cn;
    my $loc;
    foreach my $i (0 .. $att)
    {
        $cn = $ev->properties()->{attendee}->[$i]->parameters->{CN};
        if ($cn eq "$resource")
        {
            $loc = $i;
        }
    }
    $ev->properties()->{'attendee'}->[$loc]->parameters()->{'PARTSTAT'} = $val;

}

sub is_infinite
{
    my $ev = shift;
    if (my $ev_r = $ev->recurrence)
    {
        if (!defined $ev_r->count)
        {
            return 1;
        }
    }
}





sub events_conflict
{
    my ($e1, $e2) = @_;
    my $e1_span;
    my $e2_span;
    
    if (!$e1->recurrence and !$e2->recurrence)
    {
        $e1_span = DateTime::Span->from_datetimes(start => $e1->start, end => $e1->end);
        $e2_span = DateTime::Span->from_datetimes(start => $e2->start, end => $e2->end);

        return 1 if (    $e1_span->intersects($e2_span) and $e1->end   ne $e2->start and $e1->start ne $e2->end);
    } elsif ($e1->recurrence)
    {
        my $ev_span   = $e1->recurrence;
        my $iter      = $ev_span->iterator;


        ##Hack -- only looks for conflicts over 2 years -- should look forever if
        ## finite repeating event and I'm not sure how to handle infinite repeats.
        ##This is horribly inefficient as if you have 100 events in 2 years
        ##Then it has to make calculation 100^2 times if there are only 2 of these repeating events.
        my $date1 = DateTime->new( year => 2011, month => 1, day => 1 );
        my $date2 = DateTime->new( year => 2013, month => 1, day => 1 );
        my $itspan = DateTime::Span->from_datetimes( start => $date1, end => $date2 );
        
        my @evts  = $e1->explode($itspan);
        foreach my $e (@evts)
        {
            #print "Exploded event: " . $e->start . "\t" . $e2->start . "\n";
            return 1 if events_conflict($e2, $e);
        }

    } elsif ($e2->recurrence)
    {
        return events_conflict($e2, $e1);
    }
}







##This is the main part. Grab the entire ICS calendar. And save it to the disk
my $url = "http://localhost/SOGo/dav/$resource/Calendar/personal";
auth($url);
$d->get(-url => "$url.ics", to => "$tmp_dir/ev.ics");
#Load the ICS file just saved into $ev_cal as an ICal structure
my $ev_cal = Data::ICal->new(filename => "$tmp_dir/ev.ics");


##Questions:
##What is cancel_val for? When I delete an event
##it goes away from the calendar... when does this come up?

my @evs = $ev_cal->events();
my @evcopy=@evs;
foreach my $e (@evs)    {
    #print "Event " . $e->uid . " has status of " . pstat($e) . "\n";
    $isconflict=0;
    if (pstat($e) eq 'NEEDS-ACTION')
    {
        #print "event " . $i->uid . " needs action\n";      

        #Check if the event is infinite, if so reject.
        ##I want to allow infinite events, so I'll comment this part out.
        #if (is_infinite($e))
        #{
        #    print " Infinite Event, will reject \n";
        #    set_pstat($e, 'DECLINED');
        #}


        ##Is there a conflict? Go through every Event that has been accepted and 
        ##see if it conflicts with the new event.
        my $dump=0;
        foreach my $a (@evcopy) {
            ##When checking for conflicts, reject conflicts between NEEDS-ACTIONS events 
            ##as well as accepted events.
            if ( ( pstat($a) eq 'ACCEPTED' or pstat($a) eq 'NEEDS-ACTION' ) and ( $e->uid ne $a->uid ) ) {
                $isconflict=events_conflict($a,$e);
                if ($isconflict ) {
                    #print "Event Conflict Found\n";
                    last;
                } 
                
            }
        }
        my $euid = $e->uid;
        #print "UID is: " . $euid . "\n";
        auth($url);
        $d->get(-url => "$url/$euid.ics", to => "$tmp_dir/ev-single.ics");    
        #$d->get(-url => "$url.ics", to => "$tmp_dir/ev-single.ics");
        #print "$url/$euid.ics\n";
        my $ev_cal_single = Data::ICal->new(filename => "$tmp_dir/ev-single.ics");
        my @ev_single = $ev_cal_single->events();
        #print "How many events is: " . $#ev_single . "\n" ;
        #print @ev_single->as_string;
        my $e_s = $ev_single[0];
        if ($isconflict) {
            set_pstat($e_s, 'DECLINED');
        } else {
            set_pstat($e_s, 'ACCEPTED');
        }
    
        
        #print "New Part status is: " . pstat($e_s) . "\n";
        #my $testcal = Data::ICal->new();
        #$testcal=$e_s;
        if (-f "$tmp_dir/ev-single.ics")
        {
            unlink "$tmp_dir/ev-single.ics";
        }
        open(my $afh, '>', "$tmp_dir/ev-single.ics");
        print $afh $ev_cal_single->as_string;
        close($afh);
        $d->put(-local => "$tmp_dir/ev-single.ics", -url => "$url/$euid.ics");
    }
}
        
