from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from guardian.shortcuts import assign, remove_perm, get_users_with_perms, get_groups_with_perms, get_objects_for_user
from cache import get_cached_userlist
import types

try:
    from threading import local
except ImportError:
    from django.utils._threading_local import local
    
_thread_locals = local()

def get_current_user():
    return getattr(_thread_locals, 'user', None)

def set_current_user(user):
    _thread_locals.user = user
    
def del_current_user():
    del _thread_locals.user
    
def protect_models():
    cls_list = get_models_to_protect()
    if cls_list:
        user = get_current_user()
        for cls in get_models_to_protect():
                protect_model(cls, user)    
    
def unprotect_models():
    for cls in get_models_to_protect():
            unprotect_model(cls)

def get_models_to_protect():
    if hasattr(get_models_to_protect, 'cls_list'):
        return get_models_to_protect.cls_list
        
    cls_list = []
    for cls_name in settings.USE_GROUP_PERMISSIONS:
        cls_type = ContentType.objects.get(model=cls_name.lower())
        cls_list.append(cls_type.model_class())
    get_models_to_protect.cls_list = cls_list
        
    return cls_list

def protect_model(cls, user):   
    cls.safe_objects.user = user
    cls.safe_objects.check_perm = True
    
    cls.old_save = cls.save
    cls.old_delete = cls.delete
    class_name = cls.__name__.lower()
    cls.save = change_security(class_name)(cls.save)
    cls.delete = change_security(class_name)(cls.delete)    
    
def unprotect_model(cls): 
    if hasattr(cls, 'old_save'):
        cls.save = cls.old_save 
        cls.delete = cls.old_delete 
        del cls.old_save    
        del cls.old_delete
        cls.safe_objects.user = None 
        
def unprotect_instance(instance):
    if hasattr(instance, 'old_save'):
        instance.save = instance.old_save
        instance.delete = instance.old_delete
        
def protect_instance(instance):
    class_name = instance.__class__.__name__.lower()
    cls = ContentType.objects.get(model=class_name)
    cls = cls.model_class()
    
    save = types.MethodType(change_security('project')(cls.save), instance, cls)
    instance.save = save
    
    delete = types.MethodType(change_security('project')(cls.delete), instance, cls)
    instance.delete = delete
        
def change_security(cls_name):
    def wrapper(func):
        def wrapped(self, *args, **kwargs):  
                        
            if self.pk and not get_current_user().has_perm('change_%s' % cls_name, self):
                raise AttributeError('User %s is not allowed to change object %s' % (get_current_user(), self))
      
            return func(self, *args, **kwargs)
        return wrapped    
    return wrapper

def set_forbidden_stream(xml, user):
    cls = ContentType.objects.get(model='content')
    cls = cls.model_class()
    
    old_user = cls.safe_objects.user
    obj_list = cls.safe_objects.all()
    
    for elem in xml.xpath('/iri/medias/media'):
        if not obj_list.filter(iri_id=elem.get('id')):
            elem.set('video', settings.FORBIDDEN_STREAM_URL)
    
    cls.safe_objects.user = old_user 
    
    return xml

def add_change_attr(user, obj_list):
    """ 
     Add a change attribute set to True to objects of obj_list
     if permissions change_object is set with respect to user.
    """
    if len(obj_list) == 0:
        return []
    
    model_name = obj_list[0].__class__.__name__.lower()
    ctype = ContentType.objects.get(model=model_name)
    cls = ctype.model_class()

    if model_name in [cls_name.lower() for cls_name in settings.USE_GROUP_PERMISSIONS] or model_name == 'group':
        to_check = True
        change_list = get_objects_for_user(user, '%s.change_%s' % (cls._meta.app_label, model_name))
    else:
        to_check = False        
        
    for obj in obj_list:
        if not to_check or obj in change_list:
            obj.change = True
        else:
            obj.change = False
            
    return obj_list

def assign_perm_to_obj(object, read_list, write_list, owner):
    name = object.__class__.__name__.lower()
    
    old_users = get_users_with_perms(object).exclude(id=owner.id)
    old_groups = get_groups_with_perms(object)
    
    for elem in read_list:
        assign('view_%s' % name, elem, object)
        if elem in write_list:
            assign('change_%s' % name, elem, object)
        else:
            remove_perm('change_%s' % name, elem, object)            
                
    def remove_perms(new_list, old_list, obj, name):
        for e in old_list:
            if e not in new_list:
                remove_perm('view_%s' % name, e, obj)
                remove_perm('change_%s' % name, e, obj)
                
    remove_perms(read_list, old_users, object, name)
    remove_perms(read_list, old_groups, object, name) 
    
def get_userlist(user, filter=None):
    user_list = get_cached_userlist().exclude(id=user.id)
    if filter:
        user_list = user_list.filter(username__icontains=filter)
    elem_list = [{'name': u.username, 'id': u.id, 'type': 'user'} for u in user_list[0:settings.MAX_USERS_SEARCH]]
    return elem_list  

def get_userlist_model(object, owner):
    if hasattr(object, 'is_public') and object.is_public:
        return [None, None]
    
    users = get_users_with_perms(object, attach_perms=True, with_group_users=False)
    groups = get_groups_with_perms(object, attach_perms=True)
    object_name = object.__class__.__name__.lower()
    
    def create_dict (users_or_groups, name, groups=True):
        l = []
        admin_list = []
        
        for elem in users_or_groups.keys():
            if elem == owner:
                continue
                        
            if groups:
                elem_dict = {'name': elem.name, 'type': 'group', 'id': elem.id}
            else:
                elem_dict = {'name': elem.username, 'type': 'user', 'id': elem.id}
            
            for perm in users_or_groups[elem]:
                if perm == 'change_%s' % name:
                    elem_dict['change'] = True
                    admin_list.append(elem_dict)
                    continue
                
            l.append(elem_dict)   
        return l, admin_list
    
    users_list, admin_users = create_dict(users, object_name, False)
    groups_list, admin_groups = create_dict(groups, object_name, True)
    
    return [users_list + groups_list, admin_users + admin_groups]
 
def get_userlist_group(group, user):
    members = group.user_set.all()
    admin = get_users_with_perms(group)

    member_list = []
    for u in members:
        if u == user:
            continue
        u_dict = {'name': u.username, 'id': u.id, 'type': 'user', 'change': False}
        if u in admin:
            u_dict['change'] = True
        member_list.append(u_dict)
        
    admin_list = [{'name': e.username, 'id': e.id, 'type': 'user', 'change': False} for e in admin]
    
    return [member_list, admin_list]
