#!/usr/bin/env perl

use warnings;
use strict;

use JSON;
use Data::Dumper;

use POE qw(Component::Client::TCP Filter::Stream);

# Load HTTP::Request for forming the initial request.
use HTTP::Request;

# Create a JSON object
my $json = new JSON;

# A very simple 'read only' mtgox streaming api client
# This is for those that want to do there own ticker or just watch prices fly by
# on MTGox without implementing a full SocketIO or WebSocket client in POE,
# It should be noted that though this works it is not exactly and elegant way to
# handle websockets.

# Follow the comments in the code for what is going on its pretty self
# explanatory :)

# Author: Paul G Webster
# Email: 'daemon' <daemon@cpan.org>
# IRC: irc.perl.org#poe irc.freenode.net#perl

POE::Component::Client::TCP->new(
  RemoteAddress => "websocket.mtgox.com",
  RemotePort    => 80,
  Filter        => POE::Filter::Stream->new(),
  Connected     => sub {
    my $request = HTTP::Request->new(GET => '/mtgox');
    $request->protocol('HTTP/1.1');
    $request->header(
      Upgrade               => 'WebSocket',
      Connection            => 'Upgrade',
      Host                  => 'websocket.mtgox.com',
      Origin                => 'http://websocket.mtgox.com',
      Sec_WebSocket_Key     => '+HCD64gytTSbCbBTdvduPw==',
      Sec_WebSocket_Version => 13,
    );
    $_[HEAP]->{server}->put($request->as_string);
  },
  ServerInput => sub {
    my ($kernel, $heap, $input) = @_[KERNEL, HEAP, ARG0];

    # if no rcvbuf set, set it to an empty string.
    $heap->{rcvbuf} = "" if (!defined $heap->{rcvbuf});

    # This is a stream (POE::Filter::Stream) we may or may not get a
    # complete response in one go, so lets read whatever we get into
    # a buffer.
    $heap->{rcvbuf} .= $input;

    # Return from processing if there is nothing in the buffer
    return if (!$heap->{rcvbuf});

    # Ok all requests start with {"channel" so lets match upto that
    # per request...
    # This regex says ... match from the start of the buffer to
    # {"channel upto the next occurance of {"channel, the .{4}
    # is because in the stream there is a seperator between each
    # block of data that is exactly 4 characters wide.
    my ($request, $rest) =
      $heap->{rcvbuf} =~ m#^.*?({"channel".*?).{4}({"channel".*)#s;

    # Above we not only matched the packet but the start of the next
    # packet.
    # We really should have BOTH so lets check for the second match
    # $rest if it does not exist return and stop processing till we
    # have more data in the buffer.
    return if (!$rest);

    # We now have our request in $request, so lets overwrite our
    # buffer with what was left $rest
    ($heap->{rcvbuf}) = $rest;

    # $request should be a complete JSON object encoded in UTF8, so
    # lets unencode it to a perl hash.
    $request = $json->utf8->decode($request);

    # Extract the channel handler (This is a mandatory key)
    my $handler = $request->{channel_name};

    # Convert the .'s to _'s (more POE compatible)
    $handler =~ s#\.#_#g;

    # We could now post it off to that handler in this session
    print "Posting to: $handler\n";
    $kernel->yield($handler, $request);
  },
  InlineStates => {
    ticker_BTCUSD => sub {
      my ($input) = $_[ARG0];
      warn "Got: " . Dumper($input);
    },
  },
);

POE::Kernel->run();
exit;