#!/usr/bin/perl -w
# -*- perl -*-

#
# $Id: parse_synop,v 1.10 2003/08/20 22:29:08 eserte Exp $
# Author: Slaven Rezic
#
# Copyright (C) 2001 Slaven Rezic. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
# Mail: slaven.rezic@berlin.de
# WWW:  http://www.rezic.de/eserte/
#

#  SMDL10 KAWN 260000 RRA
#  AAXX 26001
#  10384 32983 13401 10121 20102 30171 40231 52002 80001 333 30///
#        55300 81076
#$synop = "10384 32983 13401 10121 20102 30171 40231 52002 80001 333 30/// 55300 81076";

#  SNDL10 KAWN 132200 RRA
#  AAXX 13221
#  10384 42973 11701 10143 20125 30160 40219 58003 80001 333 55300
#        81076
#$synop = "10384 42973 11701 10143 20125 30160 40219 58003 80001 333 55300 81076";

#  SNDL10 KAWN 132300 RRA
#  AAXX 13231
#  10384 42973 01301 10127 20119 30160 40219 57002 333 55300
#$synop = "10384 42973 01301 10127 20119 30160 40219 57002 333 55300";

#
# aus: http://www.uradio.ku.dk/~ct/eurovejr/
# Die Dateinamen heissen EURO<tag 2 digits><stunde 2 digits>
# Es gibt Meldungen zu 0 - 6 - 12 - 18 Uhr
#
#  SMDL01 EDZW 141800
#  AAXX 14181
#  ...
#  10384 32970 01103 10179 20155 30137 40195 58001
#    333 10224 20100 31007 55300=

use lib "$ENV{HOME}/lib/perl";
use strict;

use vars qw(@data);
BEGIN {
    @data =
	qw($wmo_station_number $precipitation_indicator $token
	   $station_type_and_present_and_past_weather_indicator
	   $cloud_base_of_lowest_cloud_seen $code $visibility
	   $total_cloud_cover $wind_direction $wind_direction_compass
	   $wind_speed $temperature $relative_humidity $dewpoint
	   $station_pressure $seelevel_pressure
	   $characteristics_of_pressure_tendency $three_hour_pressure_change
	   $precipitation_amount
	   $duration_over_which_precipitation_amount_measured $present_weather
	   $present_weather_category $past_weather_type1 $past_weather_type2
	   $amount_of_low_middle_clouds_covering_sky $low_cloud_type
	   $middle_cloud_type $high_cloud_type $date $time
	   $twenty_four_hour_precipitation_in_mm
	   $maximum_temperature_over_previous_24_hours
	   $minimum_temperature_over_previous_24_hours
	   $state_of_ground_with_snow_cover $snow_depth_in_cm
	   @clouds
	   $ground_condition $min_temp_5cm $sunshine_last_hour $sunshine_last_day
	   $precipitation_amount2 $duration_over_which_precipitation_amount_measured2

	   $hoechste_windspitze_in_den_letzten_10_minuten
	   $hoechste_windspitze_seit_dem_letzten_synoptischen_haupttermin
	   $hoechstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin
	   $mittlere_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin
	   $niedrigstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin
	   $windrichtung_in_grad
	   $zeitpunkt_einer_rechtsdrehenden_aenderung_der_windrichtung
	   $zeitpunkt_einer_rueckdrehenden_aenderung_der_windrichtung
	  );
}
use vars @data;

use vars qw(@token);

my($isodate, $synop, $wettermeldung2, $convert_whole_file, $outfile, $d);
use Getopt::Long;
if (!GetOptions("date=s" => \$isodate,
		"synop=s" => \$synop,
		"wettermeldung2" => \$wettermeldung2,
		"wholefile!" => \$convert_whole_file,
		"o=s" => \$outfile,
		"d!" => \$d,
	       )) {
    die "usage";
}

my $file = shift @ARGV;

if ($convert_whole_file) {
    convert_whole_file($file, $outfile);
}

doit($file, $isodate,
     -synop => $synop,
     -wettermeldung2 => $wettermeldung2,
     -main => 1,
    );

sub doit {
    my($file, $isodate, %args) = @_;
    my $wettermeldung2 = $args{-wettermeldung2};
    my $main = $args{-main};
    my $synop = $args{-synop};

    foreach (@data) {
	eval "undef $_"; die $@ if $@;
    }

    if (!defined $synop) {
	if (!$isodate) {
	SEARCH: {
		my(@lines) = split /\n/, `tail -10 $file`;
		for (my $i=$#lines; $i>=0; $i--) {
		    if ($lines[$i] =~ /^\#/) {
			$synop = "";
			#($date, $time) = get_date_time($lines[$i+1]);
			($date, $time) = get_date_time2($lines[$i]);
			foreach my $j ($i+3 .. $#lines) {
			    if ($lines[$j] =~ /^\s*$/) {
				last SEARCH;
			    }
			    $synop .= $lines[$j];
			}
			last SEARCH;
		    }
		}
	    }
	    if (!defined $synop) {
		die "Cannot find last synop in $file";
	    }
	} else {
	    open(F,$file) or die "Can't open $file: $!";
	FILE:
	    while (<F>) {
		if (/^\#$isodate/) {
		    ($date, $time) = get_date_time2($_);scalar <F>;
		    #($date, $time) = get_date_time(scalar <F>); # WMO header
		    scalar <F>; # ???
		    chomp($synop = scalar <F>);
		    while (<F>) {
			chomp;
			if (/^\s*$/) {
			    last FILE;
			}
			$synop .= $_;
		    }
		    last FILE;
		}
	    }
	    close F;
	    if (!defined $synop) {
		die "Cannot find synop for $isodate";
	    }
	}
    } else {
	if (defined $isodate) {
	    ($date, $time) = get_date_time2($isodate);
	}
    }

    @token = split /\s+/, $synop;

    eval {

	$wmo_station_number = shift @token;

	if (next_token()) {

	    $precipitation_indicator =
		{0 => 'Precipitation in groups 1 and 3',
		 1 => 'Precipitation reported in group 1 only',
		 2 => 'Precipitation reported in group 3 only',
		 3 => 'Precipitation omitted, no precipitation',
		 4 => 'Precipitation omitted, no observation'
		}->{substr $token, 0, 1};
	    $station_type_and_present_and_past_weather_indicator =
		{1 => 'manned station -- weather group included',
		 2 => 'manned station -- omitted, no significant weather',
		 3 => 'manned station -- omitted, no weather observation',
		 4 => 'automated station -- weather group included (see automated weather codes 4677 and 4561)',
		 5 => 'automated station -- omitted, no significant weather',
		 6 => 'automated station -- omitted, no weather observation',
		 7 => 'automated station -- weather group included (see automated weather codes 4680 and 4531)',
		}->{substr $token, 1, 1};
	    $cloud_base_of_lowest_cloud_seen =
		{0 => '0 to 50 m',
		 1 => '50 to 100 m',
		 2 => '100 to 200 m',
		 3 => '200 to 300 m',
		 4 => '300 to 600 m',
		 5 => '600 to 1000 m',
		 6 => '1000 to 1500 m',
		 7 => '1500 to 2000 m',
		 8 => '2000 to 2500 m',
		 9 => 'above 2500 m',
		 '/' => 'unknown',
		}->{substr $token, 2, 1};
	    $code = substr $token, 3, 2;
	    if ($code eq '//') {
		$visibility = 'missing';
	    } elsif ($code == 0) {
		$visibility = 'less than 0.1 km';
	    } elsif ($code <= 50) {
		$visibility = sprintf "%.1f km", $code/10;
	    } elsif ($code <  80) {
		$visibility = ($code-50) . " km";
	    } else {
		$visibility =
		    {80=>'30 km',
		     81=>'35 km',
		     82=>'40 km',
		     83=>'45 km',
		     84=>'50 km',
		     85=>'55 km',
		     86=>'60 km',
		     87=>'65 km',
		     88=>'70 km',
		     89=>'greater than 70 km',
		     90=>'less than 0.05 km',
		     91=>'0.05 km',
		     92=>'0.2 km',
		     93=>'0.5 km',
		     94=>'1 km',
		     95=>'2 km',
		     96=>'4 km',
		     97=>'10 km',
		     98=>'20 km',
		     99=>'greater than 50 km',
		    }->{$code};
	    }
	}

	goto PRINT if !next_token();
	$total_cloud_cover =
	    {0 => '0 eighths (clear)',
	     1 => '1/8th',
	     2 => '2/8ths',
	     3 => '3/8ths',
	     4 => '4/8ths',
	     5 => '5/8ths',
	     6 => '6/8ths',
	     7 => '7/8ths',
	     8 => '8/8ths (overcast)',
	     9 => 'sky obscured',
	     '/' => 'no observation',
	    }->{substr $token, 0, 1};
	$wind_direction = substr($token, 1, 2);
	if ($wind_direction eq '//') {
	    $wind_direction = 'no observation';
	} else {
	    $wind_direction*=10;
	    if ($wind_direction < 22.5 || $wind_direction > 360-22.5) {
		$wind_direction_compass = 'N';
	    } elsif ($wind_direction < 22.5+45) {
		$wind_direction_compass = 'NE';
	    } elsif ($wind_direction < 22.5+45*2) {
		$wind_direction_compass = 'E';
	    } elsif ($wind_direction < 22.5+45*3) {
		$wind_direction_compass = 'SE';
	    } elsif ($wind_direction < 22.5+45*4) {
		$wind_direction_compass = 'S';
	    } elsif ($wind_direction < 22.5+45*5) {
		$wind_direction_compass = 'SW';
	    } elsif ($wind_direction < 22.5+45*6) {
		$wind_direction_compass = 'W';
	    } elsif ($wind_direction < 22.5+45*7) {
		$wind_direction_compass = 'NW';
	    } else {
		die "Häh wind_direction=$wind_direction ?";
	    }
	}
	$wind_speed = substr($token, 3, 2);
	if ($wind_speed eq "//") {
	    $wind_speed = undef;
	}

	goto PRINT if !next_token();
	if ($token =~ /^00(...)/) {
	    $wind_speed = $1;
	    $token = shift @token;
	}

	die "1 expected, not $token" if $token !~ /^1/;
	$temperature = substr($token,2,3)/10 * (substr($token,1,1)==1 ? -1 : 1) . " °C";

	goto PRINT if !next_token();
	die "2 expected, not $token"  if $token !~ /^2/;

	my $_humidity = substr($token,2,3);
	if ($_humidity ne '//') {
	    my $sign = substr $token, 1,1;
	    if ($sign == 9) {
		$relative_humidity = substr($token,2,3) . " %";
	    } else {
		$dewpoint = substr($token,2,3)/10 * (substr($token,1,1)==1 ? -1 : 1) . " °C";
	    }
	}


	goto PRINT if !next_token();
	die "3 expected, not $token"  if $token !~ /^3/;
	$station_pressure = substr $token, 1, 4;
	if ($station_pressure !~ m|^////|) {
	    if ($station_pressure =~ m|(.*)/$|) {
		$station_pressure = 1000 + $1;
	    } else {
		$station_pressure = 1000 + $station_pressure/10;
	    }
	    $station_pressure-=1000 if $station_pressure>1200;
	    $station_pressure .= " hPa";
	}

	goto PRINT if !next_token();
	die "4 expected, not $token" if $token !~ /^4/;
	my $_seelevel_pressure = substr $token, 1, 4;
	if ($_seelevel_pressure ne '////') {
	    $code = substr $token, 1, 1;
	    if ($code == 1 || $code == 2 || $code == 5 || $code == 7 || $code == 8) {
		die "nyi";
	    } else {
		$seelevel_pressure = substr $token, 1, 4;
		if ($seelevel_pressure =~ m|(.*)/$|) {
		    $seelevel_pressure = 1000 + $1;
		} else {
		    $seelevel_pressure = 1000 + $seelevel_pressure/10;
		}
		$seelevel_pressure-=1000 if $seelevel_pressure>1200;
		$seelevel_pressure .= " hPa";
	    }
	}

	goto PRINT if !next_token();
	die "5 expected, not $token"  if $token !~ /^5/;
	$code = substr $token, 1, 1;
	$characteristics_of_pressure_tendency =
	    {
	     0 => 'Increasing, then decreasing -- resultant pressure same or higher',
	     1 => 'Increasing, then steady -- resultant pressure higher',
	     2 => 'Increasing steadily -- resultant pressure higher',
	     3 => 'Decreasing or steady, then increasing -- resultant pressure higher',
	     4 => 'Steady -- resultant pressure same',
	     5 => 'Decreasing, then increasing -- resultant pressure lower',
	     6 => 'Decreasing, then steady -- resultant pressure lower',
	     7 => 'Decreasing steadily -- resultant pressure lower',
	     8 => 'Increasing or steady, then decreasing -- resultant pressure lower',
	    }->{$code};
	$three_hour_pressure_change = substr($token, 2, 3)/10;

	goto PRINT if !next_token();
	do { warn "6 expected, not $token" if $d; goto TOKEN7 } if !defined $token || $token !~ /^6/;
	($precipitation_amount, $duration_over_which_precipitation_amount_measured) = handle_precipitation_amount($token);

	goto PRINT if !next_token();
    TOKEN7:
	do { warn "7 expected, not $token" if $d; goto TOKEN8 }  if !defined $token || $token !~ /^7/;
	$code = substr $token, 1, 2;
	$present_weather =
	    {
	     '00' => 'clear skies',
	     '01' => 'clouds dissolving',
	     '02' => 'state of sky unchanged',
	     '03' => 'clouds developing',
	     #Haze, smoke, dust or sand
	     '04' => 'visibility reduced by smoke',
	     '05' => 'haze',
	     '06' => 'widespread dust in suspension not raised by wind',
	     '07' => 'dust or sand raised by wind',
	     '08' => 'well developed dust or sand whirls',
	     '09' => 'dust or sand storm within sight but not at station',
	     #Non-precipitation events
	     '10' => 'mist',
	     '11' => 'patches of shallow fog',
	     '12' => 'continuous shallow fog',
	     '13' => 'lightning visible, no thunder heard',
	     '14' => 'precipitation within sight but not hitting ground',
	     '15' => 'distant precipitation but not falling at station',
	     '16' => 'nearby precipitation but not falling at station',
	     '17' => 'thunderstorm but no precipitation falling at station',
	     '18' => 'squalls within sight but no precipitation falling at station',
	     '19' => 'funnel clouds within sight',
	     #Precipitation within past hour but not at observation time
	     '20' => 'drizzle',
	     '21' => 'rain',
	     '22' => 'snow',
	     '23' => 'rain and snow',
	     '24' => 'freezing rain',
	     '25' => 'rain showers',
	     '26' => 'snow showers',
	     '27' => 'hail showers',
	     '28' => 'fog',
	     '29' => 'thunderstorms',
	     #Duststorm, sandstorm, drifting or blowing snow
	     '30' => 'slight to moderate duststorm, decreasing in intensity',
	     '31' => 'slight to moderate duststorm, no change',
	     '32' => 'slight to moderate duststorm, increasing in intensity',
	     '33' => 'severe duststorm, decreasing in intensity',
	     '34' => 'severe duststorm, no change',
	     '35' => 'severe duststorm, increasing in intensity',
	     '36' => 'slight to moderate drifting snow, below eye level',
	     '37' => 'heavy drifting snow, below eye level',
	     '38' => 'slight to moderate drifting snow, above eye level',
	     '39' => 'heavy drifting snow, above eye level',
	     #Fog or ice fog
	     '40' => 'Fog at a distance',
	     '41' => 'patches of fog',
	     '42' => 'fog, sky visible, thinning',
	     '43' => 'fog, sky not visible, thinning',
	     '44' => 'fog, sky visible, no change',
	     '45' => 'fog, sky not visible, no change',
	     '46' => 'fog, sky visible, becoming thicker',
	     '47' => 'fog, sky not visible, becoming thicker',
	     '48' => 'fog, depositing rime, sky visible',
	     '49' => 'fog, depositing rime, sky not visible',
	     #Drizzle
	     '50' => 'intermittent light drizzle',
	     '51' => 'continuous light drizzle',
	     '52' => 'intermittent moderate drizzle',
	     '53' => 'continuous moderate drizzle',
	     '54' => 'intermittent heavy drizzle',
	     '55' => 'continuous heavy drizzle',
	     '56' => 'light freezing drizzle',
	     '57' => 'moderate to heavy freezing drizzle',
	     '58' => 'light drizzle and rain',
	     '59' => 'moderate to heavy drizzle and rain',
	     #Rain
	     '60' => 'intermittent light rain',
	     '61' => 'continuous light rain',
	     '62' => 'intermittent moderate rain',
	     '63' => 'continuous moderate rain',
	     '64' => 'intermittent heavy rain',
	     '65' => 'continuous heavy rain',
	     '66' => 'light freezing rain',
	     '67' => 'moderate to heavy freezing rain',
	     '68' => 'light rain and snow',
	     '69' => 'moderate to heavy rain and snow',
	     #Snow
	     '70' => 'intermittent light snow',
	     '71' => 'continuous light snow',
	     '72' => 'intermittent moderate snow',
	     '73' => 'continuous moderate snow',
	     '74' => 'intermittent heavy snow',
	     '75' => 'continuous heavy snow',
	     '76' => 'diamond dust',
	     '77' => 'snow grains',
	     '78' => 'snow crystals',
	     '79' => 'ice pellets',
	     #Showers
	     '80' => 'light rain showers',
	     '81' => 'moderate to heavy rain showers',
	     '82' => 'violent rain showers',
	     '83' => 'light rain and snow showers',
	     '84' => 'moderate to heavy rain and snow showers',
	     '85' => 'light snow showers',
	     '86' => 'moderate to heavy snow showers',
	     '87' => 'light snow/ice pellet showers',
	     '88' => 'moderate to heavy snow/ice pellet showers',
	     '89' => 'light hail showers',
	     '90' => 'moderate to heavy hail showers',
	     #Thunderstorms
	     '91' => 'thunderstorm in past hour, currently only light rain',
	     '92' => 'thunderstorm in past hour, currently only moderate to heavy rain',
	     '93' => 'thunderstorm in past hour, currently only light snow or rain/snow mix',
	     '94' => 'thunderstorm in past hour, currently only moderate to heavy snow or rain/snow mix',
	     '95' => 'light to moderate thunderstorm',
	     '96' => 'light to moderate thunderstorm with hail',
	     '97' => 'heavy thunderstorm',
	     '98' => 'heavy thunderstorm with duststorm',
	     '99' => 'heavy thunderstorm with hail',
	    }->{$code};
	if ($code < 4) {
	    $present_weather_category = '';
	} elsif ($code < 10) {
	    $present_weather_category = 'Haze, smoke, dust or sand';
	} else {
	    $present_weather_category =
		{
		 1 => 'Non-precipitation events',
		 2 => 'Precipitation within past hour but not at observation time',
		 3 => 'Duststorm, sandstorm, drifting or blowing snow',
		 4 => 'Fog or ice fog',
		 5 => 'Drizzle',
		 6 => 'Rain',
		 7 => 'Snow',
		 8 => 'Showers',
		 9 => 'Thunderstorms',
		}->{substr $code, 0, 1};
	}


	my %past_weather =
	    (
	     '0' => 'cloud covering less than half of sky',
	     '1' => 'cloud covering more than half of sky during part of period and more than half during part of period',
	     '2' => 'cloud covering more than half of sky',
	     '3' => 'sandstorm, duststorm or blowing snow',
	     '4' => 'fog, or thick haze',
	     '5' => 'drizzle',
	     '6' => 'rain',
	     '7' => 'snow or mixed rain and snow',
	     '8' => 'showers',
	     '9' => 'thunderstorms',
	    );

	$past_weather_type1 = $past_weather{substr $token, 3, 1};
	$past_weather_type2 = $past_weather{substr $token, 4, 1};

	goto PRINT if !next_token();
    TOKEN8:
	do { warn "8 expected, not $token" if $d; goto NEXTTOKEN } if !defined $token || $token !~ /^8/;

	$amount_of_low_middle_clouds_covering_sky = substr $token, 1, 1;
	$low_cloud_type =
	    {
	     '0' => 'no low clouds',
	     '1' => 'cumulus humulis or fractus (no vertical development)',
	     '2' => 'cumulus mediocris or congestus (moderate vertical development)',
	     '3' => 'cumulonimbus calvus (no outlines nor anvil)',
	     '4' => 'stratocumulus cumulogenitus (formed by spreading of cumulus)',
	     '5' => 'stratocumulus',
	     '6' => 'stratus nebulosus (continuous sheet)',
	     '7' => 'stratus or cumulus fractus (bad weather)',
	     '8' => 'cumulus and stratocumulus (multilevel)',
	     '9' => 'cumulonimbus with anvil',
	     '/' => 'low clouds unobserved due to darkness or obscuration',
	    }->{substr $token, 2, 1};
	$middle_cloud_type =
	    {
	     '0' => 'no middle clouds',
	     '1' => 'altostratus translucidous (mostly transparent)',
	     '2' => 'altostratus opacus or nimbostratus',
	     '3' => 'altocumulus translucidous (mostly transparent)',
	     '4' => 'patches of altocumulus (irregular, lenticular)',
	     '5' => 'bands of altocumulus',
	     '6' => 'altocumulus cumulogenitus (formed by spreading of cumulus)',
	     '7' => 'altocumulus (multilayers)',
	     '8' => 'altocumulus castellanus (having cumuliform tufts)',
	     '9' => 'altocumulus of a chaotic sky',
	     '/' => 'middle clouds unobserved due to darkness or obscuration',
	    }->{substr $token, 3, 1};

	$high_cloud_type =
	    {
	     '0' => 'no high clouds',
	     '1' => 'cirrus fibratus (wispy)',
	     '2' => 'cirrus spissatus (dense in patches)',
	     '3' => 'cirrus spissatus cumulogenitus (formed out of anvil)',
	     '4' => 'cirrus unicus or fibratus (progressively invading sky)',
	     '5' => 'bands of cirrus or cirrostratus invading sky (less than 45 degree above horizon)',
	     '6' => 'bands of cirrus or cirrostratus invading sky (more than 45 degree above horizon)',
	     '7' => 'cirrostratus covering whole sky',
	     '8' => 'cirrostratus not covering sky but not invading',
	     '9' => 'cirrocumulus ',
	     '/' => 'high clouds unobserved due to darkness or obscuration',
	    }->{substr $token, 4, 1};
    }; warn $@ if $@;

 NEXTTOKEN:
    next_token();

    ######################################################################
 PRINT:
    if ($wettermeldung2) {
	my $ret = print_wettermeldung2_compatible('-print' => $main);
	return $ret;
    } else {
	printall();
    }
}				# doit

sub print_wettermeldung2_compatible {
    my(%args) = @_;
    (my $temperature = $temperature) =~ s/\s*°C//;
    (my $pressure = $seelevel_pressure) =~ s/\s*hPa//;
    require Met::Wind;
    my $wind_beaufort = Met::Wind::wind_velocity([$wind_speed, 'm/s'], 'beaufort');
    local $^W = 0;
    my($date, $time) = gmtime2localtime($date, $time);
    my $ret = join("|",
		   $date,
		   $time,
		   $temperature,
		   $pressure,
		   $wind_direction_compass,
		   $wind_beaufort,
		   "",		# feuchtigkeit
		   "",		# mittl windg
		   $twenty_four_hour_precipitation_in_mm,
		   $present_weather,
		  )."\n";
    print $ret if $args{'-print'};
    $ret;
}

sub printall {
    local $^W = 0;
    print "cloud_base_of_lowest_cloud_seen=$cloud_base_of_lowest_cloud_seen\n";
    print "precipitation_indicator=$precipitation_indicator\n" if defined $precipitation_indicator;
    print "station_type_and_present_and_past_weather_indicator=$station_type_and_present_and_past_weather_indicator\n";
    print "wmo_station_number=$wmo_station_number\n";
    print "visibility=$visibility\n";
    print "total_cloud_cover=$total_cloud_cover\n";
    print "wind_direction=$wind_direction\n";
    print "wind_direction_compass=$wind_direction_compass\n" if defined $wind_direction_compass;
    print "wind_speed=$wind_speed\n" if defined $wind_speed;
    print "temperature=$temperature\n";
    if (defined $dewpoint) {
	print "dewpoint=$dewpoint\n";
    } else {
	print "relative_humidity=$relative_humidity\n";
    }
    print "station_pressure=$station_pressure\n";
    print "seelevel_pressure=$seelevel_pressure\n";
    print "characteristics_of_pressure_tendency=$characteristics_of_pressure_tendency\n";
    print "three_hour_pressure_change=$three_hour_pressure_change\n";
    print "precipitation_amount=$precipitation_amount over $duration_over_which_precipitation_amount_measured\n" if defined $precipitation_amount;
    print "precipitation_amount2=$precipitation_amount2 over $duration_over_which_precipitation_amount_measured2\n" if defined $precipitation_amount2;
    print "present_weather_category=$present_weather_category\n" if defined $present_weather_category;
    print "present_weather=$present_weather\n" if defined $present_weather;
    print "past_weather_type1=$past_weather_type1\n" if defined $past_weather_type1;
    print "past_weather_type2=$past_weather_type2\n" if defined $past_weather_type2;
    print "amount_of_low_middle_clouds_covering_sky=$amount_of_low_middle_clouds_covering_sky\n" if defined $amount_of_low_middle_clouds_covering_sky;
    print "low_cloud_type=$low_cloud_type\n" if defined $low_cloud_type;
    print "middle_cloud_type=$middle_cloud_type\n" if defined $middle_cloud_type;
    print "high_cloud_type=$high_cloud_type\n" if defined $high_cloud_type;
    print "maximum_temperature_over_previous_24_hours=$maximum_temperature_over_previous_24_hours °C\n" if defined $maximum_temperature_over_previous_24_hours;
    print "minimum_temperature_over_previous_24_hours=$minimum_temperature_over_previous_24_hours °C\n" if defined $minimum_temperature_over_previous_24_hours;
    print "state_of_ground_with_snow_cover=$state_of_ground_with_snow_cover\n" if defined $state_of_ground_with_snow_cover;
    print "snow_depth_in_cm=$snow_depth_in_cm\n" if defined $snow_depth_in_cm;
    print "twenty_four_hour_precipitation_in_mm=$twenty_four_hour_precipitation_in_mm\n" if defined $twenty_four_hour_precipitation_in_mm;
    my $cloud_i = 1;
    foreach my $cloud (@clouds) {
	print "cloud_coverage_of_layer $cloud_i=$cloud->{'cloud_coverage_of_layer'}/8\n";
	print "genus_of_cloud=$cloud->{'genus_of_cloud'}\n";
	print "height_of_cloud_base=$cloud->{'height_of_cloud_base'} m\n";
	$cloud_i++;
    }
    print "ground_condition=$ground_condition\n" if defined $ground_condition;
    print "min_temp_5cm=$min_temp_5cm°C\n" if defined $min_temp_5cm;
    print "sunshine_last_hour=$sunshine_last_hour h\n" if defined $sunshine_last_hour;
    print "sunshine_last_day=$sunshine_last_day h\n" if defined $sunshine_last_day;

    print "hoechste_windspitze_in_den_letzten_10_minuten=$hoechste_windspitze_in_den_letzten_10_minuten\n" if defined $hoechste_windspitze_in_den_letzten_10_minuten;
    print "hoechste_windspitze_seit_dem_letzten_synoptischen_haupttermin=$hoechste_windspitze_seit_dem_letzten_synoptischen_haupttermin m/s\n" if defined $hoechste_windspitze_seit_dem_letzten_synoptischen_haupttermin;
    print "hoechstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin=$hoechstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin m/s\n" if defined $hoechstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin;
    print "mittlere_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin=$mittlere_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin m/s\n" if defined $mittlere_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin;
    print "niedrigstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin=$niedrigstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin m/s\n" if defined $niedrigstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin;
    print "windrichtung_in_grad=$windrichtung_in_grad °\n" if defined $windrichtung_in_grad;
    print "zeitpunkt_einer_rechtsdrehenden_aenderung_der_windrichtung=$zeitpunkt_einer_rechtsdrehenden_aenderung_der_windrichtung XXX\n" if defined $zeitpunkt_einer_rechtsdrehenden_aenderung_der_windrichtung;
    print "zeitpunkt_einer_rueckdrehenden_aenderung_der_windrichtung=$zeitpunkt_einer_rueckdrehenden_aenderung_der_windrichtung XXX\n" if defined $zeitpunkt_einer_rueckdrehenden_aenderung_der_windrichtung;
}

sub next_token {
    $token = shift @token;
    if (!defined $token) {
	return undef;
    }
    if ($token eq '333') {
	special_climatology_data();
	undef;
    }
    1;
}

sub special_climatology_data {

    while (next_token()) {
	if ($token =~ /^0/) {
	    # Regionally developed data
	    warn "Unhandled token $token (Regionale Angaben (derzeit nicht benutzt))";
	} elsif ($token =~ /^1(.)(...)/) {
	    my $sign;
	    ($sign, $maximum_temperature_over_previous_24_hours) = ($1, $2);
	    $maximum_temperature_over_previous_24_hours /= 10;
	    if ($sign == 1) {
		$maximum_temperature_over_previous_24_hours *= -1;
	    }
	} elsif ($token =~ /^2(.)(...)/) {
	    my $sign;
	    ($sign, $minimum_temperature_over_previous_24_hours) = ($1, $2);
	    $minimum_temperature_over_previous_24_hours /= 10;
	    if ($sign == 1) {
		$minimum_temperature_over_previous_24_hours *= -1;
	    }
	} elsif ($token =~ /^3(.)(.)(..)/) {
	    # Regionally developed data
	    my($E,$sn,$tg) = ($1,$2,$3);
	    $ground_condition =
		{
		 0 => "trocken",
		 1 => "feucht",
		 2 => "nass",
		 3 => "überflutet",
		 4 => "gefroren",
		 5 => "Glatteis oder Eisglätte (mindestens 50 % des Erdbodens bedeckend)",
		 6 => "loser, trockener Sand -- den Boden nicht vollständig bedeckend",
		 7 => "geschlossene dünne Sandschicht -- den Boden vollständig bedeckend",
		 8 => "geschlossene dicke Sandschicht -- den Boden vollständig bedeckend",
		 9 => "extrem trockener Boden mit Rissen",
		}->{$E};
	    if ($sn ne "/" && $tg ne "//") {
		$min_temp_5cm = $tg * ($sn == 1 ? -1 : +1);
	    }
	} elsif ($token =~ /^4(.)(...)/) {
	    my $E;
	    ($E, $snow_depth_in_cm) = ($1, $2);
	    $state_of_ground_with_snow_cover =
		{
		 0 => 'predominantly covered with ice',
		 1 => 'compact or wet snow covering less than half of ground',
		 2 => 'compact or wet snow covering more than half of ground but not completely covered',
		 3 => 'even layer of compact or wet snow covering entire ground',
		 4 => 'uneven layer of compact or wet snow covering entire ground',
		 5 => 'loose dry snow covering less than half of ground',
		 6 => 'loose dry snow covering more than half of ground but not completely covered',
		 7 => 'even layer of loose dry snow covering entire ground',
		 8 => 'uneven layer of loose dry snow covering entire ground',
		 9 => 'snow covering ground completely with deep drifts',
		}->{$E};
	} elsif ($token =~ /^553(..)/) {
	    if ($1 ne "//") {
		$sunshine_last_hour = $1/10;
	    }
	} elsif ($token =~ /^55(...)/) {
	    if ($1 ne "///") {
		$sunshine_last_day = $1/10;
	    }
	    if ($time ne '6.00') {
		warn "Token 55SSS found, but it's not 06:00 UTC, it's $time!"
	    }
	} elsif ($token =~ /^5/) {
	    my $this_token = $token;
	    next_token();
	    warn "Unhandled tokens $this_token, $token" if $d;
	} elsif ($token =~ /^6/) {
	    ($precipitation_amount2, $duration_over_which_precipitation_amount_measured2) = handle_precipitation_amount($token);
	} elsif ($token =~ /^7(....)/) {
	    if ($1 eq "0000") {
		$twenty_four_hour_precipitation_in_mm = 0;
	    } elsif ($1 eq "9999") {
		$twenty_four_hour_precipitation_in_mm = 0.05; # or less
	    } else {
		$twenty_four_hour_precipitation_in_mm = $1/10;
	    }
	} elsif ($token =~ /^8(.)(.)(..)/) {
	    my($N,$C,$hh) = ($1,$2,$3);
	    my $cloud_layer = {};
	    $cloud_layer->{'cloud_coverage_of_layer'} = $N;
	    $cloud_layer->{'genus_of_cloud'} =
		{
		 '0' => 'cirrus (Ci)',
		 '1' => 'cirrocumulus (Cc)',
		 '2' => 'cirrostratus (Cs)',
		 '3' => 'altocumulus (Ac)',
		 '4' => 'altostratus (As)',
		 '5' => 'nimbostratus (Ns)',
		 '6' => 'stratocumulus (Sc)',
		 '7' => 'stratus (St)',
		 '8' => 'cumulus (Cu)',
		 '9' => 'cumulonimbus (Cb)',
		 '/' => 'cloud not visible',
		}->{$C};
	    if ($hh <= 50) {
		$cloud_layer->{'height_of_cloud_base'} = $hh*30;
	    } elsif ($hh <= 80) {
		$cloud_layer->{'height_of_cloud_base'} = ($hh-50)*300;
	    } elsif ($hh <= 88) {
		$cloud_layer->{'height_of_cloud_base'} = 9000+($hh-80)*1500;
	    } elsif ($hh == 89) {
		$cloud_layer->{'height_of_cloud_base'} = "greater than 21000 m";
	    } else {
		$cloud_layer->{'height_of_cloud_base'} =
		    {
		     '90' => '0 to 50 m',
		     '91' => '50 to 100 m',
		     '92' => '100 to 200 m',
		     '93' => '200 to 300 m',
		     '94' => '300 to 600 m',
		     '95' => '600 to 1000 m',
		     '96' => '1000 to 1500 m',
		     '97' => '1500 to 2000 m',
		     '98' => '2000 to 2500 m',
		     '99' => 'above 2500 m',
		    }->{$hh};
	    }
	    push @clouds, $cloud_layer;
	} elsif ($token =~ /^9/) {
	    besondere_wettererscheinungen($token);
	    #	    warn "Unhandled token $token";
	} else {
	    warn "The token <$token> is unknown!";
	}
    }
}

# XXX funktioniert nicht korrekt, wenn der Monat nicht stimmt!
sub get_date_time {
    my($wmo_header) = @_;
    my($wmo_time) = $wmo_header =~ /\S+\s+\S+\s+(\S+)/;
    my($day,$h,$m) = $wmo_time =~ /^(..)(..)(..)$/;
    if (!defined $day) {
	die "Can't parse synop time $wmo_time";
    }
    # Umrechung GMT => localtime
    my(@g) = gmtime;
    require Time::Local;
    my $gmtime = Time::Local::timegm(0,$m,$h,$day,$g[4],$g[5]);
    my(@l) = localtime $gmtime;
    my $date = sprintf "%02d.%02d.%04d", $l[3], $l[4]+1, $l[5]+1900;
    my $time = sprintf "%d.%02d", $l[2], $l[1];
    ($date, $time);
}

# This uses the "comment" date header (#yyyy-mm-dd ...)
sub get_date_time2 {
    my($comment_header) = @_;
    if ($comment_header =~ /^\#\s*(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})/) {
	my $date = sprintf "%02d.%02d.%04d", $3, $2, $1;
	my $time = sprintf "%d.%02d", $4, $5;
	($date, $time);
    } else {
	warn "Can't parse $comment_header";
    }
}

sub gmtime2localtime {
    my($date, $time) = @_;
    my($d,$m,$y,$H,$M) = split /\./, "$date.$time";
    require Time::Local;
    my $t = Time::Local::timegm(0,$M,$H,$d,$m-1,$y-1900);
    my @l = localtime $t;
    (sprintf("%02d.%02d.%04d", $l[3], $l[4]+1, $l[5]+1900),
     sprintf("%d.%02d", $l[2], $l[1]));
}

sub handle_precipitation_amount {
    my $token = shift;
    my $code = substr $token, 1, 3;
    my $precipitation_amount;
    if ($code >= 991) {
	$precipitation_amount = ($code-990)/10;
    } elsif ($code == 990) {
	$precipitation_amount = 'trace';
    } elsif ($code == 989) {
	$precipitation_amount = '989 or more mm';
    } else {
	$precipitation_amount = int($code);
    }
    $code = substr $token, 4, 1;
    my $duration_over_which_precipitation_amount_measured =
	{
	 1 => '6 hours',
	 2 => '12 hours',
	 3 => '18 hours',
	 4 => '24 hours',
	 5 => '1 hour',
	 6 => '2 hours',
	 7 => '3 hours',
	 8 => '9 hours',
	 9 => '15 hours',
	 '/' => '24 hours',
	}->{$code};
    ($precipitation_amount, $duration_over_which_precipitation_amount_measured);
}

sub besondere_wettererscheinungen {
    my $token = shift;

    if ($token =~ /^910(..)/) {
	# >= 12.5 m/s
	$hoechste_windspitze_in_den_letzten_10_minuten = $1;
    } elsif ($token =~ /^911(..)/) {
	# >= 12.5 m/s
	$hoechste_windspitze_seit_dem_letzten_synoptischen_haupttermin = $1;
    } elsif ($token =~ /^912(..)/) {
	# >= 10.5 m/s
	$hoechstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin = $1;
    } elsif ($token =~ /^913(..)/) {
	$mittlere_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin = $1;
    } elsif ($token =~ /^914(..)/) {
	$niedrigstes_10_minuten_mittel_der_windgeschwindigkeit_seit_dem_letzten_synoptischen_haupttermin = $1;
    } elsif ($token =~ /^915(..)/) {
	$windrichtung_in_grad = $1*10;
    } elsif ($token =~ /^916(..)/) {
	$zeitpunkt_einer_rechtsdrehenden_aenderung_der_windrichtung = $1;
    } elsif ($token =~ /^917(..)/) {
	$zeitpunkt_einer_rueckdrehenden_aenderung_der_windrichtung = $1;
    } else {
	warn "Unhandled token $token";
    }
}

sub convert_whole_file {	# to something wettermeldung2 compatible
    # this is a slow unixish hack
    my($file, $outfile) = @_;
    my $since;
    if (defined $outfile && -w $outfile) {
	my($last) = `tail -1 $outfile`;
	my($d,$m,$y,$H,$M) = $last =~ /^(\d+)\D(\d+)\D(\d+)\D(\d+)\D(\d+)/;
	if (!defined $d) {
	    $since = "";
	} else {
	    $since = sprintf "%04d-%02d-%02d %02d:%02d", $y,$m,$d,$H,$M;
	}
    } else {
	undef $outfile;
    }

    open(G, "grep '^#' $file|");
    if (defined $outfile) {
	open(OUT, ">>$outfile") or die $!;
    } else {
	open(OUT, ">&STDOUT");
    }
    while (<G>) {
	chomp;
	(my $date = $_) =~ s/^.//;
	next if (defined $since && $since ge $date);
	#my $line = `$^X $0 -wettermeldung2 -date '$date' $file`;
	my $line = doit($file, $date, -wettermeldung2 => 1);
	print OUT $line;
    }
    close OUT;
    close G;

    exit 0;
}

__END__
