#!/usr/bin/perl -w
#
# Stock Quote Module
#
# Currently actually a Yahoo Stock Quote Module; if I feel the urge I
# may build a module tree instead. Based on previous Stock Exchange
# efforts.
#
# Waider 07/06/2003
package Stock;

use strict;

use LWP::UserAgent;
use HTML::TokeParser;
use Date::Parse;

my $CGISITE = 'http://finance.yahoo.com';
my $CGIROOT = $CGISITE . '/q?s=';
my $ua;

# XXX this only covers the ones I'm interested in
my %currencies = (
   'L' => 'GBP',
   'OL' => 'NOK',
  );

sub new {
  my $self = shift;

  my %options = @_;

  my $ua = new LWP::UserAgent;
  $ua->agent( "GeekToy/0.1 " . $ua->agent );
  $ua->env_proxy();

  my $obj = bless {}, $self;

  $obj->{'ua'} = $ua;

  $obj;
}

sub quote {
  my $self = shift;
  my ( $ticker, $exchange ) = ( $self->{'ticker'}, $self->{'exchange'});
  my ( $date, $value, $currency );
  $ticker ||= shift;
  $exchange ||= shift;
  $exchange ||= "";

  die "No ticker specified" unless $ticker;

  if ( defined( $exchange ) and defined( $currencies{$exchange})) {
    $currency = $currencies{$exchange};
  } else {
    $currency = "USD";
  }

  $exchange = ".$exchange" if $exchange;

  my $url = $CGIROOT . $ticker . $exchange;
  $ENV{DEBUG} and print STDERR "fetching $url...\n";
 grabpage:
  my $req = new HTTP::Request
    GET => $url;

  my $res = $self->{ua}->request( $req );

  if ( $res->is_success ) {
    my $content = $res->content;

    if ( $content =~
         /is no longer valid. It has changed to <a href="([^"]+)"/ ) {
        my $newurl = $CGISITE . $1 . "&s=";
        die "loop! " if $newurl eq $url;
        $url = $newurl;
        goto grabpage;
    }

    if ( $content =~
         /Invalid Ticker Symbol/ ) {
        die "$ticker is not a valid symbol for $exchange\n";
    }

    my $p = new HTML::TokeParser( \$content );
    my $foundit = 0;
    while ( my $token = $p->get_tag ) {
      #   last if $p->get_trimmed_text eq "$ticker$exchange";
      # 2003 09 05
        if ( $p->get_trimmed_text eq "Last Trade:" ) {
            $foundit = 1;
            $p->get_tag( "b" );
            $value = $p->get_trimmed_text( "/b" );
            $p->get_tag( "tr" );
            $p->get_tag( "th", "td" ); # Trade Time:
            if (( my $caption = $p->get_trimmed_text ) ne "Trade Time:" ) {
                if ( $ENV{DEBUG} and open( DUMP, ">$ticker.$exchange.html" )) {
                    print DUMP $content;
                }
                warn "Page format ($url) changed, found $caption\nStopped";
            }

            # date
            $p->get_tag( "td" );
            $date = $p->get_trimmed_text;

            last;
        }
        next if $token->[0] =~ /^\//;
        if ( $token->[1]{'id'} eq sprintf('yfs_l84_%s', lc($ticker))) {
            $value = $p->get_text("/span");
            $ENV{DEBUG} && print STDERR "Found value '$value'\n";
            while( $token = $p->get_tag('span')) {
                if ( $token->[1]{'id'} eq sprintf('yfs_t53_%s', lc($ticker))) {
                    $date = $p->get_trimmed_text('/span');
                    $foundit = 1;
                    last;
                }
            }
        }
        last if $foundit;
    }

    die "Failed to locate sentinel in $url" unless $foundit;

    # value
    print STDERR "price: $currency $value\n" if $ENV{DEBUG};

    # unix-time the date
    my $udate = str2time( $date );
    $udate ||= time;
    # if it's just a day spec then tweak it
    if ( $date =~ /jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/i ) {
      $udate += 60 * 60 * 24;
    }
    $date = $udate;
  } else {
    # HTML retrieve failed
    return;
  }

  return { 'date' => $date, 'value' => $value, 'currency' => $currency };
}

1;
