#!/usr/local/bin/perl -s

# NWS -- retrieve the National Weather Service forecast for
# a city, state, Canadian region, or for the USA as a whole.

# Rob Seaman (seaman@noao.edu), NOAO, Tucson, AZ, 7/2/92.

# The National Weather Service data is made available courtesy of
# the NSF-funded UNIDATA Project and the University of Michigan.

# command line switches are implemented by setting boolean variables:
# $help = 1;			# or specify -help on command line
# $debug = 1;			# or specify -debug on command line

$HOST = "downwind.sprl.umich.edu";      # = 141.212.196.177
$PORT = "3000";

$CODE = ($ARGV[0] || "tus");	# Tucson, AZ is the default
$CODE =~ tr/A-Z/a-z/;

if ($CODE =~ /^usa$/ || $CODE =~ /^us$/) {
    $CLASS = "USA";
} elsif ($CODE =~ /[0-9]+/) {
    $CLASS = "CANADA";
} elsif (length ($CODE) == 2) {
    $CLASS = "STATE";
} else {
    $CLASS = "CITY";
}

$CLASS .= "_HELP" if ($help);

# There should be help available for the individual city codes,
# but since there isn't, we use that fact to simplify the logic.
if ($CLASS eq "CITY_HELP" || $CODE =~ /^help$/) {
    &printhelp ();
    exit (0);
}

&getsocket (SOCKET, $HOST, $PORT);

# $ECHO, $MENU, & $LINE are zeroed by default
while (read (SOCKET, $char, 1)) {

    if ($char eq "\r") {

	next;

    } elsif ($char eq "\n") {

	if ($line =~
	    /The Weather Underground is fully loaded. Try again later./) {

	    &abort ("", "");

	} elsif ($line =~ /Invalid 3-letter city code./ ||
		 $line =~ /Invalid 2-letter state ID./ ||
		 $line =~ /Not a valid option. Type a number 1 to 20./) {

	    &abort ($CODE, "X");

	} elsif ($line =~ /WEATHER UNDERGROUND MAIN MENU/ ||
	    $line =~ /CITY FORECAST MENU/ ||
	    $line =~ /CURRENT WEATHER MENU/ ||
	   ($line =~ /CANADIAN FORECASTS/ && $CLASS ne "CANADA_HELP") ||
	    $line =~ /M\) Return to main menu/) {

	    $ECHO = 0;

	} 

	# -debug doesn't try to be complete, just to give some context
	if ($ECHO || $debug) {
	    if ($LINE++ == 4) {
		open (STDOUT, "| tr 'A-Z' 'a-z'") if ($CLASS !~ /_HELP/);
	    }
	    print $line, "\n";
	}

	$line = "";

    } elsif ($char eq ":") {

	if ($line =~ /Selection/) {

	    if ($CLASS eq "USA") {
		if ($MENU == 0) {
		    print SOCKET "9\n";
		    $ECHO = 1;
		} else {
		    print SOCKET "X\n";
		}

	    } elsif ($CLASS eq "CANADA") {
		if ($MENU == 0){
		    print SOCKET "2\n";
		} elsif ($MENU == 1) {
		    print SOCKET $CODE, "\n";
		    $ECHO = 1;
		} else {
		    print SOCKET "X\n";
		} 

	    } elsif ($CLASS eq "USA_HELP") {
		if ($MENU == 0){
		    print SOCKET "1\n";
		} elsif ($MENU == 1) {
		    print SOCKET "3\n";
		    $ECHO = 1;
		} else {
		    print SOCKET "X\n";
		} 

	    } elsif ($CLASS eq "CANADA_HELP") {
		if ($MENU == 0){
		    print SOCKET "2\n";
		    $ECHO = 1;
		} else {
		    print "\n";
		    print SOCKET "X\n";
		} 

	    } elsif ($CLASS eq "STATE_HELP") {
		if ($MENU == 0){
		    print SOCKET "1\n";
		} elsif ($MENU == 1) {
		    print SOCKET $CODE, "\n";
		    $ECHO = 1;
		} else {
		    print SOCKET "X\n";
		} 

	    } else {
		print SOCKET "X\n";

	    }

	    $MENU++;

	} elsif ($line =~
	    /Press Return for menu, or enter 3 letter forecast city code/) {

	    if ($CLASS eq "CITY" || $CLASS eq "STATE") {
		print SOCKET $CODE, "+\n";
		$ECHO = 1;
	    } else {
		print SOCKET "\n";
	    }

	# e.g. `/Press return to display watch, M to display main menu/'
	# or   `/Press return to display statement, M to display main menu/'
	} elsif ($line =~ /M to display main menu/) {

	    print SOCKET "M\n";

	# e.g. `/Press Return to display statement, M for menu/'
	} elsif ($line =~ /M for menu/) {

	    print SOCKET "\n";

	# also `/Press Return to continue printing or M to return to menu/'
	} elsif ($line =~ /Press Return to continue/ ||
		 $line =~ /Press Return to display menu/) {

	    print SOCKET "\n";

	} else {
	    $line .= $char;
	    next;

	}

	read (SOCKET, $char, 1) until ($char eq "\n");
	$line = "";

    } else {

	$line .= $char;

    }

}

close (SOCKET);
exit (0);


# &GETSOCKET -- open a socket to the specified host and port.
# The new socket's filehandle is also returned implicitly.

sub getsocket {
    local ($socket, $host, $port) = @_;

    local (@addr) = unpack ("C4", (gethostbyname($host))[4]);
    local ($server) = pack ("Snc4x8", 2, $port, @addr);

    die  "socket: $!\n" if (!socket ($socket, 2, 1, 6));
    die "connect: $!\n" if (!connect ($socket, $server));

    # unbuffer the socket and restore the previously selected filehandle
    local ($old_socket) = select ($socket); $| = 1; select ($old_socket);
}


# &PRINTHELP -- print the help information.

sub printhelp {
    print <<EOF;

    Get a National Weather Service forecast from the Weather Underground.
      Type `telnet downwind.sprl.umich.edu 3000' for a direct connection.
      You may be told to try again later if the connection is overloaded.

    Usage:     nws [-help] [-debug] <code>

		    -help   print help information
		   -debug   echo more of the chatter

		    <code>  the 3 letter city code, 2 letter state code,
			    Canadian zone number, or `usa' for a summary

    Examples:  Get the forecast for Tucson      nws tus (or simply `nws')
	       Current conditions for Arizona   nws az
	       Get the forecast for the Yukon   nws 18
	       National weather roundup         nws usa

	       List US state codes              nws -help usa
	       List Arizona city codes          nws -help az
	       List Canadian codes              nws -help 1
	       Print this information           nws -help

    The National Weather Service data is made available courtesy of
    the NSF-funded UNIDATA Project and the University of Michigan.

EOF
}


# &ABORT -- abort with a message after cleaning up.

sub abort {
    local ($prefix, $cmd) = @_;

    $line =~ s/^\s*//;		# trim leading whitespace

    print "\n";
    print $prefix, ":  " if ($prefix);
    print $line, "\n";
    print "Type `nws -help' for more information.\n\n";

    print SOCKET $cmd, "\n" if ($cmd);
    while (read (SOCKET, $char, 1)) {}
    close (SOCKET);
    exit (1);
}
