#!/usr/bin/perl

# the files are generated from pdf (Aushangfahrplan): run pdftohtml and keep the <FILENAME>s.html
$mydir = "/home/pi/bubu";
#$mydir = ".";
$debug = 0;
$showTrips = 4; # how many trips should be shown
$daySplitTime = "04:40"; # HH:MM normally the first trains/tubes drive at 4 o'clock - all until 3 o'clock belong to the current date
$file_public = "FeiertageBW.csv";
@files = ("VVS.S1.Oesterfeld.BadCanstatts.html", "VVS.S1.Oesterfeld.Vaihingens.html", "VVS.S2.Oesterfeld.BadCanstatts.html", "VVS.S2.Oesterfeld.Vaihingens.html", "VVS.S3.Oesterfeld.BadCanstatts.html", "VVS.S3.Oesterfeld.Vaihingens.html", "VVS.U14.Engelbold.Muehlhausens.html", "VVS.U14.Engelbold.Vaihingens.html");


@dayTypes = ("Week", "Saturday", "Sunday");
$dayTypeId = 0;
$dayType = $dayTypes[$dayTypeId];
@weekdays = qw(sunday monday tuesday wednesday thursday friday saturday); # 0, 1, 2, 3, 4, 5, 6
$today = `date +%Y-%m-%d`; chomp($today);
$todayTs = date2timestamp($today);
$todayW = getWeekday($today, "en", "id");
$nowH = `date +%H`; chomp($nowH);
$nowM = `date +%M`; chomp($nowM);
$nowY = `date +%y`; chomp($nowY);


$myself = $0;
$arg1 = $ARGV[0];
if($arg1 eq "-h" or $arg1 eq "-help" or $arg1 eq "--help"){
	println("usage: $myself [tube|train] [next|today|tomorrow|in x days] [in|out] [on <weekday>] [at <number> [number]]");
	exit 0;
}

# read $file_public for public holidays
open(F, "<$mydir/$file_public") or printAndDie("ERROR: cannot read $file_public: $!");
while(<F>){
	chomp($_);
	$line = $_;
	next if($line!~/\S/ or $line=~/^\s*#/);
	if($line=~/^(\d\d\d\d\-\d\d\-\d\d):\s+(\S.+)$/){
		$publics{$1} = $2;
	}
}
close(F);

# interprete args
$argstr = join(" ", @ARGV);
$argstr = "tube next in" if($argstr!~/\S/);
$kind = ""; # tube or train, default: tube
$kind = "tube" if($argstr=~/\btube\b/i or $argstr=~/\bu\b/i);
$kind = "train" if($argstr=~/\btrain\b/i or $argstr=~/\bs\b/i);
$kind = "tube" if($kind!~/\S/);
$on = ""; $onwasgiven = 0;
if($argstr=~/\bon (\S+)\b/i){
	$on = lc($1);
	$onwasgiven = 1;
}
$id = -1; for($w=0;$w<$#weekdays+1;$w++){ $id = $w if($weekdays[$w] eq $on); }
printAndDie("ERROR: cannot understand the given weekday $on!") if($on ne "" and $id<0);
$on = $id;
$atH = $nowH; $atM = $nowM;
if($argstr=~/\bat\s+(\d+)\b/i){
	$atH = d2($1);
	if($argstr=~/\bat\s+\d+\s+(\d+)\b/i){
		$atM = d2($1);
	} else{
		$atM = d2(0);
	}
}
$when = ""; # day gap: 0=next, 0=today, 1=tomorrow ..., default: 0 (next)
$when = 0 if($argstr=~/\bnext\b/i);
$when = 0 if($argstr=~/\btoday\b/i);
$when = 1 if($argstr=~/\btomorrow\b/i);
$when = $1 if($argstr=~/\bin (\d+) days?/i);
$when = -1 if($when eq "");
$argstr =~ s/\bin \d+ days?\b//i; # strip that phrase off, because the direction may also use "in"
# if sth. like "on sunday" was given: this has priority over sth. like "in 3 days" and also over "next"
$wdaydiff = 0;
if($onwasgiven){
	$wdaydiff = $on - $todayW;
	$wdaydiff += 7 if($wdaydiff<0);
	$when = $wdaydiff;
}
# now get the weekday of $when
$whenTs = $todayTs + $when*24*3600;
$whenDate = timestamp2date($whenTs);
$publicday = "";
$whenW = getWeekday($whenDate); # 2 letters


# public holidays render also to Su
@pkeys = sort(keys(%publics));
for($p=0;$p<$#pkeys+1;$p++){
	$publicday = $publics{$pkeys[$p]} if($whenDate eq $pkeys[$p]);
}
$whenW = "Su" if($publicday ne "");
$whenType = "Week";
$whenType = "Saturday" if($whenW eq "Sa");
$whenType = "Sunday" if($whenW eq "Su");

# get direction
$direction = "in"; # to stuttgart, default
$direction = "out" if($argstr=~/\bout\b/);
println("- searching for a $kind, direction $direction, in $when days (which is a $whenW), at $atH:$atM ...") if($debug);


# open files
for($f=0;$f<$#files+1;$f++){
	@lines = ();
	$file = $files[$f];
	$fdir = "in";
	$fdir = "out" if($file=~/Vaihingen/);
	next if($fdir ne $direction);
	next if($kind eq "tube" and $file!~/^VVS.U/);
	next if($kind eq "train" and $file=~/^VVS.U/);
	$ftyp = $1 if($file=~/^VVS.(\w+)?\./);
	$numberOfSectionsAtOnce = 0; $go = 0; $cmtstart = 0;
	println("- read file $file ...") if($debug);
	# 1) read file into @lines, and check which type we have (2 sections: a)Mo-Fr + Sa b)Su at next page, or 3 sections: Mo-Fr + Sa + Su)
	open(F, "<$mydir/$file") or printAndDie("ERROR: cannot read file $file: $!");
	while(<F>){
		chomp($_);
		$line = trim($_);
		next if($line!~/\S/);
		$line =~ s/&#160;/ /g;
		$line =~ s/&nbsp;/ /g;
		if(!$numberOfSectionsAtOnce and $line=~/Samstag/i and $lastline=~/Montag\b/i){
			$numberOfSectionsAtOnce = 2; # s-bahn: 1st page: mo-fr / sa, 2nd page: su
			$go = 1;
			next;
		}
		if($numberOfSectionsAtOnce==2 and $line=~/Feiertag/i and $lastline=~/Montag\b/i){
			$numberOfSectionsAtOnce = 3; # u-bahn: all 3 sections in 1 page
			next;
		}
		$lastline = $line;
		if($go and $line=~/ = /){
			$go = 0;
			$cmtstart = 1;
		}
		next if($cmtstart and $line!~/ = /);
		if($cmtstart and $line=~/ = /){
			$line =~ s/<[^>]+>//g;
			@cmtelems = split(/ = /, $line); # ("n72", "nur samstags TP2", "nur bis vaihingen")
			@cmtelems2 = $cmtelems[0];
			for(my $c=1;$c<$#cmtelems;$c++){
				if($cmtelems[$c]=~/^(.+)\b([●■\w]{1,3})\s*$/){
					push(@cmtelems2, $1, $2);
				}
			}
			push(@cmtelems2, $cmtelems[-1]);
			for(my $c=0;$c<$#cmtelems2+1;$c+=2){
				println("WARN: comment ". $cmtelems2[$c] ." already exists, with value ". $comments{$cmtelems2[$c]}) if(exists($comments{$cmtelems2[$c]}) and $debug);
				$comments{$cmtelems2[$c]} = $cmtelems2[$c+1];
			}
		}
		next if(!$go);
		push(@lines, $line);
	}
	close(F);
	println("- file has $numberOfSectionsAtOnce sections at once") if($debug);

	for($n=0;$n<$#lines+1;$n++){
		$line = $lines[$n];
		if($line=~/^<b>(\d+)\s*<\/b>(.*)<br\/>/){
			$el1 = $1; $el2 = $2;
			$line =~ s/<br\/>//g;
			storeTimes($ftyp, $hour, $minutes);
			$hour = d2($el1);
			$minutes = $el2;
			resetDayType();
			nextDayType() if($minutes!~/\S/);
		} else{
			$line =~ s/<br\/>//g;
			$minutes .= " ". $line;
		}
	}
	storeTimes($ftyp, $hour, $minutes);
}


# sort acc. to $daySplitTime
@schedules = sort(arrayUnique(@{$allSchedules{$whenType}}));
for($t=0;$t<$#schedules+1;$t++){
	($stime, $stype, $scomt) = split(/,/, $schedules[$t]);
	if(time2sec($stime)<time2sec($daySplitTime)){
		push(@toTheEnd, $schedules[$t]);
	} else{
		push(@toTheStart, $schedules[$t]);
	}
}
@schedules = ();
push(@schedules, @toTheStart, @toTheEnd);


# go through all schedules
$snr = 0;
$dirword = "to the center";
$dirword = "to the south" if($direction eq "out");
for($t=0;$t<$#schedules+1;$t++){
	($stime, $stype, $scomt) = split(/,/, $schedules[$t]);
	next if(time2sec($stime)<time2sec("$atH:$atM"));
	$isDriving = 1;
	# here can the comment be evaluated
	next if(!$isDriving);
	$snr++;
	println("a $kind at $stime $dirword, $stype");
	last if($snr>=$showTrips);
}










### functions ###
sub storeTimes{
	my $traintype = shift;
	my $hour = shift;
	my $str = shift;
	$str =~ s/<[^>]+>//g;
	return if($hour!~/\S/ or $str!~/\S/);
	my $lastmin = ""; my $min = ""; my $cmt = "";
	my @elems = split(/\s+/, trim($str));
	for(my $e=0;$e<$#elems+1;$e++){
		my $elem = $elems[$e];
		if($elem=~/^(\d\d)(.*)$/){
			my $el1 = $1;
			$cmt = $2;
			next if($el1>59 || length($cmt)>3);
			$min = $el1;
		}
		next if($cmt=~/^[\-\d]{5}/);
		nextDayType() if($min<=$lastmin);
		$lastmin = $min;
		push(@{$allSchedules{$dayType}}, "$hour:$min,$traintype,$cmt");
	}
}
sub resetDayType{
	$dayTypeId = 0;
	$dayType = $dayTypes[$dayTypeId];
}
sub nextDayType{
	$dayTypeId++;
	$dayTypeId = 0 if($dayTypeId>2);
	$dayType = $dayTypes[$dayTypeId];
}
sub printAndDie{
	my $msg = shift;
	println($msg);
	exit 1;
}
sub println{
	my $msg = shift;
	print $msg ."\n";
}
sub trim{
	my $msg = shift;
	$msg =~ s/^\s+//;
	$msg =~ s/\s+$//;
	return $msg;
}
sub timestamp2date{
	my $datets = shift;
	my ($tsec, $tmin, $thour, $tday, $tmon, $tyear) = localtime($datets);
	$tmon = d2($tmon+1); $tyear += 1900; $tday = d2($tday);
	return "$tyear-$tmon-$tday";
}
# convert a date [YY]YY-MM-DD [hh:ss:[mm]] to a timestamp
sub date2timestamp{
	my $datetime = shift;
	$datetime .= " 00:00:00" if($datetime=~/^\d+\-\d{2}\-\d{2}$/);
	my @elems = split(/\s+/, $datetime);
	my $time = pop(@elems);
	$time = trim($time);
	$time = "00:00" if($time!~/\S/);
	$time .= ":00" if($time=~/^\d\d:\d\d$/);
	my @timeelems = split(/:/, $time);
	my $hour = d2($timeelems[0]);
	my $minute = d2($timeelems[1]);
	my $second = d2($timeelems[2]);
	my $date = date2mysql(join(" ", @elems));
	my($year, $month, $day) = split(/\-/, $date);
	$year = "20$year" if(length($year)==2 and $day=~/\S/);
	if($day!~/\S/){
		$day = $month;
		$month = $year;
		my ($tsec, $tmin, $thour, $tday, $tmon, $tyear) = localtime(time);
		$year = $tyear;
	}
	use Time::Local;
	$month--;
	timelocal("$second","$minute","$hour",$day,$month,$year);
}
sub d2{
	my $number = shift;
	return "" if($number!~/\S/);
	return "0$number" if($number<10);
	return $number;
}

sub getWeekday{
	my $date = shift;
	my $mode = shift;
	my $kind = shift;
	$mode = "en" if($mode!~/\S/);
	my @weekdays = ("Su", "Mo", "Tu", "We", "Th", "Fr", "Sa");
	@weekdays = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa") if($mode eq "de");
	$timestamp = date2timestamp(date2mysql($date));
	my ($sec, $min, $hour, $mday, $month, $year, $wday, $yday, $summertime) = localtime($timestamp);
	return $wday if($kind eq "id");
	return $weekdays[$wday];
}
sub date2mysql{
	my $val = shift;
	my $date = "";
	$val = trim($val);
	$date = "$3-$2-$1" if($val=~/^(\d\d)\.(\d\d)\.(\d\d\d\d)/);
	$date = "$1-$2-$3" if($val=~/^(\d\d\d\d)\-(\d\d)\-(\d\d)/);
	$date = "$3-$2-$1" if($val=~/^(\d\d)\/(\d\d)\/(\d\d\d\d)/);
	$date = "$3-$2-$1" if($val=~/^(\d\d)\-(\d\d)\-(\d\d\d\d)/);
	if($val=~/(\d+)[stndrh\.]{0,2}\s*(\w{3,})\s*(\d\d\d\d)/i or $val=~/(\d\d\d\d)\s*(\w{3,10})\s*(\d+)\s*[stndrh\.]{0,2}/i){
		my $day = trim($1);
		my $mname = trim($2);
		my $year = trim($3);
		if($day=~/\d{4}/){
			my $tmp = $year;
			$year = $day;
			$day = $tmp;
		}
		$mnr = 1;
		$mnr = 2 if($mname=~/feb/i);
		$mnr = 3 if($mname=~/m[a.]r/i);
		$mnr = 4 if($mname=~/apr/i);
		$mnr = 5 if($mname=~/ma[yi]/i);
		$mnr = 6 if($mname=~/jun/i);
		$mnr = 7 if($mname=~/jul/i);
		$mnr = 8 if($mname=~/aug/i);
		$mnr = 9 if($mname=~/sep/i);
		$mnr = 10 if($mname=~/o[ck]t/i);
		$mnr = 11 if($mname=~/nov/i);
		$mnr = 12 if($mname=~/de[cz]/i);
		$date = "$year-". d2($mnr) ."-$day";
	}
	return $date;
}
# converts a time HH:MM:SS to a number
sub time2sec{
	my $time = shift;
	my $d = 0;
	if($time=~/[,\.](\d{2,3})$/){
		$d = $1;
		$time =~ s/[,\.]\d{2,3}$//;
	}
	my ($h, $m, $s) = split(/:/, $time);
	$h = "00" if($h!~/\S/);
	$m = "00" if($m!~/\S/);
	$s = "00" if($s!~/\S/);
	my $res = int($h)*3600 + int($m)*60 + int($s);
	$res += ".$d" if($d);
	return $res;
}
# sort an array numerically
sub numsort{ # call: numsort(@numbers);
	return sort { $a <=> $b } @_;
}
sub arrayUnique {
	my %seen;
	grep !$seen{$_}++, @_;
}

