import eisoil.core.pluginmanager as pm
import eisoil.core.log
logger=eisoil.core.log.getLogger('ofed')

GMAv1DelegateBase = pm.getService('gmav1delegatebase')
gfed_ex = pm.getService('gfedv1exceptions')

config = pm.getService('config')
geniutil = pm.getService('geniutil')

class OMAv1Delegate(GMAv1DelegateBase):
    VERSION = '1'
    SUPPLEMENTARY_FIELDS = {
        "_OFELIA_MEMBER_AFFILIATION" : {
            "OBJECT" : "MEMBER",
            "TYPE"   : "STRING",
            "DESC"   : "Organization the member belongs to",
            "CREATE" : "ALLOWED",
            "UPDATE" : True,
            "MATCH"  : True,
            "PROTECT": "IDENTIFYING"
        },
        "_OFELIA_ISLAND_NAME": {
            "OBJECT" : "MEMBER",
            "TYPE"   : "STRING",
            "DESC"   : "Home island of the member",
            "CREATE" : "ALLOWED",
            "UPDATE" : True,
            "MATCH"  : True,
            "PROTECT": "PUBLIC"
        },
        "_OFELIA_MEMBER_X509_CERTIFICATE" : {
            "OBJECT" : "MEMBER",
            "TYPE"   : "CERTIFICATE",
            "DESC"   : "X509 Certificate",
            "CREATE" : "ALLOWED",
            "UPDATE" : False,
            "MATCH"  : False,
            "PROTECT": "PUBLIC"
        },
        "_OFELIA_MEMBER_X509_PRIVATE_KEY" : {
            "OBJECT" : "MEMBER",
            "TYPE"   : "KEY",
            "DESC"   : "Private key for the X509 Certificate",
            "CREATE" : "ALLOWED",
            "UPDATE" : False,
            "MATCH"  : False,
            "PROTECT": "PRIVATE"
        },
        "_OFELIA_MEMBER_ENABLED" : {
            "OBJECT" : "MEMBER",
            "TYPE"   : "BOOLEAN",
            "DESC"   : "If the user is allowed to perform actions",
            "CREATE" : "ALLOWED",
            "UPDATE" : True,
            "MATCH"  : True,
            "PROTECT": "PUBLIC"
        }
    }

    def get_version(self, client_cert):
        # no auth necessary
        certs = {"SFA": "1"}
        fields = {}
        for fname, f in self.SUPPLEMENTARY_FIELDS.iteritems():
            fields[fname] = {
                "TYPE" : f["TYPE"],
                "PROTECT" : f["PROTECT"]
            }
            if "CREATE" in f:
                fields[fname]["CREATE"] = f["CREATE"]
            if "MATCH" in f:
                fields[fname]["MATCH"] = f["MATCH"]
            if "UPDATE" in f:
                fields[fname]["UPDATE"] = f["UPDATE"]
        return self.VERSION, ['KEY'], certs, fields

    def lookup_public_member_info(self, client_cert, field_filter, field_match, options):
        # no auth necessary
        members = self.TEST_DATA # TODO get this from the database
        members = self._map_field_names(members)
        members = self._whitelist_fields(members, self._member_field_names_for_protect('PUBLIC')) # filter data, so only public/identifying/private member info is given out
        return self._match_and_filter_and_to_dict(members, "MEMBER_URN", field_filter, field_match)

    def lookup_identifying_member_info(self, client_cert, credentials, field_filter, field_match, options):
        # TODO get the data from the database
        members = self.TEST_DATA
        members = self._map_field_names(members)
        members = self._whitelist_fields(members, self._member_field_names_for_protect('IDENTIFYING') + ['MEMBER_URN'])
        return self._match_and_filter_and_to_dict(members, "MEMBER_URN", field_filter, field_match)

    def lookup_private_member_info(self, client_cert, credentials, field_filter, field_match, options):
        c_urn, c_uuid, c_email = geniutil.extract_certificate_info(geniutil.infer_client_cert(client_cert, credentials))
        members = self.TEST_DATA # TODO get this from the database
        members = self._map_field_names(members)
        members = self._whitelist_fields(members, self._member_field_names_for_protect('PRIVATE') + ['MEMBER_URN'])
        return self._match_and_filter_and_to_dict(members, "MEMBER_URN", field_filter, field_match)



    # -- Helper methods
    def _member_field_names_for_protect(self, protect):
        """
        Returns all keys (e.g. MEMEBER_URN) which have the given value for protect (e.g. PUBLIC).
        Both the keys in DEFAULT_FIELDS (with object 'MEMBER') and SUPPLEMENTARY_FIELDS are considered.
        """
        result = []
        fields = self.MEMBER_DEFAULT_FIELDS.copy()
        fields.update((k,v) for k,v in self.SUPPLEMENTARY_FIELDS.iteritems() if (v['OBJECT'] == 'MEMBER'))
        for (name, spec) in fields.iteritems():
            if spec['PROTECT'] == protect:
                result.append(name)
        return result

    def _map_field_names(self, members):
        """
        Convertes the internal representation to the GENI field names (e.g. attribute name urn to MEMBER_URN).
        Unmapped entries are eliminated.
        """
        result = []
        for member in members:
            mapped_info = {}
            for entry_key, entry_value in member.iteritems():
                if not entry_key in self.DATA_MAPPING: # skip the entry if there is no mapping
                    continue
                mapped_info[self.DATA_MAPPING[entry_key]] = entry_value
            result.append(mapped_info)
        return result

    def _whitelist_fields(self, members, whitelist):
        """Reduces members' attributes to only have field names which are in whitelist.
        {members} A list of dicts.
        {whitelist} A list of strings."""
        result = []
        for member in members:
            reduced_member_info = {}
            for entry_key, entry_value in member.iteritems():