#!/usr/bin/perl -w
use LWP;
use Text::CSV;
use utf8;
use strict;

sub make_symbol_list($);
sub fetch_quotes($$$);

# Base symbol to convert everything into (units will use the base symbol to
# translate between 2 other symbols.)
my ($basesymbol) = 'USD';

# URI to glue a symbol list onto to fetch our data
my ($baseuri) = "http://download.finance.yahoo.com/d/quotes.csv?f=sl1t1d1&s=";

my (%symbol);
# unit names from units.dat for each ISO currency code
$symbol{'AED'}{'unit'} = 'unitedarabemiratesdirham';
$symbol{'ALL'}{'unit'} = 'albanialek';
$symbol{'ANG'}{'unit'} = 'netherlandsantillesguilder';
$symbol{'AOA'}{'unit'} = 'angolakwanza';
$symbol{'ARS'}{'unit'} = 'argentinapeso';
$symbol{'AUD'}{'unit'} = 'australiadollar';
$symbol{'AWG'}{'unit'} = 'arubaguilder';
$symbol{'BAM'}{'unit'} = 'bosniamarka';
$symbol{'BBD'}{'unit'} = 'barbadosdollar';
$symbol{'BDT'}{'unit'} = 'bangladeshtaka';
$symbol{'BGN'}{'unit'} = 'bulgarialev';
$symbol{'BHD'}{'unit'} = 'bahraindinar';
$symbol{'BIF'}{'unit'} = 'burundifranc';
$symbol{'BMD'}{'unit'} = 'bermudadollar';
$symbol{'BND'}{'unit'} = 'bruneidollar';
$symbol{'BOB'}{'unit'} = 'boliviaboliviano';
$symbol{'BRL'}{'unit'} = 'brazilreal';
$symbol{'BSD'}{'unit'} = 'bahamasdollar';
$symbol{'BTN'}{'unit'} = 'bhutanngultrum';
$symbol{'BYR'}{'unit'} = 'belarusruble';
$symbol{'BZD'}{'unit'} = 'belizedollar';
$symbol{'CAD'}{'unit'} = 'canadadollar';
$symbol{'CHF'}{'unit'} = 'switzerlandfranc';
$symbol{'CLP'}{'unit'} = 'chilepeso';
$symbol{'CNY'}{'unit'} = 'chinayuan';
$symbol{'COP'}{'unit'} = 'colombiapeso';
$symbol{'CRC'}{'unit'} = 'costaricacolon';
$symbol{'CUP'}{'unit'} = 'cubapeso';
$symbol{'CYP'}{'unit'} = 'cypruspound';
$symbol{'CZK'}{'unit'} = 'czechrepublickoruna';
$symbol{'DKK'}{'unit'} = 'denmarkkrone';
$symbol{'DOP'}{'unit'} = 'dominicanrepublicpeso';
$symbol{'DZD'}{'unit'} = 'algeriadinar';
$symbol{'EEK'}{'unit'} = 'estoniakroon';
$symbol{'EGP'}{'unit'} = 'egyptpound';
$symbol{'ETB'}{'unit'} = 'ethiopiabirr';
$symbol{'EUR'}{'unit'} = 'euro';
$symbol{'FJD'}{'unit'} = 'fijidollar';
$symbol{'GBP'}{'unit'} = 'greatbritainpound';
$symbol{'GEL'}{'unit'} = 'georgialari';
$symbol{'GHS'}{'unit'} = 'ghanacedi';
$symbol{'GMD'}{'unit'} = 'gambiadalasi';
$symbol{'GNF'}{'unit'} = 'guineafranc';
$symbol{'GTQ'}{'unit'} = 'guatemalaquetzal';
$symbol{'HKD'}{'unit'} = 'hongkongdollar';
$symbol{'HNL'}{'unit'} = 'honduraslempira';
$symbol{'HRK'}{'unit'} = 'croatiakuna';
$symbol{'HTG'}{'unit'} = 'haitigourde';
$symbol{'HUF'}{'unit'} = 'hungaryforint';
$symbol{'IDR'}{'unit'} = 'indonesiarupiah';
$symbol{'ILS'}{'unit'} = 'israelnewshekel';
$symbol{'INR'}{'unit'} = 'indiarupee';
$symbol{'IQD'}{'unit'} = 'iraqdinar';
$symbol{'IRR'}{'unit'} = 'iranrial';
$symbol{'ISK'}{'unit'} = 'icelandkrona';
$symbol{'JMD'}{'unit'} = 'jamaicadollar';
$symbol{'JOD'}{'unit'} = 'jordandinar';
$symbol{'JPY'}{'unit'} = 'japanyen';
$symbol{'KES'}{'unit'} = 'kenyashilling';
$symbol{'KGS'}{'unit'} = 'kyrgyzstansom';
$symbol{'KMF'}{'unit'} = 'comorosfranc';
$symbol{'KRW'}{'unit'} = 'southkoreawon';
$symbol{'KWD'}{'unit'} = 'kuwaitdinar';
$symbol{'KYD'}{'unit'} = 'caymanislandsdollar';
$symbol{'KZT'}{'unit'} = 'kazakhstantenge';
$symbol{'LBP'}{'unit'} = 'lebanonpound';
$symbol{'LKR'}{'unit'} = 'srilankarupee';
$symbol{'LSL'}{'unit'} = 'lesotholoti';
$symbol{'LTL'}{'unit'} = 'lithuanialitas';
$symbol{'LVL'}{'unit'} = 'latvialat';
$symbol{'MAD'}{'unit'} = 'moroccodirham';
$symbol{'MDL'}{'unit'} = 'moldovaleu';
$symbol{'MGA'}{'unit'} = 'madagascarariary';
$symbol{'MKD'}{'unit'} = 'macedoniadenar';
$symbol{'MNT'}{'unit'} = 'mongoliatughrik';
$symbol{'MOP'}{'unit'} = 'macaupataca';
$symbol{'MRO'}{'unit'} = 'mauritaniaouguiya';
$symbol{'MTL'}{'unit'} = 'maltalira';
$symbol{'MUR'}{'unit'} = 'mauritiusrupee';
$symbol{'MVR'}{'unit'} = 'maldivesrufiyaa';
$symbol{'MWK'}{'unit'} = 'malawikwacha';
$symbol{'MXN'}{'unit'} = 'mexicopeso';
$symbol{'MYR'}{'unit'} = 'malaysiaringgit';
$symbol{'MZN'}{'unit'} = 'mozambiquemetical';
$symbol{'NAD'}{'unit'} = 'namibiadollar';
$symbol{'NGN'}{'unit'} = 'nigerianaira';
$symbol{'NIO'}{'unit'} = 'nicaraguacordoba';
$symbol{'NOK'}{'unit'} = 'norwaykrone';
$symbol{'NPR'}{'unit'} = 'nepalrupee';
$symbol{'NZD'}{'unit'} = 'newzealanddollar';
$symbol{'OMR'}{'unit'} = 'omanrial';
$symbol{'PAB'}{'unit'} = 'panamabalboa';
$symbol{'PEN'}{'unit'} = 'perunuevosol';
$symbol{'PGK'}{'unit'} = 'papuanewguineakina';
$symbol{'PHP'}{'unit'} = 'philippinespeso';
$symbol{'PKR'}{'unit'} = 'pakistanrupee';
$symbol{'PLN'}{'unit'} = 'polandzloty';
$symbol{'PYG'}{'unit'} = 'paraguayguarani';
$symbol{'QAR'}{'unit'} = 'qatarriyal';
$symbol{'RON'}{'unit'} = 'romanianewleu';
$symbol{'RUB'}{'unit'} = 'russiaruble';
$symbol{'RWF'}{'unit'} = 'rwandafranc';
$symbol{'SAR'}{'unit'} = 'saudiarabiariyal';
$symbol{'SBD'}{'unit'} = 'solomonislandsdollar';
$symbol{'SCR'}{'unit'} = 'seychellesrupee';
$symbol{'SEK'}{'unit'} = 'swedenkrona';
$symbol{'SGD'}{'unit'} = 'singaporedollar';
$symbol{'SKK'}{'unit'} = 'slovakiakoruna';
$symbol{'SLL'}{'unit'} = 'sierraleoneleone';
$symbol{'STD'}{'unit'} = 'sãotomeandprincipedobra';
$symbol{'SVC'}{'unit'} = 'elsalvadorcolon';
$symbol{'SZL'}{'unit'} = 'swazilandlilangeni';
$symbol{'THB'}{'unit'} = 'thailandbaht';
$symbol{'TND'}{'unit'} = 'tunisiadinar';
$symbol{'TOP'}{'unit'} = "tongapa'anga";
$symbol{'TRY'}{'unit'} = 'turkeynewlira';
$symbol{'TTD'}{'unit'} = 'trinidadandtobagodollar';
$symbol{'TWD'}{'unit'} = 'taiwannewdollar';
$symbol{'TZS'}{'unit'} = 'tanzaniashilling';
$symbol{'UAH'}{'unit'} = 'ukrainehryvna';
$symbol{'UGX'}{'unit'} = 'ugandashilling';
$symbol{'USD'}{'unit'} = 'unitedstatesdollar';
$symbol{'UYU'}{'unit'} = 'uruguaypeso';
$symbol{'UZS'}{'unit'} = 'uzbekistansom';
$symbol{'VEB'}{'unit'} = 'venezuelabolivar';
$symbol{'VND'}{'unit'} = 'vietnamdong';
$symbol{'VUV'}{'unit'} = 'vanuatuvatu';
$symbol{'WST'}{'unit'} = 'samoatala';
$symbol{'XAF'}{'unit'} = 'equatorialguineacfafrancbeac';
$symbol{'XAG'}{'unit'} = 'silverounce';
$symbol{'XAU'}{'unit'} = 'goldounce';
$symbol{'XCD'}{'unit'} = 'eastcaribbeandollar';
$symbol{'XPD'}{'unit'} = 'palladiumounce';
$symbol{'XPT'}{'unit'} = 'platinumounce';
$symbol{'YER'}{'unit'} = 'yemenrial';
$symbol{'ZAR'}{'unit'} = 'southafricarand';
$symbol{'ZMK'}{'unit'} = 'zambiakwacha';
$symbol{'ZWD'}{'unit'} = 'zimbabwedollar';

$symbol{'XCP'}{'unit'} = 'copperprice';
$symbol{'XCP'}{'inunit'} = 'pound';
$symbol{'XAL'}{'unit'} = 'aluminumprice';
$symbol{'XAL'}{'inunit'} = 'troyounce';
$symbol{'XAU'}{'unit'} = 'goldprice';
$symbol{'XAU'}{'inunit'} = 'troyounce';
$symbol{'XPD'}{'unit'} = 'palladiumprice';
$symbol{'XPD'}{'inunit'} = 'troyounce';
$symbol{'XPT'}{'unit'} = 'platinumprice';
$symbol{'XPT'}{'inunit'} = 'troyounce';
$symbol{'XAG'}{'unit'} = 'silverprice';
$symbol{'XAG'}{'inunit'} = 'troyounce';

my ($lwpua, $csvparser);

$lwpua = LWP::UserAgent->new();
$csvparser = Text::CSV->new();

# Fetch currency conversions in forward order
fetch_quotes($lwpua, $csvparser, make_symbol_list(0));
# ...then get the ones reversed for more precision
fetch_quotes($lwpua, $csvparser, make_symbol_list(1));

binmode(STDOUT, ':utf8');

# print the unit conversions
foreach my $cur (keys %symbol) {
	next if ($cur eq $basesymbol);
	
	printf("# %s to %s, %s\n", $cur, $basesymbol, $symbol{$cur}{'ts'});
	printf("%-40s %s %s%s\n", $symbol{$cur}{'unit'}, $symbol{$cur}{'value'}, $basesymbol, defined($symbol{$cur}{'inunit'}) ? '/' . $symbol{$cur}{'inunit'} : '');
}


sub make_symbol_list($)
{
	my ($reverse_only) = @_;
	my ($cur, $symlist, $comma);
	
	$comma = '';
	
	foreach $cur (keys %symbol) {
		# don't translate our base unit into itself
		next if ($cur eq $basesymbol);

		if (!$reverse_only) {
			# Forward behavior is to translate basesymbol -> otherunit, since the
			# default basesymbol is USD and the USD is higher for most currencies.
			# If you change the base unit, you may want to change the reverse
			# logic, depending on the relative value of your new base currency.
			$symlist .= $comma . $basesymbol . $cur . "=X";
			$comma = ',';
		} elsif ($symbol{$cur}{'reverse'} || 0) {
			# Reverse behavior is to go from otherunit -> basesymbol, and the need
			# to do this is flagged by the parser for values where forward behavior
			# was < 1.  Since Yahoo truncates at very few decimal places, this gets
			# us the most accurate conversion data possible.
			$symlist .= $comma . $cur . $basesymbol . "=X";
			$comma = ',';
		}
	}
	
	return $symlist;
}

sub fetch_quotes($$$)
{
	my ($lwpua, $csvparser, $symbols) = @_;
	my ($response, $line, @lines, @fields);
	my ($symbol, $value, $time, $date, $isocode);
	
	$response = $lwpua->get($baseuri . $symbols);
	if (!$response->is_success) {
		printf("# Failed to fetch symbols: %s\n", $symbols);
		printf("# Response: %s\n", $response->status_line);
		return;
	}

	# split up the 
	@lines = split(/\n/, $response->content);

	foreach $line (@lines) {
		# Text::CSV needs a newline to parse the line
		next if (!$csvparser->parse($line . "\n"));
		
		($symbols, $value, $time, $date) = $csvparser->fields();

		if ($symbols =~ /^$basesymbol(...)=X$/) {
			$isocode = $1;
			$symbol{$isocode}{'value'} = '1|' . $value;
		} elsif ($symbols =~ /^(...)$basesymbol=X$/) {
			$isocode = $1;
			$symbol{$isocode}{'value'} = $value;
		}
		if (($date =~ /^(\d+)\/(\d+)\/(\d+)$/)) {
			$symbol{$isocode}{'ts'} = sprintf("%04d-%02d-%02d %s", $3, $1, $2, $time);
		} elsif ($date =~ /N\/A/) {
			$symbol{$isocode}{'ts'} = $date;
		} else {
			print STDERR "Failed to parse date: $date\n";
		}

		# Flag to convert the currencies in the other direction go get more precision
		$symbol{$isocode}{'reverse'} = 1 if ($value < 1);
	}
}
