

class FSMProxy(object):

	def __init__(self, cfg):
		from fysom import Fysom
		events = set([event['name'] for event in cfg['events']])
		cfg['callbacks'] = self.collect_event_listeners(events, cfg['callbacks'])
		self.fsm = Fysom(cfg)
		self.attach_proxy_methods(self.fsm, events)

	def collect_event_listeners(self, events, callbacks):
		callbacks = callbacks.copy()
		callback_names = []
		for event in events:
			callback_names.append(('_before_' + event, 'onbefore' + event))
			callback_names.append(('_after_' + event, 'onafter' + event))
		for fn_name, listener in callback_names:
			fn = getattr(self, fn_name, None)
			if callable(fn):
				if listener in callbacks:
					old_fn = callbacks[listener]

					def wrapper(e, old_fn=old_fn, fn=fn):
						old_fn(e)
						fn(e)
					callbacks[listener] = wrapper
				else:
					callbacks[listener] = fn
		return callbacks

	def attach_proxy_methods(self, fsm, events):
		def make_proxy(fsm, event):
			fn = getattr(fsm, event)

			def proxy(*args, **kwargs):
				if len(args) > 0:
					raise FSMProxyError('FSMProxy event listeners only accept named arguments.')
				fn(**kwargs)
			return proxy

		for event in events:
			if not hasattr(self, event):
				setattr(self, event, make_proxy(fsm, event))

	def __getstate__(self):
		state = {}
		for key, value in self.__dict__.iteritems():
			if callable(value) or key == 'fsm':
				continue
			state[key] = value
		state['__class__'] = self.__module__ + '.' + self.__class__.__name__
		return state

	def __setstate__(self, state):
		for key in state:
			self.__dict__[key] = state[key]


class FSMProxyError(Exception):
	pass
