#!/usr/bin/env python
import sys
import os
import atexit
import time
import grequests
import json
from argparse import ArgumentParser

# exchanges as (url, id, response_indices_last, currency)
exchanges = {
    'https://btc-e.com/api/3/ticker/btc_usd':
        ('btce', ('btc_usd', 'last'), 'USD'),
    'https://api.gdax.com/products/BTC-USD/ticker':
        ('coinbase', ('price',), 'USD'),
    'https://www.bitstamp.net/api/v2/ticker/btcusd/':
        ('bitstamp', ('last',), 'USD'),
    'https://api.bitfinex.com/v1/pubticker/btcusd':
        ('bitfinex', ('last_price',), 'USD'),
    'https://data.btcchina.com/data/ticker?market=btccny':
        ('btcchina', ('ticker', 'last'), 'CNY'),
    'http://api.huobi.com/staticmarket/ticker_btc_json.js':
        ('huobi', ('ticker', 'last'), 'CNY'),
    'https://www.okcoin.com/api/v1/ticker.do?symbol=btc_usd':
        ('okcoin', ('ticker', 'last'), 'CNY'),
    'https://api.kraken.com/0/public/Ticker?pair=XXBTZEUR':
        ('kraken', ('result', 'XXBTZEUR', 'c', 0), 'EUR'),
    'https://api.coinmarketcap.com/v1/ticker/bitcoin/':
        ('coinmarketcap', (0, 'market_cap_usd'), 'USD'),
}


def handle_error(req, e):
    print >>sys.stderr, 'Error:', e.message


if __name__ == '__main__':
    # parse command line options
    parser = ArgumentParser(description='Poll bitcoin API services.')
    parser.add_argument('-d', '--dir', metavar='PATH', type=str,
                        default='/dev/shm/tothemoon',
                        help='directory to save poll results in '
                        '(default dev/shm/tothemoon)')
    parser.add_argument('-i', '--interval', metavar='SECONDS=2', type=float,
                        default=2, help='exchange poll interval')
    parser.add_argument('-t', '--timeout', metavar='SECONDS=7', type=float,
                        default=5, help='timeout for polling')
    parser.add_argument('-l', '--list', action='store_true', default=False,
                        help='list api info')
    args = parser.parse_args()

    root = args.dir

    if args.list:
        for url, (exid, indices, currency) in exchanges.iteritems():
            print exid
            print '  url:     ', url
            print '  currency:', currency
            print '  lookup:   response[%s]' % ']['.join(repr(i) for i in indices)
        sys.exit(0)

    create_root = not os.path.isdir(root)

    # clean up created files on exit
    def remove_files():
        for url, (exid, indices, currency) in exchanges.iteritems():
            path = root + '/' + exid
            if os.path.exists(path):
                os.remove(path)

        if create_root:
            os.rmdir(root)

    atexit.register(remove_files)

    # create root directory if necessary
    if create_root:
        try:
            os.mkdir(root)
        except OSError as e:
            print >>sys.stderr, 'Could not create root directory %s:' % root,
            print >>sys.stderr, e.strerror
            sys.exit(1)

    # poll APIs in endless loop
    try:
        last_update = 0

        while True:
            diff = time.time() - last_update
            if diff < args.interval:
                time.sleep(args.interval - diff)

            requests = (grequests.get(url, timeout=args.timeout)
                        for url in exchanges.iterkeys())

            for res in grequests.imap(requests, exception_handler=handle_error):
                if res is None:
                    continue

                exid, indices, currency = exchanges[res.request.url]

                try:
                    res_json = res.json()

                    if res.status_code != 200:
                        if 'error' in res_json:
                            print >>sys.stderr, 'Server error %d at %s: %s' % \
                                    (res.status_code, exid, res_json['error'])
                        else:
                            print >>sys.stderr, 'Server error %d at %s' % \
                                    (res.status_code, exid)
                    else:
                        last = res_json
                        for i in indices:
                            last = last[i]

                        status = {
                            'last': float(last),
                            'currency': currency,
                            'updated': time.time()
                        }

                        with open(root + '/' + exid, 'w') as f:
                            print >>sys.stderr, 'Updating', exid
                            json.dump(status, f)

                except ValueError as e:
                    print >>sys.stderr, 'Invalid response from %s:' % \
                            exid, e.message
                except KeyError as e:
                    print >>sys.stderr, 'Unexpected response ' \
                            'content from %s:' % exid, res.text

            last_update = time.time()
    except KeyboardInterrupt:
        pass
