#!/usr/bin/perl
# Filename:     yp
# Author:       David Ljung
# Description:  Does a lookup at Yahoo
# Requires:	GET (lwp-request)
use strict;

##################################################
# Setup the variables
##################################################
my $PROGNAME = $0;
   $PROGNAME =~ s|.*/||;

#my $YP_HOST="yp.uswest.com";
#my $YP_PORT=80;
#my $DOC="/cgi/search.fcg";
my $YP_HOST="yp.yahoo.com";
my $YP_PORT=80;
my $DOC="/py/ypResults.py";

# Starting address
my $DEFAULT_ADDR="1553 Fulton Street";
my $DEFAULT_CITY="San Francisco";
my $DEFAULT_STATE="CA";

# Actions
my $BROWSER	= "browser";
my $DIALER	= "d";

##################################################
# Usage
##################################################
sub usage {
  my $msg;
  foreach $msg (@_) { print "ERROR:  $msg\n"; }
  print "\n";
  print "Usage:\t$PROGNAME [-d] [-v] [-c city] [-s state] <business>\n";
  print "\tDoes an online Yellow Pages lookup\n";
  print "\t-more\tGet more listings after the first page\n";
  print "\t-a\tChoose starting address [Default: $DEFAULT_ADDR]\n";
  print "\t-c\tChoose starting city [Default: $DEFAULT_CITY]\n";
  print "\t-s\tChoose starting state [Default: $DEFAULT_STATE]\n";
  print "\t-v\tVerbose (output address)\n";
  print "\t-b\tStart up query in a browser\n";
  print "\t-d\tDial menu [using: $DIALER]\n";
  print "\t-D\tSet debug mode\n";
  print "\n";
  exit -1;
}

sub parse_args {
  my ($name,$addr,$city,$state);
  while ($#ARGV>=0) {
    $a=shift(@ARGV);
    if ($a =~ /^-h$/) { &usage; }
    if ($a =~ /^-D$/) { $MAIN::DEBUG=1; next; }
    if ($a =~ /^-d$/) { $MAIN::DIAL=1; next; }
    if ($a =~ /^-more$/) { $MAIN::MORE_LISTINGS=1; next; }
    if ($a =~ /^-v$/) { $MAIN::VERBOSE=1; next; }
    if ($a =~ /^-a$/) { $addr=shift(@ARGV); next; }
    if ($a =~ /^-c$/) { $city=shift(@ARGV); next; }
    if ($a =~ /^-s$/) { $state=shift(@ARGV); next; }
    if ($a =~ /^-b(rowser)?$/) { $MAIN::BROWSER=1; next; }
    if ($a =~ /^-/) { &usage("Unknown option: $a"); }
    $name=($name ? "$name " : "" ) . $a;
  }
  usage("You need to specify a name to lookup") if (!defined($name));
  $addr=$DEFAULT_ADDR unless ($addr || $city);
  $city=$city || $DEFAULT_CITY;
  $state=$state || $DEFAULT_STATE;
  $city="san francisco" if (lc($city) eq "sf");
  ($name,$addr,$city,$state);
}

##################################################
# Dialing code
##################################################
sub menu {
  my (@menu) = @_;
  for(my $i=0; $i<=$#menu; $i++) {
    printf "%0.2d] ",$i+1;
    show_row(@{$menu[$i]});
  }
  my $ans=-1;
  print STDERR "\n";
  do {
    print STDERR "Choose: ";
    $ans = scalar <>;
    chomp $ans;
  } until ($ans>0 && $ans<=$#menu+1);
  $ans-1;
}

##################################################
# YP stuff
##################################################

sub prep_url {
  $_[0] =~ s/ /+/g;
  $_[0];
}

sub query {
  my ($name,$addr,$city,$state) = @_;

  my $query = $MAIN::BROWSER ? "stp=a" : "stp=n";

  $query.="&stx=".prep_url($name) if ($name);
  $query.="&addr=".prep_url($addr) if ($addr);
  $query.="&city=".prep_url($city) if ($city);
  $query.="&state=".prep_url($state) if ($state);

  print "QUERY: $query\n" if ($MAIN::DEBUG);
  $query;
}

sub submit {
  my ($query) = @_;
  if ($MAIN::BROWSER) {
    $query=~s/&/\\&/g;
#    print "Command: $BROWSER http://$YP_HOST:$YP_PORT${DOC}?$query\n";
    system("browser http://$YP_HOST:$YP_PORT${DOC}?$query");
    exit;
  }
  # Yahoo doesn't allow GET
#  open(HTML,"GET 'http://$YP_HOST:$YP_PORT${DOC}?$query' |")
#    || die("Couldn't GET to $YP_HOST\n");
  open(HTML,"lynx -source 'http://$YP_HOST:$YP_PORT${DOC}?$query' |")
    || die("Couldn't lynx -source to $YP_HOST\n");
  my @text=<HTML>;
  close(HTML);
  print "START RESPONSE:\n\n@text\n\nEND RESPONSE\n" if ($MAIN::DEBUG);
  chomp(@text);
  @text;
}

# Listings are in table after:
# <!-- ACTUAL RECORDS  -->
# Table format:  (garbage), name, address, city, phone, (garbage), (distance)
sub parse_text {
  my(@text)=@_;
  my %FOUND;

  # Find start of listings
  my $i;
  for($i=0;$i<=$#text;$i++) {
#    print "Query didn't hold for some reason.\n"
#      if ($text[$i] =~ /fields are required/i);
    last if ($text[$i] =~ /ACTUAL RECORDS/i);
    die("Invalid search Criteria?\n") if ($text[$i] =~ /Invalid Search Criteria/);
    die("Couldn't find it\n") if ($text[$i] =~ /no .*found/);
  }
  die("Couldn't find start line [ACTUAL RECORDS]\n") if ($i>$#text);
  $i++;

  # Get listings
  my (@rows, @row);
  while($i<=$#text && !($text[$i] =~ /DISTANCE FOOTNOTE/i)) {

    if ($text[$i] =~ m|<td>(.+)</td>|i) {
      my $td = $1;
      $td =~ s/<[^>]+>//g;
      $td =~ s/&nbsp;//g;
      push(@row,$td);
    } elsif ($text[$i] =~ /<td[^>]*>(.*)/) {
      my $line = $1;
      do {
        $line =~ s/<[^>]+>//g;
        $line =~ s/&nbsp;//g;
        $line =~ s/^\s*//g;  $line =~ s/\s*$//g;
        push(@row,$line) if $line;
        $line = $text[$i+1];
        last if $line =~ /DISTANCE FOOTNOTE/;
      } until $text[++$i] =~ m|</td>|;
    }
#print "$text[$i]\n";

    if ($text[$i] =~ m|</tr>| && @row) {
      my ($name,$phone,$addr,$city,$map,$dist) = @row;
      # Don't print multiple copies of the same entry/phone
      unless ($FOUND{$phone}++) {
        if (defined $dist) {
          my @copy = @row;
          push(@rows, \@copy);
        } else {
          print STDERR "[$PROGNAME] Incomplete table row [$i]: @row\n";
        }
      }

      # Clear the row
      undef @row;
    }

    $i++;

  }
  #die("Couldn't find end line\n") if ($i>$#text);
  return @rows if ($i>$#text);

  # Check for more listings problem
  while ($i<=$#text) {
    if ($text[$i] =~ /\<a href=\".*\?(.*)\"\>NEXT MATCHES/i) {
      return print STDERR "--There were more listings.  To see them, use the '-more' option.\n"
        unless ($MAIN::MORE_LISTINGS);

      print STDERR "--Getting more listings...\n";
      my @text=submit($1);
      push(@rows, parse_text(@text));
    }
    $i++;
  }

  return @rows;
}

sub handle_rows {
  my (@rows) = @_;
  return map (show_row(@$_), @rows) unless $MAIN::DIAL;

  my $pick = menu(@rows);
  my $num = $rows[$pick][1];
  $num =~ s/[\(\)]//g;
  print "Dialing: $num\n";
  exec($DIALER,$num);
}

sub show_row {
  my ($name,$phone,$addr,$city,$map,$dist) = @_;

  $MAIN::VERBOSE ?
    print "$name $phone\n$addr $city\n$dist miles\n\n" :
    printf "%-40s $phone\n",$name;
}

##################################################
# Main code
##################################################
handle_rows(parse_text(submit(query(parse_args()))));
