# Bank Account Example with Class-Level Decorator
# Python 3 Reflexive Metaprogramming
# H. Conrad Cunningham

# Developed for CSci 658, Software Language Engineering, Spring 2018

#234567890123456789012345678901234567890123456789012345678901234567890

# 2018-03-29: (V1) debug and debugmethods decorators adapted from
#             Beazley's 2013 Metaprogramming tutorial
# 2018-04-02: (V1a) Changed balance to get_balance
# 2018-05-02: (V1b) Made consistent with account4.py

from functools import wraps, partial

# Function-level prefix decorator
def debug(func = None, *, prefix = ''): 
    if func is None:
        return partial(debug, prefix=prefix)
    msg = prefix + func.__qualname__
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(msg)
        return func(*args, **kwargs)
    return wrapper

# Class-level decorator
def debugmethods(cls): 
    for name, val in vars(cls).items():
        print(f'name = {name}, val = {val}')
        if callable(val):
            setattr(cls, name, debug(val)) 
    return cls

@debugmethods
class Account:
    def __init__(self):
        self._bal = 0

    def deposit(self,amt):
        self._bal += amt

    def withdraw(self,amt):
        if amt <= self._bal:
            self._bal -= amt
        else:
            print(f'Insufficient funds for withdrawal of {amt}')

    def get_balance(self):
        return self._bal

    def __str__(self):
        return f'Account with balance {self._bal}'

if __name__ == '__main__':
    acct = Account()
    print(f'Account: {acct}')
    acct.deposit(100)
    print(f'Depost 10: {acct.get_balance()}')
    acct.withdraw(60)
    print(f'Withdraw 60: {acct.get_balance()}')

