%{

#foreach {cloud_code cloud_desc cloud_amt} { \
#    "SKC" "Clear Skies" 0 "CLR" "Clear Skies" 0 "FEW" "Few Clouds" 20 \
#    "SCT" "Scattered Clouds" 40 "BKN" "Broken Clouds" 75 \
#    "OVC" "Overcast" 100} {
#    set cloud_table($cloud_code) [list $cloud_desc $cloud_amt]
#}

;# use this more human-readable format
foreach {cloud_code cloud_desc cloud_amt} { \
    "SKC" "Clear skies" 0 "CLR" "Clear skies" 0 "FEW" "Mostly clear skies" 20 \
    "SCT" "Partly cloudy" 40 "BKN" "Mostly cloudy" 75 \
    "OVC" "Overcast" 100} {
    set cloud_table($cloud_code) [list $cloud_desc $cloud_amt]
}
        
foreach {condition_code condition_desc} { \
    "-" "Light" "+" "Heavy" "VC" "Nearby" \
    "MI" "Shallow" "BC" "Patches" "BL" "Blowing" "TS" "Thunderstorms" \
    "PR" "Partial" "DR" "Low Drifting" "SH" "Showers" "FZ" "Freezing" \
    "RA" "Rain" "SN" "Snow" "IC" "Ice Crystals" "GR" "Hail" \
    "DZ" "Drizzle" "SG" "Snow Grains" "PE" "Ice Pellets" "GS" "Small Hail" \
    "UP" "Unknown Precipitation" \
    "FG" "Fog" "BR" "Mist" "DU" "Widespread Dust" "SA" "Sand" \
    "VA" "Volcanic Ash" "HZ" "Haze" "FU" "Smoke" "PY" "Spray" \
    "SQ" "Squall" "DS" "Duststorm" "FC" "Funnel Cloud" \
    "PO" "Dust/Sand Whirls" "SS" "Sandstorm"} {
        set condition_table($condition_code) $condition_desc
}

      
%}

%x TIME WIND VIS CONDS PRES
%GLOBAL yylval
%NOHEADERS

clouds  SKC|CLR|BKN|SCT|FEW|OVC

%%

<INITIAL>\S+                       BEGIN TIME;   ;# ignore the location
<TIME>\S+Z                         decode_time $yytext; BEGIN WIND
<TIME>\S                           unput $yytext; BEGIN WIND
<WIND>AUTO                         ;# ignore AUTO readings
<WIND>COR                          ;# also ignore
<WIND>\S+                          decode_wind $yytext; BEGIN VIS
<VIS>\S+V\S+                       ;# ignore the extra wind information
<VIS>(\d+( [MP]?\d+\/\S+)?)|(\S+)  decode_visibility $yytext; BEGIN CONDS
<CONDS>R\S+                        ;# ignore runway visibility
<CONDS>{clouds}(\d{3}(\S+)?)?      decode_cloud $yytext
<CONDS>\S+\/\S+                    decode_temperature $yytext; BEGIN PRES
<CONDS>\S+                         decode_conditions $yytext
<PRES>\S+                     decode_pressure $yytext; BEGIN INITIAL; return 0
<*>\s+                                  ;# ignore

%%

proc YY_INPUT {buf result max_size} {
    global yyin
    upvar $result ret_val
    upvar $buf new_data
    set ret_val $max_size
    if {$ret_val > [string length $yyin]} {
        set ret_val [string length $yyin]
    }
    set new_data [string range $yyin 0 [expr $ret_val - 1]]
    set yyin [string range $yyin $ret_val end]
}

proc parse_weather {observation_string target_loc} {
    global yyin yylval tw

    YY_FLUSH_BUFFER
    set yyin $observation_string
    upvar $target_loc target_varname
    if {$tw(print_obsdata)} {
        puts $observation_string
    }
    while {[set token [yylex]] != 0} {}
}

proc decode_time {time_string} {
    upvar 2 target_varname target
    regexp {^(\d{2})(\d{2})(\d{2})} $time_string foo \
            month_day hour min
    if {[info exists hour] && [info exists min]} {
        set target(time,hour) $hour
        set target(time,min) $min
    }
}


proc decode_wind {wind_string} {
    upvar 2 target_varname target
    if {[regexp {^(\d{3}|VRB)(\d{2,3})(G\d+)?} $wind_string foo \
            direction speed gust] == 0}  {
        set target(wind,dir) ""
    }
    if {[string equal $direction "VRB"]} {
        set target(wind,dir) -1
    } else {
        set target(wind,dir) [strip_zeros $direction]
    }
    set target(wind,speed) [knots_to_kph [strip_zeros $speed]]
    if {[string equal $gust ""]} {
        set target(wind,gust) "0"
    } else {
        set target(wind,gust) \
                [knots_to_kph [strip_zeros [string range $gust 1 end]]]
    }
}


proc decode_visibility {visibility_string} {
    upvar 2 target_varname target
    ;# special case of a mixed fraction
    if {[regexp {(\d)+ (M?)(\d)+\/(\d+)} $visibility_string foo \
            int_vis minus_vis divisor_vis dividend_vis] == 0} {
        regexp {(\d+)|(M?)(\d)+\/(\d+)} $visibility_string foo \
                int_vis minus_vis divisor_vis dividend_vis
    }
    if {![info exists int_vis]} {
        set visibility 9999
    } elseif {[string equal $int_vis ""]} {
        set visibility 0
    } else {
        set visibility [strip_zeros $int_vis]
    }
    if {[info exists minus_vis] && [string equal $minus_vis "M"]} {
        set visibility 0.001
    } elseif {[info exists divisor_vis] && ![string equal $divisor_vis ""]} {
        set visibility [expr $visibility + \
            [strip_zeros $divisor_vis] / [strip_zeros $dividend_vis]]
    }
    if {[string equal [string range $visibility_string end-1 end] "SM"]} {
        set visibility [sm_to_km $visibility]
    } else {
        ;# assume that units are in meters
        set visibility [m_to_km $visibility]
    }
    set target(vis) $visibility
}

proc decode_cloud {cloud_string} {
    global cloud_table
    upvar 2 target_varname target
    regexp {(SKC|CLR|BKN|SCT|FEW|OVC)(\d{3})?} $cloud_string foo \
        cloud_code cloud_altitude
    if {![info exists cloud_altitude] || [string equal $cloud_altitude ""]} {
        set cloud_altitude 0
    } else {
        set cloud_altitude \
                [feet_to_m [expr [strip_zeros $cloud_altitude] * 100]]
    }
    if {[string equal $cloud_code "SKC"]} {
        set cloud_code "CLR"
    }
    set cloud_type [lindex $cloud_table($cloud_code) 0]
    set cloud_amount [lindex $cloud_table($cloud_code) 1]
    ;# overwrite the old cloud setting if this entry has more clouds
    if {![info exists target(cloud,amt)] || \
            $target(cloud,amt) < $cloud_amount} {
        set target(cloud,type) $cloud_type
        set target(cloud,alt) $cloud_altitude
        set target(cloud,amt) $cloud_amount
    }
}

proc decode_temperature {temperature_string} {
    upvar 2 target_varname target
    regexp {(M?)(\d{2})\/(M?)(\d{2})} $temperature_string foo \
            sign_air_temp air_temp sign_dew_point dew_point
    if {[info exists sign_air_temp] && [string equal $sign_air_temp "M"]} {
        set air_temp [expr -1 * [strip_zeros $air_temp]]
    } else {
        set air_temp [strip_zeros $air_temp]
    }
    set target(temp,air) $air_temp
    if {[info exists sign_air_temp] && [string equal $sign_dew_point "M"]} {
        set dew_point [expr -1 * [strip_zeros $dew_point]]
    } else {
        set dew_point [strip_zeros $dew_point]
    }
    set target(temp,dew) $dew_point
    ;# calculate the relative humidity
    set sat [expr 6.11 * pow(10, 7.5 * $air_temp / (237.7 + $air_temp))]
    set surf [expr 6.11 * pow(10, 7.5 * $dew_point / (237.7 + $dew_point))]
    set rel_humidity [expr $surf / $sat * 100.0]
    set target(temp,relhum) $rel_humidity
}

proc decode_pressure {pressure_string} {
    upvar 2 target_varname target
    regexp {(A|Q)(\d{4})} $pressure_string foo \
            measurement_type measurement_value
    if {[string equal $measurement_type "A"]} {
        set pressure [inch_to_cm \
            [expr [strip_zeros $measurement_value] / 100.0]]
    } else {
        set pressure [mbar_to_cm [expr [strip_zeros $measurement_value]]]
    }
    set pressure [expr $pressure * 10]
    set target(pres) $pressure
}

proc decode_conditions {conditions_string} {
    upvar 2 target_varname target
    global condition_table
    set condition ""
    while {[string length $conditions_string] > 0} {
      regexp -lineanchor -- {^(-|\+|VC|MI|BC|PR|TS|BL|SH|DR|FZ|DZ|RA|SN|SG|IC|PE|GR|GS|UP|BR|FG|FU|VA|SA|HZ|PY|DU|SQ|SS|DS|PO|FC|.)(.*)} \
              $conditions_string foo cond_type conditions_string
        if {[info exists cond_type] && \
                [info exists condition_table($cond_type)]} {
            append condition "$condition_table($cond_type) "
        }
    }
    lappend target(cond) [string trim $condition]
}

proc strip_zeros {s} {
    while {[string equal [string index $s 0] "0"] && \
            [string is digit -strict [string index $s 1]]} {
        set s [string range $s 1 end]
    }
    return $s
}
