# encoding: utf-8
"""
store.py

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

from exabgp.bgp.message import IN
from exabgp.bgp.message import OUT


class Cache(object):
    def __init__(self, cache, families):
        self.cache = cache
        self._seen = {}
        # self._seen[family][change-index] = change
        # nlri.index() would be a few bytes shorter than change.index() but ..
        # we need change.index() in other part of the code
        # we pre-compute change.index() so that it is only allocted once
        self.families = families

    def clear_cache(self):
        self._seen = {}

    def delete_cached_family(self, families):
        for family in self._seen.keys():
            if family not in families:
                del self._seen[family]

    def cached_changes(self, families=None, actions=[OUT.ANNOUNCE]):
        # families can be None or []
        requested_families = self.families if families is None else set(families).intersection(self.families)

        # we use list() to make a snapshot of the data at the time we run the command
        for family in requested_families:
            for change in self._seen.get(family, {}).values():
                if change.nlri.action in actions:
                    yield change

    def is_cached(self, change):
        if not self.cache:
            return False
        # if we cache sent NLRI and this NLRI was never sent before, we do not need to send a withdrawal
        # as the route removed before we could announce it
        return change.index() not in self._seen.get(change.nlri.family(), {})

    # return a tuple exists in cache, same in cache
    def in_cache(self, change):
        if not self.cache:
            return False, False

        old_change = self._seen.get(change.nlri.family(), {}).get(change.index(), None)
        if not old_change:
            return False, False

        if change.nlri.action != OUT.ANNOUNCE:
            return True, False

        if old_change.attributes.index() != change.attributes.index():
            return True, False

        if old_change.nlri.nexthop.index() != change.nlri.nexthop.index():
            return True, False

        return True, True

    # add a change to the cache of seen Change
    def update_cache(self, change):
        if not self.cache:
            return
        family = change.nlri.family()
        index = change.index()
        if change.nlri.action == OUT.ANNOUNCE:
            self._seen.setdefault(family, {})[index] = change
        elif family in self._seen:
            self._seen[family].pop(index, None)
