#!/usr/bin/perl

$dir = ".";
$dir = "/home/pi/bubu";
$file = "$dir/Holidays.BW.%YEAR%.htm"; # the htm file from https://www.schulferien.org/Kalender_mit_Ferien/kalender_2020_ferien_Baden_Wuerttemberg.html (load as many for the future as you can)
$pfile = "$dir/FeiertageBW.csv";
$ffile = "$dir/Faschingsferien.csv";
$myself = $0;
$tellDate = 0;
$hdate = $ARGV[0];
if($hdate!~/\S/ or $hdate eq "-h" or $hdate eq "-help" or $hdate eq "--help"){
	println("usage: $myself [tellDate] [next] [this year|next year|in x years|<year>] [this month|next month|in x months|<month>]");
	exit 0;
}

$today = `date +%Y-%m-%d`; chomp($today);
$todayN = `date +%Y%m%d`; chomp($todayN);
#$today = "2020-03-31";
$todayts = date2timestamp($today);
$todayY = `date +%Y`; chomp($todayY);
#$todayY = "2020";
$todayM = `date +%m`; chomp($todayM);
#$todayM = "03";
$todayD = `date +%d`; chomp($todayD);
#$todayD = "31";
@months = qw(january february march april may june july august september october november december);


# interprete arguments
$hdateM = ""; # month starts with 1
$hdateY = "";
$hdateD = "";
# calculate desired year
$argstr = join(" ", @ARGV);
if($argstr=~/\btellDate\b/i){
	$tellDate = 1;
	$argstr =~ s/\btellDate\b//i;
}
if($argstr=~/\bthis year\b/i){
	$hdateY = $todayY;
	$argstr =~ s/\bthis year\b//i;
}
if($argstr=~/\bnext year\b/i){
	$hdateY = $todayY + 1;
	$argstr =~ s/\bnext year\b//i;
	$hdateM = 1;
	$hdateD = 1;
}
if($argstr=~/\bin (\d+) years?\b/i){
	$hdateY = $todayY + $1;
	$argstr =~ s/\bin \d+ years?\b//i;
	$hdateM = 1;
	$hdateD = 1;
}
if($argstr=~/\b(\d\d\s*\d\d)\b/i){
	$hdateY = $1;
	$hdateY =~ s/\D//g;
	$argstr =~ s/\b\d\d\s*\d\d\b//i;
	$hdateM = 1;
	$hdateD = 1;
}
$hdateY = $todayY if($hdateY eq "");
$hdateY2 = $hdateY + 1;
# calculate desired month
$argstr = join(" ", @ARGV);
if($argstr=~/\bthis month\b/i){
	$hdateM = $todayM;
	$argstr =~ s/\bthis month\b//i;
}
if($argstr=~/\bnext month\b/i){
	$hdateM = $todayM + 1;
	$argstr =~ s/\bnext month\b//i;
}
if($argstr=~/\bin (\d+) months?\b/i){
	$hdateM = $todayM + $1;
	$argstr =~ s/\bin \d+ months?\b//i;
}
if($argstr=~/\bin [a-z][a-z]\b/i or $argstr=~/\b([a-z][a-z])\b/i){
	if($1 ne "in"){
		println("ERROR: month is too short!");
		exit 1;
	}
}
for($m=0;$m<$#months+1;$m++){
	$month = $months[$m];
	$mon = substr($month, 0, 3);
	if($argstr=~/\bin $month\b/i or $argstr=~/\bin $mon\b/i){
		$hdateM = ($m+1);
		$argstr =~ s/\bin $month\b//i;
		$argstr =~ s/\bin $mon\b//i;
	}
	if($argstr=~/\b$month\b/i or $argstr=~/\b$mon\b/i){
		$hdateM = ($m+1);
		$argstr =~ s/\b$month\b//i;
		$argstr =~ s/\b$mon\b//i;
	}
	$hdateD = 1;
}
# calculate desired start day
$searchForNext = 0;
if($argstr=~/\bnext\b/){
	$searchForNext = 1;
	$argstr =~ s/\bnext\b//i;
}
$hdateM = $todayM if($hdateM eq "");
$hdateD = $todayD if($hdateD eq "");
$hdateD = d2($hdateD);
$hdateM = d2($hdateM);



### main
# read public holidays
open(F, "<$pfile") or printAndDie("ERROR: cannot read public holidays file $pfile: $!");
while(<F>){
	chomp($_);
	$line = $_;
	$line =~ s/#.*$//;
	next if($line!~/\S/);
	if($line=~/^\s*([\d\-]+)\s*;\s*/){
		$pday = $1;
		$pday =~ s/\D//g;
		push(@publicdays, $pday);
	}
}
close(F);

# read fasching holidays
open(F, "<$ffile") or printAndDie("ERROR: cannot read fasching holidays file $ffile: $!");
while(<F>){
	chomp($_);
	$line = $_;
	$line =~ s/#.*$//;
	next if($line!~/\S/);
	if($line=~/^\s*([\d\-]+)\s*;\s*/){
		$pday = $1;
		$pday =~ s/\D//g;
		push(@publicdays, $pday);
	}
}
close(F);

$startDate = "$hdateY$hdateM$hdateD";
browseYear($hdateY);
browseYear($hdateY2) if($hdateM==12); # in order to catch year breaks, go through 2 files

# go through %dates, and simplify to %free (only 2 states: free | normal)
@days = sort(keys(%dates));
for($n=0;$n<$#days+1;$n++){
	$day = $days[$n];
	$state = $dates{$day}; # normal, saturday, sunday, public or holiday
	$stateY = $dates{$days[$n-1]}; # state of yesterday - necessary for holidays starting at a monday
	$stateY = "normal" if(!$n); # if the first day of search is a monday, we don't consider it
	if($state eq "saturday"){
		if($n<1){
			$frees{$day} = "normal";
		} else{
			# 1) take over the state of Monday - but just normal of free
			# 2) take over the state of Friday - and opt. overwrite 1)
			$friday = $days[$n-1]; $stateF = $dates{$friday};
			$monday = $days[$n+2]; $stateM = $dates{$monday};
			$frees{$day} = ($stateM eq "normal" ? "normal" : "free" );
			$frees{$day} = ($stateF eq "normal" ? "normal" : "free" );
		}
	}
	elsif($state eq "sunday"){
		if($n<2){
			$frees{$day} = "normal";
		} else{
			# 1) take over the state of Monday - but just normal of free
			# 2) take over the state of Friday - and opt. overwrite 1)
			$friday = $days[$n-2]; $stateF = $dates{$friday};
			$monday = $days[$n+1]; $stateM = $dates{$monday};
			$frees{$day} = ($stateM eq "normal" ? "normal" : "free" );
			$frees{$day} = ($stateF eq "normal" ? "normal" : "free" );
		}
	}
	else{
		$frees{$day} = ($state eq "normal" ? "normal" : "free" );
		if($stateY eq "sunday" and $frees{$day} eq "free"){
			$frees{$days[$n-2]} = "free";
			$frees{$days[$n-1]} = "free";
		}
	}
	# check with @publicdays: if mentioned, the free state is set
	$isin = 0;
	foreach $pday(@publicdays){
		$isin = 1 if($pday eq $day);
	}
	$frees{$day} = "free" if($isin);
}

# go through %frees
for($n=0;$n<$#days+1;$n++){
	$day = $days[$n];
	($y, $m, $d) = getDateElems($day);
	$state = $dates{$day}; # normal, saturday, sunday, public or holiday
	$free = $frees{$day}; # normal or free
	if($free eq "free" and $free ne $lastfree){
		last if($m ne $hdateM and !$searchForNext);
		$frees{$day} = "start";
		$holidays{$day} = "start";
	} elsif($free eq "normal" and $free ne $lastfree){
		if($n>0){
			$yesterday = $days[$n-1];
			$frees{$yesterday} = "stop";
			if($holidays{$yesterday} eq "start"){
				$holidays{$yesterday} = "public";
			} else{
				$holidays{$yesterday} = "stop";
			}
		}
		last if($m ne $hdateM);
	}
	$lastfree = $free;
}

# go through %holidays
@hkeys = sort(keys(%holidays));
if($#hkeys<0){
	$month = $months[$hdateM-1];
	println("no holidays in $month");
} else{
	for($n=0;$n<$#hkeys+1;$n++){
		next if($searchForNext and $hkeys[$n]<$todayN);
		$day = $hkeys[$n];
		$kind = $holidays{$day};
		$day = substr($day, 4);
		$m = substr($day, 0, 2);
		$month = $months[$m-1];
		$d = int(substr($day, 2, 2));
		if($tellDate){
			$d = "0$d" if($d<10);
			$hdateY++ if($m<$todayM);
			println("$hdateY-$m-$d: $kind");
		} else{
			println("$kind on $month $d");
		}
	}
}



### functions ###
sub getDateElems{
	my $day = shift; # in YYYYMMDD
	my $y = substr($day, 0, 4);
	my $m = substr($day, 4, 2);
	my $d = substr($day, 6, 2);
	return ($y, $m, $d);
}
sub browseYear{
	my $year = shift;
	# identify file and open it
	my $yfile = $file;
	$yfile =~ s/%YEAR%/$year/; my $go = 0; my $code = "";
#	println("- read file $yfile ...");
	open(F, "<$yfile") or printAndDie("ERROR: cannot read file $yfile: $!");
	while(<F>){
		my $line = $_;
		chomp($line);
		# start: <div class="bereich_panel">
		$go = 1 if($line=~/<div class=\"bereich_panel\">/i);
		next if(!$go);
		$code .= " ". $line;
	}
	close(F);

	my @lines = split(/<\/thead><tbody><tr class/, $code); # split into monthly sections
	shift @lines; # delete start - now it starts with Janary
	for(my $i=0;$i<$#lines+1;$i++){
		my $line = $lines[$i];
		my $month = $i + 1;
		$line =~ s/ class=\"([^\"]+)\" style=\" \">(\d+)<\/td>/&handleDay($year, $month, $2, $1);/ges; # handleDay(year, month, day, class)
	}
}
$handleOk = 0;
sub handleDay{
	my $year = shift;
	my $month = shift;
	my $day = shift;
	my $class = shift;
	$day = d2($day);
	$month = d2($month);
	my $handleDate = "$year$month$day";
#println($handleDate);
	$handleOk = 1 if($handleDate>=$startDate);
	return if(!$handleOk);
	my @cls = split(/\s+/, $class);
	my $public = 0; my $holiday = 0; my $saturday = 0; my $sunday = 0;
	foreach $class(@cls){
		$saturday = 1 if($class=~/saturday/);
		$sunday = 1 if($class=~/sunday/);
		$public = 1 if($class=~/feiertag/);
		$holiday = 1 if($class=~/ferien/);
	}
	my $state = "normal";
	$state = "public" if($public);
	$state = "holiday" if($holiday);
	$state = "saturday" if($saturday); # we need to keep this information as higher priority
	$state = "sunday" if($sunday);
#println("$handleDate: $state, $class");
	$dates{$handleDate} = $state;
}
sub println{
	my $msg = shift;
	print $msg ."\n";
}
sub printAndDie{
	my $msg = shift;
	println($msg);
	exit 1;
}
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 "0". int($number) if($number<10);
	return $number;
}

sub getWeekday{
	my $date = shift;
	my $mode = 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 $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;
}
