#!/usr/bin/perl -w
#
# Get dart times and parse them into something readable
#
# http://www.irishrail.ie/your_journey/picker_real_time_results.asp?station_name=Glenageary&station=GYDN%2CGYUP&direction=S&mins=60&image1.x=11&image1.y=5&radioservice=1&radioservice1=1
# http://www.irishrail.ie/your_journey/picker_real_time_results.asp?station_name=Glenageary&direction=S&mins=60&image1.x=11&image1.y=5&radioservice=1&radioservice1=1 also works
#
use WWW::Mechanize;
use HTML::TokeParser;
use CGI;
use strict;
use constant CACHEDIR => "/var/tmp/.dartcache";

-d CACHEDIR or mkdir CACHEDIR, 0755;

my $agent = WWW::Mechanize->new( env_proxy => 1, autocheck => 1 );
$agent->agent_alias( 'Windows IE 6' );

# arrange these from north to south
my @locations = (
                 [ "Skerries", 0, 0 ],
                 [ "Sutton", 0, 0 ],
                 [ "Balbriggan", 0, 0 ],
                 [ "Bayside", 0, 0 ],
                 [ "Clontarf Road", 0, 0 ],
                 [ "Donabate", 0, 0 ],
                 [ "Drogheda", 0, 0 ],
                 [ "Drumcondra", 0, 0 ],
                 [ "Gormanston", 0, 0 ],
                 [ "Harmonstown", 0, 0 ],
                 [ "Howth Jct", 0, 0 ],
                 [ "Howth", 0, 0 ],
                 [ "Kilbarrack", 0, 0 ],
                 [ "Killester", 0, 0 ],
                 [ "Laytown", 0, 0 ],
                 [ "Malahide", 0, 0 ],
                 [ "Raheny", 0, 0 ],
                 [ "Rush & Lusk", 0, 0 ],
                 [ "Portmarnock", 0, 0 ],
                 [ "Connolly", 0, 0 ],
                 [ "Tara Street", 0, 0 ],
                 [ "Pearse", 0, 0 ],
                 [ "Gd Canal Dock", 0, 0 ],
                 [ "Lansdowne Rd", 0, 0 ],
                 [ "Sandymount", 0, 0 ],
                 [ "Sydney Parade", 0, 0 ],
                 [ "Booterstown", 0, 0 ],
                 [ "Blackrock", 0, 0 ],
                 [ "Seapoint", 0, 0 ],
                 [ "Salthill", 53.29532, -6.15241 ],
                 [ "Dun Laoghaire", 53.29503, -6.13496 ],
                 [ "Sandycove", 53.28812, -6.12722 ],
                 [ "Glenageary", 53.28132, -6.12290 ],
                 [ "Dalkey", 53.27562, -6.10307 ],
                 [ "Killiney", 0, 0 ],
                 [ "Shankill", 0, 0 ],
                 [ "Bray", 0, 0 ],
                 [ "Greystones", 0, 0 ],
);

my $station;
my $cgi;
my $output = "text";
my ( $wap, $html );

if ( $ENV{'REMOTE_ADDR'} ) {
    $cgi = new CGI;

    # find out what the hell is talking to us
    $wap = $cgi->Accept( "text/vnd.wap.wml" );
    $html = $cgi->Accept( "text/html" );

    # prefer wap only if it's higher up the list
    if ( $wap >= $html ) {
        $output = "wap";
    } else {
        $output = "html";
    }

    # or explictly requested
    if ( $cgi->param( "fmt" )) {
        $output = $cgi->param( "fmt" );
        if ( $output ne "html" and $output ne "wap" and $output ne "xml" ) {
            $output = "html";
        }
    }

    $station = $cgi->param( "station" );
}

if ( $output eq "html" ) {
    print $cgi->header;
    print $cgi->start_html( -title => "DART Timetable (HTML)" );
} elsif ( $output eq "xml" ) {
    print $cgi->header( -type => 'text/xml' );
} elsif ( $output eq "wap" ) {
    print $cgi->header( -type => 'text/vnd.wap.wml',
                        -vary => '*' );
    print <<"WML";
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
WML
} else {
    if ( @ARGV ) {
        $station = join( " ", @ARGV );
    }
}

if ( defined( $station )) {
    my @times = get_times( $station );
    my $se = $station;
    $se =~ s/\&/\&amp;/g;
    if ( $output eq "wap" ) {
        print "<card id=\"DART\" title=\"Times for $se\">\n<p>";
    }

    if ( @times ) {
        my $maxw = 0;
        map {
            if ( length( $_->[0] ) > $maxw ) {
                $maxw = length( $_->[0] );
            }
        } @times;
        $maxw++;

        print "Times for $se\n";

        if ( $output ne "text" ) {
            print "<table columns=\"3\">\n";
        }
        for my $t ( @times ) {
            if ( $output eq "text" ) {
                printf( "%-${maxw}s %-8s %7s\n", @{$t} );
            } else {
                my @t = ( $t->[0], $t->[2] ); # skip platform
                print "<tr><td>";
                print join( "</td><td>", @t );
                print "</td></tr>";
            }
        }
        if ( $output ne "text" ) {
            print "</table>\n</p>\n";
        }
        if ( $output eq "wap" ) {
            print "</card>\n";
        }
    } else {
        print "No scheduled services listed for $station";
    }
} else {
    # present a list of stations
    my @stations = get_stations();
    if ( @stations ) {
        if ( $output eq "wap" ) {
            print "<card id=\"DARTselect\" title=\"DART on WAP\">\n";
#            print "<do type=\"accept\" label=\"Get Timetable\">\n";
        }

        if ( $output ne "text" ) {
            # debug
            print "<p>pref wml: $wap, html: $html</p>\n";
            print "<p>";
        }
        print "Please choose a DART station";
        if ( $output ne "text" ) {
            print "</p>";
        }
        print "\n";

        if ( $output eq "html" ) {
            print "<form>\n";
        }
        if ( $output eq "wap" ) {
            print "<p>";
        }

        if ( $output ne "text" ) {
            print "<select name=\"station\">\n";
            map { s/\&/\&amp;/g; print "<option value=\"$_\">$_</option>\n" } @stations;
            print "</select>\n";
        }
        if ( $output eq "wap" ) {
            print "<anchor>\n";
            print "<go href=\"/cgi-bin/dart.pl\">\n";
            print "<postfield name=\"fmt\" value=\"$output\"/>\n";
            print "<postfield name=\"station\" value=\"\$station\"/>\n";
            print "</go>\n";
            print "Get Timetable\n";
            print "</anchor>\n";
            print "</p>\n</card>\n";
        }
        if ( $output eq "html" ) {
            print "<input type=\"submit\" />\n";
            print "</form>\n";
        }

        # simple!
        if ( $output eq "text" ) {
            print join( "\n", @stations ) . "\n";
        }
    } else {
        print STDERR "error: unable to get station list\n";
    }
}

if ( $output eq "html" ) {
    print $cgi->end_html . "\n";
} elsif ( $output eq "wap" ) {
    print "</wml>\n";
}

sub get_times {
    my $station = shift;

    my @times;

    $station =~ s/\&/%26/g;

    my $fetch = 'http://www.iarnrodeireann.ie/your_journey/picker_real_time_results.asp?station_name=' . $station . '&direction=A&mins=60&image1.x=11&image1.y=5&radioservice=1&radioservice1=1';

    my $res = $agent->get( $fetch );

    if ( $res->is_success ) {
        my $content = $res->content;

        if ( $content =~ /There are no trains running/s ) {
            # hacky
            push @times, [ "There are no trains running", "", "" ];
            return @times;
        }

        if ( $content =~ /invalid station/ ) {
            push @times, [ "You have", "picked an invalid", "station" ];
            return @times;
        }

        my $parser = new HTML::TokeParser \$content;

        while ( my $token = $parser->get_token ) {
            if ( $token->[0] eq "S" and $token->[1] eq "a" and
                 ( $token->[2]{name}||"") eq "content" ) {

                my $tag = $parser->get_tag( "table" ); # header
                $tag = $parser->get_tag( "table" ); # header 2
                $tag = $parser->get_tag( "table" );

                while ( $token = $parser->get_token ) {
                    if ( $token->[0] eq "E" and $token->[1] eq "table" ) {
                        last;
                    }

                    if ( $token->[0] eq "S" and $token->[1] eq "tr" ) {
                        $tag = $parser->get_tag( "td" );
                        my $from = $parser->get_trimmed_text( "/td" );
                        $tag = $parser->get_tag( "td" );
                        my $plat = $parser->get_trimmed_text( "/td" );
                        $tag = $parser->get_tag( "td" );
                        my $time = $parser->get_trimmed_text( "/td" );

                        push @times, [ $from, $plat, $time ];
                    }
                }
                last;
            }
        }
    } else {
        return ( [ "error", "fetching", "page" ] );
    }

    return @times;
}

sub get_stations {
    my @stations;

    if ( open( STATIONS, CACHEDIR . "/stations.txt" )) {
        @stations = map { chomp; $_ } <STATIONS>;
    } else {
        my $res = $agent->get( 'http://www.iarnrodeireann.ie/include/rtpi_stationpicker.asp?box=station&service=6' );
        if ( $res->is_success) {
            my $c = $res->content;
            my $p = new HTML::TokeParser( \$c );
            while ( my $t = $p->get_tag( "select" )) {
                if ( $t->[1]{name} eq "selectmenu" ) {
                    while ( $t = $p->get_token ) {
                        if ( $t->[0] eq "S" and $t->[1] eq "option" ) {
                            my $station = $p->get_trimmed_text( "/option" );
                            push @stations, $station;
                        }
                        last if ( $t->[0] eq "E" and $t->[1] eq "select" );
                    }
                    last;
                }
            }
        } else {
            return;
        }

        if ( open( STATIONS, ">" . CACHEDIR . "/stations.txt" )) {
            print STATIONS join( "\n", @stations ) . "\n";
            close( STATIONS );
        }
    }

    return @stations;
}
