#!/usr/bin/perl -w

# Jerusalem cinematheque movie extractor, version 2.8
# http://www.cs.huji.ac.il/~irush/projects/cinematheque.html
# Written by Yair Yarom <irush@cs.huji.ac.il>

my $VERSION = "2.8";


BEGIN { 
    # Can't have -C8 in #! line anymore...
    binmode STDOUT, ":utf8";
}

use Date::ICal;
use HTML::Entities;
use warnings;
use strict;
use utf8;
use encoding "utf8";
use Encode;
use LWP::Simple;

# event hash:
# hour     (s)
# minute   (s)
# day      (s)
# month    (s)
# year     (s)
# link     (s)
# location (s)
# name     (s + e)
# director
# cast
# country
# movie_year
# duration
# language
# subtitles
# name_en
# description_en
# comments
# price
# trailer
# screening

# todo
# 2 in one
# with picture
# with extra notes
# better remove spaces from hash
# comments in collection

binmode STDOUT, ":utf8";

my $endl = "\r\n";
my @events;
my @dates;
my $verbose = 0;
my $output = "Cinematheque.ics";
my $input = "http://www.jer-cin.org.il/website/modules/films/Monthly.aspx";
my @excludeHalls = ();
my @includeHalls = ();
my %monthCache = ();
my %eventCache = ();

# remove wired and problematic entities
my %entity2char = %HTML::Entities::entity2char;
$entity2char{"lsquo;"} = "'";

sub usage {
    print "usage: $0 [-d|--day day(s)] [-m|--month month] [-y|--year year] [day(s)/month/year] [--verbose] [-i input] [-o output] [-(hall(s))] [+(hall(s))\n";
    print "    day: <num>[..<num>],   <num>: 1..31\n";
    print "  month: <num>,            <num>: 1..12\n";
    print "   year: <num>,            <num>: \\d{2} | \\d{4}\n";
    print "  input: input html file (Monthly.aspx), as this script can't change months in the site...\n";
    print " output: output ics file, defaults to Cinematheque.ics\n";
    print "   hall: \\d. exclude/include these halls\n";
    exit 1;
}

# builds the description from the event
sub buildDescription {
    my $event = shift;
    my $description = "";
    $description .= "$$event{director}\n" if ($$event{director});
    if ($$event{country}) {
        $description .= "$$event{country}" ;
        $description .= ", $$event{movie_year}" if ($$event{movie_year});
        $description .= "\n";
    } else {
        $description .= "$$event{movie_year}\n" if ($$event{movie_year});
    }
    $description .= "$$event{duration} דקות\n";
    $description .= "$$event{language}" if ($$event{language});
    $description .= " ($$event{subtitles})" if ($$event{subtitles});
    $description .= "דיבוב לעברית\n" if ($$event{"dub"});
    $description .= "\n" if ($$event{language} or $$event{subtitles});
    $description .= "הקרנת תלת מימד\n" if ($$event{"3d"});
    $description .= "משחק: $$event{cast}\n" if ($$event{cast});
    $description .= "$$event{price}\n" if ($$event{price});
    $description .= "\n$$event{comments}\n" if ($$event{comments});
    $description .= "\n\n$$event{description}" if ($$event{description});
    $description .= "\n\n$$event{description_en}" if ($$event{description_en});
    $description .= "\n\nהקרנות:\n$$event{screening}" if ($$event{screening});
    $description .= "\n\n$$event{link}" if ($$event{link});
    $description .= "\n\n$$event{trailer}" if ($$event{trailer});
    return $description;
}

# prints the calendar (@events) into $output
sub print_cal {
    my $TEMPFILE;
    my $temp = "/tmp/cinematheque.$$";
    open TEMPFILE, ">:utf8", "$temp";

    print TEMPFILE "BEGIN:VCALENDAR".$endl;
    print TEMPFILE "CALSCALE:GREGORIAN".$endl;
    print TEMPFILE "PRODID:-//human irush//Cinematheque Movies Extractor $VERSION//".$endl;
    print TEMPFILE "VERSION:2.0".$endl;

    foreach my $event (@events) {


        print TEMPFILE "BEGIN:VEVENT".$endl;
        my $date = Date::ICal->new(year => $$event{year}, month => $$event{month}, day => $$event{day}, hour => $$event{hour}, min => $$event{minute});
        my $dtstart = $date->Date::ICal::ical(localtime => 1);
        #print TEMPFILE "DTSTART;TZID=Asia/Jerusalem:".$dtstart.$endl;
        print TEMPFILE "DTSTART:".$dtstart.$endl;
        print TEMPFILE "DURATION:PT".$$event{duration}."M".$endl;
        print TEMPFILE "LOCATION:".$$event{location}.$endl if $$event{location};
        print TEMPFILE "SUMMARY:".$$event{name};
        print TEMPFILE " - 3d" if $$event{"3d"};
        print TEMPFILE " (".$$event{name_en}.")" if $$event{name_en};
        print TEMPFILE $endl;
        my $description = "DESCRIPTION:".buildDescription($event);
        
        $description =~ s/^\s+$//mg;
        $description =~ s/\n\n\n+/\n\n/gs;
        $description =~ s/\n/\\n/gs;
        $description =~ s/(.{72})/$1\n /gs;

        print TEMPFILE "$description".$endl;
        print TEMPFILE "URL:".$$event{link}.$endl;
        print TEMPFILE $endl."END:VEVENT".$endl;
    }

    print TEMPFILE $endl."END:VCALENDAR".$endl;
    close TEMPFILE;

    rename $temp, $output;
};

# checks value of first hash the same as second hash, or deletes it
sub mergeValue {
    (my $first, my $second, my $value) = @_;
    if (defined $first->{$value}) {
        $first->{$value} = "" if not defined $second->{$value} or $first->{$value} ne $second->{$value};
    } else {
        if (defined $second->{$value}) {
            $first->{$value} = $second->{$value};
        } else {
            $first->{$value} = "";
        }
    }
}

sub cacheCollection {
    my $event = shift;
    my %event = %$event;
    my $link = $event{link};

    my $data = `curl -s "$link"`;

    # comments
    $event{comments} = "" unless $event{comments};
    (my $comments) = ($data =~ m|class="showDetailsBoxBig_recTxt">(.*?)</td>|s);
    if ($comments) {
        $comments =~ s/<[^>]*>//gs;
        $comments =~ s/\s+/ /gs;
        $event{comments} .= "\n$comments" if $comments;
        $event{comments} =~ s/^\s*(.*?)\s*$/$1/;
    }
    _decode_entities($event{comments}, \%entity2char, 1);
    delete $event{comments} unless $event{comments};

    # name
    (my $collection, $event{name}) = ($data =~ m|class="filmShow">(.*?)</div>.*class="filmName">(.*?)</div>|s);
    $event{name} = "$collection: $event{name}";
    $event{name} =~ s/\s+/ /g;
    $event{name} =~ s/^\s*(.*?)\s*$/$1/;

    
    $event{description} = "";
    $event{duration} = 1;
    my @subEvents = ($data =~ m|<div class="recBoxMovie">(.*?)</div>|sg);
    #@subEvents = ($subEvents[10]); # debug
    foreach my $subevent (@subEvents) {
        $subevent =~ s/.*?href="(.*?)".*/$1/s;
        (my $sublink) = ($link =~ m|^(.*)/[^/]+$|);
        $sublink .= "/$subevent";
        my %subevent = (link => $sublink);
        fillCachedEvent(\%subevent);
        $event{description} .= "\n\n$subevent{name}\n".buildDescription(\%subevent);
        $event{duration} += $subevent{duration};
        mergeValue(\%event, \%subevent, "country");
        mergeValue(\%event, \%subevent, "movie_year");
        mergeValue(\%event, \%subevent, "director");
        mergeValue(\%event, \%subevent, "language");
        mergeValue(\%event, \%subevent, "subtitles");
        mergeValue(\%event, \%subevent, "cast");
    }
    $event{description} =~ s/^\s+//;
    $event{description} =~ s/^\s+//;

    # english
    $data = `curl -s "$link&Lang=En"`;


    # name_en
    my $nameenFromShow = 0;
    ($event{name_en}) = ($data =~ m|class="filmName">(.*?)</div>|s);
    unless ($event{name_en}) {
        $nameenFromShow = 1;
        ($event{name_en}) = ($data =~ m|class="filmShow">(.*?)</div>|s);
    }
    if ($event{name_en}) {
        $event{name_en} =~ s/\s+/ /g;
        $event{name_en} =~ s/^\s*(.*?)\s*$/$1/;
    }

    # description_en
    $event{description_en} = "";

    foreach my $key (keys %event) {
        $event->{$key} = $event{$key};
    }
}

sub cacheFilm {
    my $event = shift;
    my %event = %$event;
    my $link = $event{link};

    # in case of pdf redirection...
    my $data = `curl -L -I -s "$link"`;

    if ($data =~ m/Content-Type: / and
        index((grep {index($_, "Content-Type: ") == 0} split /\n/, $data)[-1], "application/pdf") >= 0) {
        $event{name_en} = "";
        $event{duration} = 0;
        goto cacheFilmEnd;
    } 
    $data = decode('utf8', `curl -s "$link"`);
    
    # name
    my $nameFromShow = 0;
    ($event{name}) = ($data =~ m|class="filmName">(.*?)</div>|s);
    unless ($event{name}) {
        $nameFromShow = 1;
        ($event{name}) = ($data =~ m|class="filmShow">(.*?)</div>|s);
    }
    $event{name} =~ s/\s+/ /g;
    $event{name} =~ s/^\s*(.*?)\s*$/$1/;

    # comments
    ($event{comments}) = ($data =~ m|<div id="ctl00_ContentPlaceHolder1_filmProgram" class="filmProgram">(.*?)</div>|s);
    if ($event{comments}) {
        $event{comments} =~ s/<[^>]*>//gs;
        $event{comments} =~ s/\s+/ /g;
        $event{comments} =~ s/^\s*(.*?)\s*$/$1/;
    }

    $event{comments} = "" unless $event{comments};
    (my $comments) = ($data =~ m|class="showDetailsBoxBig_recTxt">(.*?)</td>|s);
    if ($comments) {
        #$comments =~ s/<p[^>]*>/\n/gs;
        $comments =~ s/<[^>]*>//gs;
        #$comments =~ s/[ \t\r]*\n+[ \t\r]*/\n/gs;
        $comments =~ s/[ \t\r]+/ /gs;
        $comments =~ s/\n[ \t\r]+\n//gs;
        $event{comments} .= "\n$comments" if $comments;
        $event{comments} =~ s/^\s*(.*?)\s*$/$1/;
    }

    unless ($nameFromShow) {
        ($comments) = ($data =~ m|class="filmShow">(.*?)</div>|s);
        if ($comments) {
            $comments =~ s/\s+/ /g;
            $comments =~ s/^\s*(.*?)\s*$/$1/;
            $event{comments} = "$comments\n\n$event{comments}" if $comments;
            $event{comments} =~ s/^\s*(.*?)\s*$/$1/;
        }
    }
    _decode_entities($event{comments}, \%entity2char, 1);
    delete $event{comments} unless $event{comments};

    # director
    ($event{director}) = ($data =~ m|class="filmDirectors">(.*?)</div>|s);
    $event{director} =~ s/\s+/ /g;
    $event{director} =~ s/^\s*(.*?)\s*$/$1/;

    # description
    ($event{description}) = ($data =~ m|class="recTxtBig">(.*?)</td>|s);
    if ($event{description}) {
        $event{description} =~ s|</?p\b[^>]*>||gs;
        _decode_entities($event{description}, \%entity2char, 1);
        if ($event{description} =~ m|<strong>(כרטיסים:.*?)</strong>|) {
            $event{price} = $1;
            $event{description} =~ s|<strong>כרטיסים:.*?</strong>||;
        }
        $event{description} =~ s|</?strong\b[^>]*>||gs;
        $event{description} =~ s|<span class="Apple-style-span".*?</span>||gs;
        $event{description} =~ s|</?div>||gs;
        $event{description} =~ s|<object.*?</object>||gs;
        $event{description} =~ s|<a href.*?</a>||gs;
        $event{description} =~ s,,,gs;
        $event{description} =~ s/^\s*(.*?)\s*$/$1/;
        $event{description} =~ s|<iframe.*?</iframe>||gs;
    }

    # cast
    ($event{cast}) = ($data =~ m|<td id="ctl00_ContentPlaceHolder1_filmCast_txt" class="recInfo">(.*?)</td>|s);
    if ($event{cast}) {
        $event{cast} =~ s/\s+/ /g;
        $event{cast} =~ s/^\s*(.*?)\s*$/$1/;
    }

    # country
    ($event{country}) = ($data =~ m|<td id="ctl00_ContentPlaceHolder1_filmCountry_txt" class="recInfo">(.*?)</td>|s);
    if ($event{country}) {
        $event{country} =~ s/\s+/ /g;
        $event{country} =~ s/^\s*(.*?)\s*$/$1/;
    }

    # movie_year
    ($event{movie_year}) = ($data =~ m|<td id="ctl00_ContentPlaceHolder1_filmYear_txt" class="recInfo">(.*?)</td>|s);
    if ($event{movie_year}) {
        $event{movie_year} =~ s/\s+/ /g;
        $event{movie_year} =~ s/^\s*(.*?)\s*$/$1/;
    }

    # duration
    ($event{duration}) = ($data =~ m|<td id="ctl00_ContentPlaceHolder1_filmLength_txt" class="recInfo">(?:.*?)(\d+)(?:.*?)</td>|s);
    if ($event{duration}) {
        $event{duration} =~ s/\s+/ /g;
        $event{duration} =~ s/^\s*(.*?)\s*$/$1/;
    } else {
        $event{duration} = 1;
    }

    # language
    ($event{language}) = ($data =~ m|<td id="ctl00_ContentPlaceHolder1_filmLanguage_txt" class="recInfo">(.*?)</td>|s);
    if ($event{language}) {
        $event{language} =~ s/\s+/ /g;
        $event{language} =~ s/^\s*(.*?)\s*$/$1/;
    }

    # subtitles
    ($event{subtitles}) = ($data =~ m|<td id="ctl00_ContentPlaceHolder1_filmTranslation_txt" class="recInfo">(.*?)</td>|s);
    if (defined $event{subtitles}) {
        $event{subtitles} =~ s/\s+/ /g;
        $event{subtitles} =~ s/^\s*(.*?)\s*$/$1/;
    }

    # 3d
    ($event{"3d"}) = ($data =~ m|<td id="ctl00_ContentPlaceHolder1_filmComments_txt".*?class="recInfo2">.*?<strong>הקרנת תלת מימד</strong>|s);
    ($event{"3d"}) = ($data =~ m|<td id="ctl00_ContentPlaceHolder1_filmComments_txt".*?class="recInfo2">.*?<strong>תלת מימד</strong>|s) unless defined $event{"3d"};
    $event{"3d"} = defined $event{"3d"};

    # hebrew dubbing
    ($event{"dub"}) = ($data =~ m|<td id="ctl00_ContentPlaceHolder1_filmComments_txt".*?class="recInfo2">.*?\s*דיבוב לעברית\s*|s);
    $event{"dub"} = defined $event{"dub"};

    # trailer
    ($event{"trailer"}) = ($data =~ m|filmSynopsis_txt.*?<iframe[^>]*src="([^"]*youtube[^"]*)"|s);
    if (defined $event{"trailer"}) {
        $event{"trailer"} =~ s|\?.*||;
        $event{"trailer"} =~ s|/embed/|/watch?v=|;
    } elsif (($event{"trailer"}) = ($data =~ m|filmSynopsis_txt.*?<embed[^>]*src="([^"]*youtube[^"]*)"|s)) {
        $event{"trailer"} =~ s|\?.*||;
        $event{"trailer"} =~ s|/v/|/watch?v=|;
    } elsif (($event{"trailer"}) = ($data =~ m|filmSynopsis_txt.*?<iframe[^>]*src="([^"]*vimeo.com[^"]*)"|s)) {
        $event{"trailer"} =~ s|\?.*||;
        $event{"trailer"} =~ s|.*/|http://vimeo.com/|;
    }

    # screening
    $event{"screening"} = "";
    (my $temp) = $data =~ m|(ctl00_ContentPlaceHolder1_reletadEvent" class="eventsTable".*)|ms;
    while ($temp and $temp =~ m|ctl00_ContentPlaceHolder1_RepeaterReletadEvent_ctl\d+_eventStartDate(.*?)$(.*)|ms) {
        $temp = $2;
        $1 =~ m/>(.*)</;
        $event{"screening"} .= "$1";
        $temp =~ m|eventHour">(.*?)<.*?$(.*)|sm;
        $event{"screening"} .= " $1\n";
        $temp  = $2;
    }
    
    # english
    $data = `curl -s "$link&Lang=En"`;
    

    # name_en
    my $nameenFromShow = 0;
    ($event{name_en}) = ($data =~ m|class="filmName">(.*?)</div>|s);
    unless ($event{name_en}) {
        $nameenFromShow = 1;
        ($event{name_en}) = ($data =~ m|class="filmShow">(.*?)</div>|s);
    }
    if ($event{name_en}) {
        $event{name_en} =~ s/\s+/ /g;
        $event{name_en} =~ s/^\s*(.*?)\s*$/$1/;
    } else {
        $event{name_en} = "";
    }

    # description_en
    ($event{description_en}) = ($data =~ m|class="recTxtBig">(.*?)</td>|s);
    if (defined $event{description_en}) {
        #$event{description_en} =~ s|</?p\b[^>]*>||gs;
        $event{description_en} =~ s|<[^>]+?>||gs;
        _decode_entities($event{description_en}, \%entity2char, 1);
        $event{description_en} =~ s|<object.*?</object>||gs;
        $event{description_en} =~ s/^\s*(.*?)\s*$/$1/;
    }
  
  cacheFilmEnd:
    foreach my $key (keys %event) {
        $event->{$key} = $event{$key};
    }
}

# input: event; output: partial event hash from event url (+ english)
sub fillCachedEvent {
    my $event = shift;

    my $link = $event->{link};
    unless (defined $eventCache{$link}) {

        if ($link =~ m/collection.aspx/) {
            cacheCollection($event);
        } else {
            cacheFilm($event);
        }

        $eventCache{$link} = $event;
    }
    my $cached = $eventCache{$link};
    $event->{keys %$cached} = values %$cached;
}

# fills the @events
sub get_events {
    (my $year, my $month, my $day) = m/(\d+)-(\d+)-(\d+)/;
    print STDOUT "Fatching date: $day/$month/$year";
    my $eventCount = 0;
    my $skipped = 0;
    $monthCache{$month} = `curl -s "$input"` unless (defined $monthCache{$month});
    my $monthData = $monthCache{$month};
    ($monthData) = ($monthData =~ m|<td class="eventsTable".*?<table[^>]*>(.*?)</table>|s);

    my @programs = ($monthData =~ m|<tr[^>]*>(.*?)</tr>|gs);

    # remove table header
    shift @programs;

    # just today
    @programs = grep{$_ =~ m|eventDate" class="eventDate">$day.$month.$year</div>|} @programs;

    my $numEvents = scalar @programs;

    print STDOUT " \t$numEvents events\n" if $verbose;
    
    #@programs = ($programs[3]); # debug
    foreach my $program (@programs) {
        $eventCount++;
        if ($verbose) {
            print STDOUT "\tevent $eventCount: ";
        } else {
            print STDOUT "\rFatching date: $day/$month/$year \t$eventCount/$numEvents events".($skipped ? " ($skipped skipped)" : "");
        }

        my %event;

        # date + time
        (my $hour) = ($program =~ m|<td class="eventHour">(.*?)</td>|s);
        $hour =~ s/\s+//gm;
        ($event{hour}, $event{minute}) = ($hour =~ m|(\d\d):(\d\d)|);
        $event{day} = $day;
        $event{month} = $month;
        $event{year} = $year;

        # link + name
        ($event{link},$event{name}) = ($program =~ m|<a class="eventName" href="([^"]+)">([^<]+)</a>|s);
        $event{link} = "http://www.jer-cin.org.il$event{link}";
        $event{name} =~ s/\s+/ /g;
        $event{name} =~ s/^\s*(.*?)\s*$/$1/;

        # location
        ($event{location}) = ($program =~ m|class="hallImg"><img[^<]*alt="([^"]+)"|s);
        ($event{location}) = ($program =~ m|class="hallImg">(.*?)</td>|s) unless $event{location};
        $event{location} =~ s/\s+/ /g;
        $event{location} =~ s/^\s*(.*?)\s*$/$1/;

        if ($event{location} ~~ @excludeHalls) {
            print "Skipped ($event{location})\n" if $verbose;
            $skipped++;
            next;
        }
        if (@includeHalls and not $event{location} ~~ @includeHalls) {
            print "Skipped ($event{location})\n" if $verbose;
            $skipped++;
            next;
        }
        # extra data
        fillCachedEvent(\%event); # undebug
        #$event{link} = "file:///Users/irush/Programming/Projects/cinematheque/site/collection.aspx.showid=12916.aspx"; fillCachedEvent(\%event); # debug


        print STDOUT "$event{name}".($event{"3d"} ? " - 3d" : "").($event{name_ne} ? " ($event{name_en})" : "")." - $event{location}\n" if $verbose;

        push @events, \%event;

        #last;  # debug
    }

    if ($verbose) {
        print STDOUT "\tNo events\n" if ($numEvents == 0);
        print STDOUT "\n";
    } else {
        print STDOUT "\rFatching date: $day/$month/$year \t$eventCount/$numEvents events".($skipped ? " ($skipped skipped)" : "")."\n";
    }
}

# fills the @dates, format: year-month-day
sub get_dates {
    (undef, undef, undef, my $day, my $month,my $year, undef, undef) = gmtime(time);
    $year += 1900;
    $month += 1;

    while (my $argv = shift @ARGV) {
        if ($argv eq "-d" or $argv eq "--day") {
            $day = shift @ARGV;
        } elsif ($argv eq "-m" or $argv eq "--month") {
            $month = shift @ARGV;
        } elsif ($argv eq "-y" or $argv eq "--year") {
            $year = shift @ARGV;
        } elsif ($argv =~ m|^(\d+(?:\.\.\d+)?)/(\d+)/(\d+)$|) {
            $day = $1;
            $month = $2;
            $year = $3;
        } elsif ($argv eq "--verbose" or $argv eq "-v") {
            $verbose = 1;
        } elsif ($argv eq "-o") {
            $output = shift @ARGV;
        } elsif ($argv eq "-i") {
            $input = shift @ARGV;
            if ($input =~ m,^/,) {
                $input = "file://$input";
            } else {
                my $pwd = `pwd`;
                chomp $pwd;
                $input = "file://$pwd/$input";
            }
        } elsif ($argv =~ m/^-([1234]+)$/) {
            push @excludeHalls, map {"סינמטק $_"} split //, $1;
        } elsif ($argv =~ m/^\+([1234]+)$/) {
            push @includeHalls, map {"סינמטק $_"} split //, $1;
        } else {
            print STDERR "unknown option $argv\n";
            usage();
        }
    }

    $year += 2000 if ($year =~ m/^\d{1,2}$/);

    if ($day =~ m/(\d+)..(\d+)/) {
        for $day ($1..$2) {
            push @dates, "$year-$month-$day";
            #push @dates, "$day.$month.$year";
        }
    }

    if (scalar @dates == 0) {
        push @dates, "$year-$month-$day"
        #push @dates, "$day.$month.$year";
    }
}

get_dates;

foreach (@dates) {
    get_events $_;
}

if (scalar @events == 0) {
    print STDOUT "no events found\n";
    exit 0;
}

print_cal;

exit 0;
