'Delegate a role (auth_group) from one entity to another
@param group_id: the role ID or UID (or a list of either)
@param entity: the delegating entity
@param receiver: the pe_id of the receiving entity (or a list of pe_ids)
@param role: the affiliation role
@param role_type: the role type for the affiliation role (default=9)
@note: if role is None, a new role of role_type 0 will be created
for each entity in receiver and used for the delegation
(1:1 delegation)
@note: if both receiver and role are specified, the delegation will
add all receivers to this role and create a 1:N delegation to
this role. If the role does not exist, it will be created (using
the given role type)'

def s3_delegate_role(self, group_id, entity, receiver=None, role=None, role_type=None):

    if (not self.permission.delegations):
        return False
    db = current.db
    s3db = current.s3db
    dtable = s3db.table('pr_delegation')
    rtable = s3db.table('pr_role')
    atable = s3db.table('pr_affiliation')
    if ((dtable is None) or (rtable is None) or (atable is None)):
        return False
    if ((not group_id) or (not entity) or ((not receiver) and (not role))):
        return False
    gtable = self.settings.table_group
    query = None
    uuids = None
    if isinstance(group_id, (list, tuple)):
        if isinstance(group_id[0], str):
            uuids = group_id
            query = gtable.uuid.belongs(group_id)
        else:
            group_ids = group_id
    elif (isinstance(group_id, str) and (not group_id.isdigit())):
        uuids = [group_id]
        query = (gtable.uuid == group_id)
    else:
        group_ids = [group_id]
    if (query is not None):
        query = ((gtable.deleted != True) & query)
        groups = db(query).select(gtable.id, gtable.uuid)
        group_ids = [g.id for g in groups]
        missing = [u for u in uuids if (u not in [g.uuid for g in groups])]
        for m in missing:
            group_id = self.s3_create_role(m, uid=m)
            if group_id:
                group_ids.append(group_id)
    if (not group_ids):
        return False
    if (receiver is not None):
        if (not isinstance(receiver, (list, tuple))):
            receiver = [receiver]
        query = (((((((dtable.deleted != True) & dtable.group_id.belongs(group_ids)) & (dtable.role_id == rtable.id)) & (rtable.deleted != True)) & (atable.role_id == rtable.id)) & (atable.deleted != True)) & atable.pe_id.belongs(receiver))
        rows = db(query).select(atable.pe_id)
        assigned = [row.pe_id for row in rows]
        receivers = [r for r in receiver if (r not in assigned)]
    else:
        receivers = None
    if (role_type is None):
        role_type = 9
    roles = []
    if (role is None):
        if (receivers is None):
            return False
        for pe_id in receivers:
            role_name = ('__DELEGATION__%s__%s__' % (entity, pe_id))
            query = (rtable.role == role_name)
            role = db(query).select(limitby=(0, 1)).first()
            if (role is not None):
                if role.deleted:
                    role.update_record(deleted=False, role_type=0)
                role_id = role.id
            else:
                role_id = s3db.pr_add_affiliation(entity, pe_id, role=role_name, role_type=0)
            if role_id:
                roles.append(role_id)
    else:
        query = (((rtable.deleted != True) & (rtable.pe_id == entity)) & (rtable.role == role))
        row = db(query).select(rtable.id, limitby=(0, 1)).first()
        if (row is None):
            role_id = rtable.insert(pe_id=entity, role=role, role_type=role_type)
        else:
            role_id = row.id
        if role_id:
            if (receivers is not None):
                pr_rebuild_path = s3db.pr_rebuild_path
                for pe_id in receivers:
                    atable.insert(role_id=role_id, pe_id=pe_id)
                    pr_rebuild_path(pe_id, clear=True)
            roles.append(role_id)
    for role_id in roles:
        for group_id in group_ids:
            dtable.insert(role_id=role_id, group_id=group_id)
    self.s3_set_roles()
    return True
