#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
"""Utility methods for getting allocation candidates."""

import collections


RPCandidate = collections.namedtuple('RPCandidates', 'id root_id rc_id')


class RPCandidateList(object):
    """Helper class to manage allocation candidate resource providers list,
    RPCandidates, which consists of three-tuples with the first element being
    the resource provider ID, the second element being the root provider ID
    and the third being resource class ID.
    """
    def __init__(self, rp_candidates=None):
        self.rp_candidates = rp_candidates or set()

    def __len__(self):
        return len(self.rp_candidates)

    def __bool__(self):
        return bool(len(self))

    def __nonzero__(self):
        return self.__bool__()

    def merge_common_trees(self, other):
        """Merge two RPCandidateLists by OR'ing the two list of candidates
        and if the tree is not in both RPCandidateLists, we exclude resource
        providers in that tree. This is used to get trees that can satisfy
        all requested resource.
        """
        if not self:
            self.rp_candidates = other.rp_candidates
        elif not other:
            pass
        else:
            trees_in_both = self.trees & other.trees
            self.rp_candidates |= other.rp_candidates
            self.filter_by_tree(trees_in_both)

    def add_rps(self, rps, rc_id):
        """Add given resource providers to the candidate list.

        :param rps: tuples of (resource provider ID, anchor root provider ID)
        :param rc_id: ID of the class of resource provided by these resource
                      providers
        """
        self.rp_candidates |= set(
            RPCandidate(id=rp[0], root_id=rp[1], rc_id=rc_id) for rp in rps)

    def filter_by_tree(self, tree_root_ids):
        """Filter the candidates by given trees"""
        self.rp_candidates = set(
            p for p in self.rp_candidates if p.root_id in tree_root_ids)

    def filter_by_rp(self, rptuples):
        """Filter the candidates by given resource provider"""
        self.rp_candidates = set(
            p for p in self.rp_candidates if (p.id, p.root_id) in rptuples)

    def filter_by_rp_or_tree(self, rp_ids):
        """Filter the candidates out if neither itself nor its root is in
        given resource providers
        """
        self.rp_candidates = set(
            p for p in self.rp_candidates if set([p.id, p.root_id]) & rp_ids)

    def filter_by_rp_nor_tree(self, rp_ids):
        """Filter the candidates out if either itself or its root is in
        given resource providers
        """
        self.rp_candidates = set(
            p for p in self.rp_candidates if not (
                set([p.id, p.root_id]) & rp_ids))

    @property
    def rps(self):
        """Returns a set of IDs of nominated resource providers"""
        return set(p.id for p in self.rp_candidates)

    @property
    def trees(self):
        """Returns a set of nominated trees each of which are expressed by
        the root provider ID
        """
        return set(p.root_id for p in self.rp_candidates)

    @property
    def all_rps(self):
        """Returns a set of IDs of all involved resource providers"""
        return (self.rps | self.trees)

    @property
    def rps_info(self):
        return self.rp_candidates
