#!/usr/bin/perl -w
#
# Author: Petter Reinholdtsen <pere@hungry.com>
# Date:   2002-01-10
#
# Convert plan calender data to/from icalender format.
#
# plan, <URL:http://www.bitrot.de/>
# icalendar, RFC 2445, <URL:http://www.faqs.org/rfcs/rfc2445.html">iCalendar>
#
# Depend on debian package libdate-ical-perl

use warnings;
use strict;

use Getopt::Std;
use Date::Parse;
use Date::Format;
use Date::ICal;
use iCal::Parser;
use Data::Dumper;

my @events;
my %opts;
unless (getopts('dif:o:', \%opts)) {
    usage();
    exit 1;
}

my $debug = $opts{d} || 0;
my $input  = $opts{f};
my $output = $opts{o};

if ($opts{i}) { # Import
    read_icalendar($input   || "test.ical");
    write_planfile($output  || $ENV{HOME}."/.plan.dir/fromical");
} else { # Default is to export
    read_planfile($input    || $ENV{HOME}."/.plan.dir/dayplan");
    write_icalendar($output || "-");
}
exit 0;

sub usage {
    print <<EOF
Usage: $0 [-i] [-f infile] [-o outfile]

Imports or exports between plan and iCalendar.

  -i          import ical file
  -f infile   read input from 'infile'
  -o outfile  read output from 'outfile'
  -d          enable debug output
EOF
}
sub read_planfile {
    my ($planfile) = @_;
    open(PLAN, "<$planfile") || die "Unable to read $planfile";
    my $planver = <PLAN>;
    chomp $planver;
    while (<PLAN>) {
        chomp;
        if (m%\d+/\d+/\d+\s%) {
            my ($date, $start, $duration) = split(/\s+/);
            my $info = <PLAN>;
            chomp $info;
            my ($dontknow, $msg) = $info =~ m/^(.)\t(.+)$/;
            print STDERR "E: $date $start $duration $msg\n" if $debug;

            my $timestamp = str2time("$date $start");
            my $isostartstamp  = time2str("%Y%m%dT%H%M%S", $timestamp);
            my $icalstartstamp = Date::ICal->new( epoch => $timestamp );

            my @f = split(/:/, $duration);
            $timestamp += ($f[0]*60*60 + $f[1]*60 +$f[2]);
            my $isoendstamp  = time2str("%Y%m%dT%H%M%S", $timestamp);
            my $icalendstamp = Date::ICal->new( epoch => $timestamp );

            print STDERR "IE: $isostartstamp $isoendstamp\n" if $debug;
            push(@events, {
                summary   => $msg,
                dtstart   => $icalstartstamp->ical,
                dtend     => $icalendstamp->ical,
                });
        }
    }
    close(PLAN);
}


# BEGIN:VCALENDAR
# PRODID
#  :-//test environment//NONSGML plan2icalendar//EN
# VERSION
#  :2.0
# BEGIN:VEVENT
# CREATED
#  :20020110T153939
# UID
#  :KOrganizer-141229514.660
# SEQUENCE
#  :0
# LAST-MODIFIED
#  :20020110T153939
# DTSTAMP
#  :20020110T153952
# SUMMARY
#  :SL-veiledning
# DTSTART
#  :20020114T151500
# DTEND
#  :20020114T160000
# END:VEVENT
# END:VCALENDAR
sub write_icalendar {
    my $filename = shift;
    open(PLAN, ">$filename") || die "Unable to write to $filename";
    print PLAN <<EOF;
BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//test environment//NONSGML plan2icalender//EN
VERSION:2.0
EOF
    my $count = 0;
    for my $event (@events) {
        $event->{uid} = "plan2icalendar-$count" unless $event->{uid};
        $event->{dtstamp} = "20020110T153952" unless $event->{dtstamp};
        $event->{'last-modified'} = "20020110T153939" unless $event->{'last-modified'};
        $event->{sequence} = $count unless $event->{sequence};
        $event->{created} = "20020110T153939" unless $event->{created};
        print PLAN <<EOF;
BEGIN:VEVENT
CREATED:$event->{created}
UID:$event->{uid}
SEQUENCE:$count
LAST-MODIFIED:$event->{'last-modified'}
DTSTAMP:$event->{dtstamp}
SUMMARY:$event->{summary}
DTSTART:$event->{dtstart}
DTEND:$event->{dtend}
END:VEVENT

EOF
        $count++;
    }
    print PLAN "END:VCALENDAR\n";
    close PLAN;
}

#
# Should probably leave this task to Net::ICal.  But it is not
# available in debian/sarge, so I make a quick-n-dirty solution
# instead.
#
sub read_icalendar {
    my $filename = shift;

    if (1) {
        my $parser = iCal::Parser->new();
        my $hash = $parser->parse($filename);
#        print Dumper($hash) if $debug;
#        print Dumper($hash->{'events'});
        for my $year (sort keys %{$hash->{'events'}}) {
            for my $month (sort keys %{$hash->{'events'}->{$year}}) {
                for my $day (sort keys %{$hash->{'events'}->{$year}->{$month}}) {
                    for my $evid (keys %{$hash->{'events'}->{$year}->{$month}->{$day}}) {
                        my $event =
                            $hash->{'events'}->{$year}->{$month}->{$day}->{$evid};
#                        print "$year-$month-$day $evid\n";
#                        print Dumper($event);

                        my %newevent;
                        $newevent{description} = $event->{'DESCRIPTION'};
                        $newevent{created} = $event->{'CREATED'};
                        $newevent{dtend} = $event->{'DTEND'};
                        $newevent{dtstamp} = $event->{'DTSTAMP'};
                        $newevent{dtstart} = $event->{'DTSTART'};
                        $newevent{'last-modified'} = $event->{'LAST-MODIFIED'};
                        $newevent{sequence} = $event->{'SEQUENCE'};
                        $newevent{summary} = $event->{'SUMMARY'};
                        $newevent{uid} = $event->{'UID'};
#                        print Dumper(%newevent);
                        push(@events, \%newevent);
                        print STDERR "Event pushed\n" if $debug;
                    }
                }
            }
        }
    } else {
    open (ICALENDAR, "<$filename") or die "Unable to read from $filename";
    my $oldval = $/;
    $/ = "\r\n";
    print STDERR "Loading $filename\n" if $debug;
    while (<ICALENDAR>) {
        chomp;
        if (m/^BEGIN:VEVENT/) {
            my %event;
            while (<ICALENDAR>) {
                chomp;
                last if (m/END:VEVENT/);
                $event{description} = $1 if (m/^DESCRIPTION\s*:\s*(.+)$/);
                $event{created} = $1 if (m/^CREATED\s*:\s*(.+)$/);
                $event{dtend} = $1 if (m/^DTEND\s*:\s*(.+)$/);
                $event{dtstamp} = $1 if (m/^DTSTAMP\s*:\s*(.+)$/);
                $event{dtstart} = $1 if (m/^DTSTART\s*:\s*(.+)$/);
                $event{last-modified} = $1 if (m/^LAST-MODIFIED\s*:\s*(.+)$/);
                $event{sequence} = $1 if (m/^SEQUENCE\s*:\s*(.+)$/);
                $event{summary} = $1 if (m/^SUMMARY\s*:\s*(.+)$/);
                $event{uid} = $1 if (m/^UID\s*:\s*(.+)$/);
            }
            push @events, \%event;
            print STDERR "Event pushed\n" if $debug;
        }
    }
    close (ICALENDAR);
    $/ = $oldval;
    }
}

# Simple version covering the chars I need.
sub utf8tolocalmap {
    my $string = shift;
    $string =~ s/Ã¸/ø/g;
    $string =~ s/Ã¦/æ/g;
    $string =~ s/Ã¥/å/g;
    $string =~ s/Ã…/Å/g;
    $string =~ s/Â«/«/g;
    $string =~ s/Â»/»/g;
    return $string;
}

sub write_planfile {
    my $filename = shift;
    open(PLAN, ">$filename") or die "Unable to write to $filename";
    for my $event (@events) {
        # offset = 0 -- assume all timestamps in local time zone
        my $ical;
        if ("DateTime" eq ref $event->{dtstart}) {
            $ical = Date::ICal->new( epoch => $event->{dtstart}->epoch );
        } else {
            $ical = Date::ICal->new( ical => $event->{dtstart}, offset => 0 );
        }
        my $date = join("/", $ical->month, $ical->day, $ical->year);
        my $start = join(":", $ical->hour, $ical->min, $ical->sec);

        my $icalstop;
        if ("DateTime" eq ref $event->{dtend}) {
            $icalstop = Date::ICal->new( epoch => $event->{dtend}->epoch );
        } else {
            $icalstop = Date::ICal->new( ical => $event->{dtend}, offset => 0 );
        }
        my $icalduration = $icalstop - $ical;
        my $duration = join(":",
                            $icalduration->hours   || "0",
                            $icalduration->minutes || "0",
                            $icalduration->seconds || "0");

        my $code = "N";
        my $summary = utf8tolocalmap($event->{summary});
        print PLAN "$date  $start  $duration  0:0:0  0:0:0  ---------- 0 0\n";
        print PLAN "$code\t$summary\n";
    }
}
