# encoding: utf-8
"""
line/rib.py

Created by Thomas Mangin on 2017-07-01.
Copyright (c) 2009-2017 Exa Networks. All rights reserved.
License: 3-clause BSD. (See the COPYRIGHT file)
"""

from exabgp.reactor.api.command.command import Command
from exabgp.reactor.api.command.limit import match_neighbors
from exabgp.reactor.api.command.limit import extract_neighbors

from exabgp.bgp.message.update.nlri.nlri import NLRI
from exabgp.bgp.message.update.nlri.inet import INET
from exabgp.bgp.message.update.nlri.flow import Flow
from exabgp.bgp.message.update.nlri.vpls import VPLS
from exabgp.bgp.message.update.nlri.evpn.nlri import EVPN

from exabgp.reactor.api.response.answer import Answer

from exabgp.configuration.environment import environment


def register_rib ():
	pass


def _show_adjrib_callback(reactor, service, last, route_type, advertised, rib_name, extensive):
	def callback ():
		families = None
		lines_per_yield = environment.settings().api.chunk
		if last in ('routes', 'extensive', 'static', 'flow', 'l2vpn'):
			peers = list(reactor.peers)
		else:
			peers = [n for n in reactor.peers.keys() if 'neighbor %s' % last in n]
		for key in peers:
			peer = reactor.peers.get(key, None)
			if not peer:
				continue
			if advertised:
				families = peer.proto.negotiated.families if peer.proto else []
			rib = peer.neighbor.rib.outgoing if rib_name == 'out' else peer.neighbor.rib.incoming
			routes = list(rib.cached_changes(families))
			while routes:
				changes, routes = routes[:lines_per_yield], routes[lines_per_yield:]
				for change in changes:
					if isinstance(change.nlri, route_type):
						if extensive:
							reactor.processes.answer(service,'%s %s %s' % (peer.neighbor.name(),'%s %s' % change.nlri.family(),change.extensive()),force=True)
						else:
							reactor.processes.answer(service,'neighbor %s %s %s' % (peer.neighbor.peer_address,'%s %s' % change.nlri.family(),str(change.nlri)),force=True)
				yield True
		reactor.processes.answer_done(service)
	return callback


@Command.register('text','show adj-rib')
def show_adj_rib (self, reactor, service, line):
	words = line.split()
	extensive = line.endswith(' extensive')
	try:
		rib = words[2]
	except IndexError:
		if words[1] == 'adj-rib-in':
			rib = 'in'
		elif words[1] == 'adj-rib-out':
			rib = 'out'
		else:
			reactor.processes.answer(service,Answer.error)
			return False

	if rib not in ('in','out'):
		reactor.processes.answer(service,Answer.error)
		return False

	klass = NLRI

	if 'inet' in words:
		klass = INET
	elif 'flow' in words:
		klass = Flow
	elif 'l2vpn' in words:
		klass = (VPLS, EVPN)

	for remove in ('show','adj-rib','adj-rib-in','adj-rib-out','in','out','extensive'):
		if remove in words:
			words.remove(remove)
	last = '' if not words else words[0]
	callback = _show_adjrib_callback(reactor, service, last, klass, False, rib, extensive)
	reactor.asynchronous.schedule(service, line, callback())
	return True


@Command.register('text','flush adj-rib out')
def flush_adj_rib_out (self, reactor, service, line):
	def callback (self, peers):
		self.log_message("Flushing adjb-rib out for %s" % ', '.join(peers if peers else []) if peers is not None else 'all peers')
		for peer_name in peers:
			peer = reactor.peers.get(peer_name, None)
			if not peer:
				continue
			yield False

		reactor.processes.answer_done(service)

	try:
		descriptions,command = extract_neighbors(line)
		peers = match_neighbors(reactor.peers,descriptions)
		if not peers:
			self.log_failure('no neighbor matching the command : %s' % command,'warning')
			reactor.processes.answer(service,Answer.error)
			return False
		reactor.asynchronous.schedule(service, command, callback(self, peers))
		return True
	except ValueError:
		self.log_failure('issue parsing the command')
		reactor.processes.answer(service, Answer.error)
		return False
	except IndexError:
		self.log_failure('issue parsing the command')
		reactor.processes.answer(service, Answer.error)
		return False


@Command.register('text','clear adj-rib')
def clear_adj_rib (self, reactor, service, line):
	def callback (self, peers, direction):
		self.log_message("clearing adjb-rib-%s for %s" % (direction,', '.join(peers if peers else []) if peers is not None else 'all peers'))
		for peer_name in peers:
			peer = reactor.peers.get(peer_name, None)
			if not peer:
				continue
			if direction == 'out':
				peer.neighbor.rib.outgoing.clear()
			else:
				peer.neighbor.rib.incoming.clear()
			yield False

		reactor.processes.answer_done(service)

	try:
		descriptions,command = extract_neighbors(line)
		peers = match_neighbors(reactor.peers,descriptions)
		if not peers:
			self.log_failure('no neighbor matching the command : %s' % command,'warning')
			reactor.processes.answer(service, Answer.error)
			return False
		words = line.split()
		direction = 'in' if 'adj-rib-in' in words or 'in' in words else 'out'
		reactor.asynchronous.schedule(service, command, callback(self, peers, direction))
		return True
	except ValueError:
		self.log_failure('issue parsing the command')
		reactor.processes.answer(service, Answer.error)
		return False
	except IndexError:
		self.log_failure('issue parsing the command')
		reactor.processes.answer(service, Answer.error)
		return False
