from django import newforms as forms
from django.utils.encoding import force_unicode
from django.contrib.admin.options import ModelAdmin
from django.db.models.fields import NOT_PROVIDED
from decimal import Decimal
from django.template.defaultfilters import linebreaksbr
from django.template.defaultfilters import truncatewords_html

DEBUG = False

class ReadOnlyTextWidget(forms.widgets.Widget):
    """
    This is improved DefaultValueWidget
    from http://www.djangosnippets.org/snippets/323/
    """
    def __init__(self, value, display=None, attrs=None, fk=False):
        #if isinstance(display.widget, label):
        #raise Exception(display.widget.__dict__)
        if DEBUG: print '[i]', display, value
        # this allows to genericly pass in any field object intending to
        # catch ModelChoiceFields, without having to care about the actual
        # type.
        if isinstance(display, forms.Field):
            self.display = display
        else:
            self.display = None
        self.value = value
        super(ReadOnlyTextWidget, self).__init__(attrs)

    #def _has_changed(self, initial, data):
        #print '[H]', initial, data
        #return False

    def value_from_datadict(self, data, files, name):
        value = data.get(name, self.value)
        if DEBUG: print "[d] value for %s = %s" % (name, value)
        #HACK: Decimal can't be converted to Decimal again
        # so returning str instead
        if type(value) is Decimal: return str(value)
        if isinstance(value, forms.Field): return str(value)
        return value

    def render(self, name, value, attrs=None):
        if DEBUG: print "[r] value for %s = %s" % (name, value)
        if isinstance(self.display, forms.ModelChoiceField):
            try:
                value = self.display.queryset.get(pk=value)
            except:
                value = value
        if isinstance(value, list):
            r = ",".join(map(unicode, self.value))
        else:
            r = value
        s = force_unicode(r, strings_only=False)
        return truncatewords_html(linebreaksbr(s), 50)

class ReadOnlyTextWidget(forms.widgets.Widget):
    """
    This is improved DefaultValueWidget
    from http://www.djangosnippets.org/snippets/323/
    """
    def __init__(self, value, display=None, attrs=None, fk=False):
        #if isinstance(display.widget, label):
        #raise Exception(display.widget.__dict__)
        if isinstance(display, forms.ModelChoiceField):
            try:
                self.display = display.queryset.get(pk=value)
            except:
                self.display = value
        # this allows to genericly pass in any field object intending to
        # catch ModelChoiceFields, without having to care about the actual
        # type.
        elif isinstance(display, forms.Field):
            self.display = None
        else:
            self.display = display
        self.value = value
        super(ReadOnlyTextWidget, self).__init__(attrs)

    def value_from_datadict(self, data, files, name):
        value = data.get(name, self.value)
        if DEBUG: print "[d] value for %s = %s" % (name, value)
        #HACK: Decimal can't be converted to Decimal again
        # so returning str instead
        if type(value) is Decimal: return str(value)
        if isinstance(value, forms.Field): return str(value)
        return value

    def render(self, name, value, attrs=None):
        if self.display is None:
            r = self.value
        elif isinstance(self.display, list):
            r = ",".join(map(unicode, self.display))
        else:
            r = self.display
        s = force_unicode(r, strings_only=False)
        return truncatewords_html(linebreaksbr(s), 50)

class FieldLevelPermissionsAdmin(ModelAdmin):
    def get_fieldsets(self, request, obj):
        "Hook for specifying fieldsets for the add form."
        if self.declared_fieldsets:
            fieldsets = self.declared_fieldsets
        else:
            form = self.get_form(request, obj)
            fieldsets = [(None, {'fields': form.base_fields.keys()})]
        for fs in fieldsets:
            fs[1]['fields'] = [f for f in fs[1]['fields'] if self.can_view_field(request, obj, f)]
        return fieldsets

    def get_form(self, request, obj=None):
        superclass = super(RowLevelPermissionsAdmin, self)
        formclass = superclass.get_form(request, obj)
        for name, field in formclass.base_fields.items():
            if request.method == 'POST' and not self.can_change_field(request, obj, name):
                del formclass.base_fields[name]
            elif not self.can_view_field(request, obj, name):
                del formclass.base_fields[name]
        return formclass

    #XXX: To be overriden in child
    def can_view_field(self, request, object, field_name):
        """
        Boolean method, returning True if user allowed to view
        field with name field_name.
        user is stored in the request object,
        object is None only if object does not exist yet
        """
        if request.user.is_superuser:
            return True
        if object is None:
            return request.user.has_add_permission(request)
        else:
            return request.user.has_change_permission(request, object)

    #XXX: To be overriden in child
    def can_change_field(self, request, object, field_name):
        """
        Boolean method, returning True if user allowed to
        change field with name field_name.
        user is stored in the request object,
        object is None only if object does not exist yet
        """
        if self.can_view_field(request, object, field_name):
            return True
        return False

    def formfield_for_dbfield(self, db_field, **kwargs):
        superclass = super(RowLevelPermissionsAdmin, self)
        field = superclass.formfield_for_dbfield(db_field, **kwargs)
        if not field: return None
        default_value = kwargs.get('initial', db_field.default)
        if default_value is NOT_PROVIDED:
            default_value = None
        if not self.can_view_field(self._request, self._object, db_field.name):
            #XXX: Not displayed, but used when default value is provided
            #if default_value:
            field.widget = ReadOnlyTextWidget(default_value, display=field, fk=True)
            #else:
            #    return None
            #return None
        elif not self.can_change_field(self._request, self._object, db_field.name):
            #XXX: Displaying as text
            #return None
            field.widget = ReadOnlyTextWidget(default_value, display=field)
        return field

    def has_add_permission(self, request):
        perm_name = self.opts.app_label + '.' + self.opts.get_add_permission()
        return request.user.has_perm(perm_name)

    def has_change_permission(self, request, obj=None):
        perm_name = self.opts.app_label + '.' + self.opts.get_change_permission()
        if not request.user.has_perm(perm_name): return False
        if obj:
            #TODO: Remove this extra query
            if not self.queryset(request).filter(pk=obj.id).count():
                return False
        return True

    def changelist_view(self, request):
        # useful for list_display customization
        self._action = 'changelist'
        self._request = request
        self._object = None
        superclass = super(RowLevelPermissionsAdmin, self)
        return superclass.changelist_view(request)

    def change_view(self, request, object_id):
        # assignments are required for permission checks later
        self._action = 'change'
        model = self.model
        try:
            #TODO: Get rid of this query (lines copied from parent)
            obj = model._default_manager.get(pk=object_id)
        except model.DoesNotExist:
            # Don't raise Http404 just yet, because we haven't checked
            # permissions yet. We don't want an unauthenticated user to be able
            # to determine whether a given object exists.
            obj = None
        self._object = obj
        self._request = request
        superclass = super(RowLevelPermissionsAdmin, self)
        return superclass.change_view(request, object_id)

    def add_view(self, request):
        # assignments are required for permission checks later
        self._action = 'add'
        self._object = None
        self._request = request
        superclass = super(RowLevelPermissionsAdmin, self)
        return superclass.add_view(request)