from elixir.ext.associable import associable
from elixir import events
from elixir.statements import Statement, MUTATORS
from user import Permission
import types

_has_permissions = associable(Permission, '_permissions')

"""
has_permissions

Elixir statement for handling row-level permissions. Creates associated 
tables using associable using {{package}}.model.user.Permission Entity class.

has_permissions takes a list of permission names (or name/description tuples)
and three optional callbacks:

precheck(self, principal, permission_name): checked before actual permission 
checking. If returns True, permission check is halted and True returned. 
Argument 'principal' may be User or Group instance.

postcheck(self, principal, permission_name): checked after actual permission
checking, if check returns False. Can therefore override permission result if
False.

default_permissions(self): assign permissions to user(s) and/or group(s) after
permissions for that instance have been created.

Example:

from elixir import *
from permissions import has_permissions

class NewsItem(Entity):
    has_field('title', Unicode(100))
    has_field('content', Unicode)
    has_field('created', DateTime, default=datetime.now)
    belongs_to('office', of_kind='Office')
    belongs_to('author', of_kind='{{package}}.model.User')
    has_permissions(['edit', 'delete'], 
                    precheck='_is_author', 
                    postcheck='_check_office_permission')

    def _is_author(self, principal, perm):
        if self.author == principal: return True

    def _check_office_permission(self, principal, perm):
        if self.office:
            return self.office.has_permission(perm, principal)

"""
class HasPermissions(object):
    def __init__(self, entity, permissions, precheck=None, postcheck=None,
        default_permissions=None):

        entity._permission_names = permissions
        entity._permissions_precheck = precheck
        entity._permissions_postcheck = postcheck

        statements = getattr(entity, MUTATORS, [])
        statements.append((_has_permissions, (), {}))
        setattr(entity, MUTATORS, statements)
        
        @events.before_insert
        def create_permissions(self):
            for perm in entity._permission_names:
                if type(perm) is types.StringType:
                    name, description = perm, None
                elif type(perm) is types.TupleType:
                    name, description = perm
                self._permissions.append(Permission(name=name,
                                         description=description))
            if self.default_permissions:
                self.default_permissions()

        def get_permission(self, name):
            for p in self._permissions:
                if name == p.name:
                    return p

        def get_permissions(self, *names):
            if names:
                return [p for p in self._permissions if p.name in names]
            return self._permissions

        def grant_permissions_for_user(self, user, *names):
            for p in self.get_permissions(*names):
                user.user_permissions.append(p)

        def grant_permissions_for_group(self, group, *names):
            for p in self.get_permissions(*names):
                group.permissions.append(p)

        def revoke_permissions_for_user(self, user, *names):
            for p in self.get_permissions(*names):
                if p in user.user_permissions: user.user_permissions.remove(p)

        def revoke_permissions_for_group(self, group, *names):
            for p in self.get_permissions(*names):
                if p in group.permissions: group.permissions.remove(p)

        def has_permission(self, principal, perm):
            if entity._permissions_precheck:
                precheck = getattr(self, entity._permissions_precheck)
                if precheck and precheck(principal, perm):
                    return True
            permission = self.get_permission(perm)
            if permission and permission in principal.permissions:
                return True
            if entity._permissions_postcheck:
                postcheck = getattr(self, entity._permissions_postcheck)
                if postcheck and postcheck(principal, perm):
                    return True
            return False

        entity.create_permissions = create_permissions
        entity.default_permissions = default_permissions
        entity.get_permission = get_permission
        entity.get_permissions = get_permissions
        entity.grant_permissions_for_user = grant_permissions_for_user
        entity.grant_permissions_for_group = grant_permissions_for_group
        entity.revoke_permissions_for_user = revoke_permissions_for_user
        entity.revoke_permissions_for_group = revoke_permissions_for_group
        entity.has_permission = has_permission
    
has_permissions = Statement(HasPermissions)

__all__=['has_permissions']
