from gevent import Greenlet, monkey, core; monkey.patch_all()

import bottle, jsonrpc, sys

class DuplicateKeyError(Exception):
    pass

class CoinBank(object):
    def __init__(self, config):
        self.acct  = config['coinrpc.acct']
        self.url   = config['coinrpc.url']
        self.ratio = float(config['coinrpc.ratio'])
        self.txfee = float(config['coinrpc.txfee'])
        self.interval  = float(config['coinrpc.interval'])

        self.next_payout = core.time() + self.interval
        self.pending = {}
        self.paid = [(0,0)]
        self.pay_periods = 0
        self.making_payment = False

        self._balance_stat = 0
        self._balance = self.balance

        self._public_address_stat = 0
        self._public_address = self.public_address

    @property
    def balance(self):
        svc = None
        while self._balance_stat != self.get_pay_status():
            if svc is None:
                svc = jsonrpc.ServiceProxy(self.url)
            self._balance_stat = self.get_pay_status()
            self._balance = float(svc.getbalance(self.acct))
        return self._balance

    @property
    def public_address(self):
        svc = None
        while self._public_address_stat != self.get_pay_status():
            if svc is None:
                svc = jsonrpc.ServiceProxy(self.url)
            self._public_address_stat = self.get_pay_status()
            self._public_address = svc.getaccountaddress(self.acct)
        return self._public_address

    def get_total_pending(self):
        return sum(self.pending.values())

    def get_available(self):
        net = self.balance - self.get_total_pending() - self.txfee
        if net < 0:
            net = 0
        return net

    def get_current_payout(self):
        return min(self.ratio * self.get_available(), 1000)

    def get_pay_status(self):
        '''This method should be greenthread-atomic'''
        return (len(self.paid), sum(self.pending.values()), self.pay_periods)

    def schedule_payment(self, addr):
        svc = jsonrpc.ServiceProxy(self.url)
        if not svc.validateaddress(addr)['isvalid']:
            raise ValueError
        if addr in self.pending:
            raise DuplicateKeyError

        while 1:
            pay_status = self.get_pay_status()
            amt = self.get_current_payout()
            # Check to make sure nothing happened
            if pay_status == self.get_pay_status():
                break

        self.pending[addr] = amt
        return amt

def with_bank(orig_func):
    '''Function decorator to provide coinrpc bank'''
    def wrapped_func(*arg, **kwarg):
        app = bottle.default_app()
        bank = app.config['coinrpc.bank']
        return orig_func(bank, *arg, **kwarg)
    return wrapped_func

@with_bank
def make_payments(bank):

    bank.pay_periods += 1
    amt = bank.get_total_pending()
    Greenlet.spawn_later(bank.interval, globals()['make_payments'])

    if amt == 0:
        return False

    svc = jsonrpc.ServiceProxy(bank.url)
    to_pay = bank.pending
    bank.pending = {}
    bank.paid += [(core.time(), amt)]

    svc.sendmany(bank.acct, to_pay)

@bottle.get('/')
@with_bank
def make_main_page(bank):
    return """
Donation address is: {}
<br>
Current payout is: {}
<br>
<form action="/payout" method="post">
  Address: <input name="addr" type="text" />
  <input value="payout" type="Submit">
</form>
""".format(bank.public_address, bank.get_current_payout())

@bottle.get('/stats')
@with_bank
def make_stats_page(bank):
    return """
Donation address is: {}
<br>
Available funds are: {}
<br>
Current payout is: {}
<br>
Total pending payouts are: {}
<br>
Total pay periods elapsed: {}
<br>
Last payout was on {} for {}
""".format(
    bank.public_address,
    bank.get_available(),
    bank.get_current_payout(),
    bank.get_total_pending(),
    bank.pay_periods,
    bank.paid[-1][0],
    bank.paid[-1][1],
)

@bottle.post('/payout')
@with_bank
def attempt_payout(bank):
    form = bottle.request.forms
    addr = form.get('addr')
    try:
        amt = bank.schedule_payment(addr)
    except ValueError:
        return bottle.HTTPResponse(status = 400, body = "Bad address: "+addr)
    except DuplicateKeyError:
        return bottle.HTTPResponse(status = 400, body = addr+" already queued")

    return "Scheduled {} to {}".format(amt, addr)


if __name__ == '__main__':
    app = bottle.default_app()
    try:
        conf_file = sys.argv[1]
    except IndexError:
        conf_file = 'coin.conf'

    config = app.config.load_config(conf_file)

    url = 'http://%s:%s@%s:%s' % (
        config['coinrpc.user'],
        config['coinrpc.pass'],
        config['coinrpc.host'],
        config['coinrpc.port'],
    )
    config['coinrpc.url'] = url

    bank = CoinBank(config)
    config['coinrpc.bank'] = bank

    # GeventServer is more fragile than the default WSGIRefServer: extra
    # arguments must be filtered out of config
    gevent_conf = {'server': 'gevent'}
    for key, val in config.items():
        if '.' not in key and key not in ('catchall','autojson'):
            gevent_conf[key] = val

    Greenlet.spawn_later(bank.interval, make_payments)
    app.run(**gevent_conf)

@@ -941,37 +941,23 @@
 c = 
-jsonrpc.ServiceProxy(self.url
+self.get_proxy(
 )%0A  
@@ -1283,37 +1283,23 @@
 c = 
-jsonrpc.ServiceProxy(self.url
+self.get_proxy(
 )%0A  
@@ -1459,24 +1459,96 @@
 ic_address%0A%0A
+    def get_proxy(self):%0A        return jsonrpc.ServiceProxy(self.url)%0A%0A
     def get_
@@ -2082,37 +2082,23 @@
 c = 
-jsonrpc.ServiceProxy(self.url
+self.get_proxy(
 )%0A  
@@ -3017,37 +3017,23 @@
 c = 
-jsonrpc.ServiceProxy(bank.url
+bank.get_proxy(
 )%0A  

add get_proxy() method