from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import generics
from rest_framework import status
from rest_framework.exceptions import APIException
from rest_framework.exceptions import PermissionDenied as RestFrameworkPermissionDenied
from django.core.exceptions import PermissionDenied as DjangoPermissionDenied

class XOSProgrammingError(APIException):
    status_code=400
    def __init__(self, why="programming error", fields={}):
        APIException.__init__(self, {"error": "XOSProgrammingError",
                            "specific_error": why,
                            "fields": fields})

class XOSPermissionDenied(RestFrameworkPermissionDenied):
    def __init__(self, why="permission error", fields={}):
        APIException.__init__(self, {"error": "XOSPermissionDenied",
                            "specific_error": why,
                            "fields": fields})

class XOSNotAuthenticated(RestFrameworkPermissionDenied):
    def __init__(self, why="you must be authenticated to use this api", fields={}):
        APIException.__init__(self, {"error": "XOSNotAuthenticated",
                            "specific_error": why,
                            "fields": fields})

class XOSValidationError(APIException):
    status_code=403
    def __init__(self, why="validation error", fields={}):
        APIException.__init__(self, {"error": "XOSValidationError",
                            "specific_error": why,
                            "fields": fields})

class XOSRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):

    # To handle fine-grained field permissions, we have to check can_update
    # the object has been updated but before it has been saved.

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        self.object = self.get_object_or_none()

        if self.object is None:
            raise XOSProgrammingError("Use the List API for creating objects")

        serializer = self.get_serializer(self.object, data=request.DATA,
                                         files=request.FILES, partial=partial)

        assert(serializer.object is not None)

        serializer.object.caller = request.user

        if not serializer.is_valid():
            raise XOSValidationError(fields=serializer._errors)

        if not serializer.object.can_update(request.user):
            raise XOSPermissionDenied()

        if (hasattr(self, "pre_save")):
            # rest_framework 2.x
            self.pre_save(serializer.object)
            self.object = serializer.save(force_update=True)
            self.post_save(self.object, created=False)
        else:
            # rest_framework 3.x
            self.perform_update(serializer)

        return Response(serializer.data, status=status.HTTP_200_OK)

    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        obj.caller = request.user
        if obj.can_update(request.user):
            # this is the guts of DestroyModelMixin, copied here so that we
            # can use the obj with caller set in it,
            self.pre_delete(obj)
            obj.delete()
            self.post_delete(obj)
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            return Response(status=status.HTTP_400_BAD_REQUEST)

    def handle_exception(self, exc):
        # REST API drops the string attached to Django's PermissionDenied
        # exception, and replaces it with a generic "Permission Denied"
        if isinstance(exc, DjangoPermissionDenied):
            response=Response({'detail': {"error": "PermissionDenied", "specific_error": str(exc), "fields": {}}}, status=status.HTTP_403_FORBIDDEN)
            response.exception=True
            return response
        else:
            return super(XOSRetrieveUpdateDestroyAPIView, self).handle_exception(exc)

class XOSListCreateAPIView(generics.ListCreateAPIView):
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.DATA, files=request.FILES)

        # In rest_framework 3.x: we can pass raise_exception=True instead of
        # raising the exception ourselves
        if not serializer.is_valid():
            raise XOSValidationError(fields=serializer._errors)

        # now do XOS can_update permission checking

        obj = serializer.object
        obj.caller = request.user
        if not obj.can_update(request.user):
            raise XOSPermissionDenied()

        # stuff below is from generics.ListCreateAPIView

        if (hasattr(self, "pre_save")):
            # rest_framework 2.x
            self.pre_save(serializer.object)
            self.object = serializer.save(force_insert=True)
            self.post_save(self.object, created=True)
        else:
            # rest_framework 3.x
            self.perform_create(serializer)

        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED,
                        headers=headers)

    def handle_exception(self, exc):
        # REST API drops the string attached to Django's PermissionDenied
        # exception, and replaces it with a generic "Permission Denied"
        if isinstance(exc, DjangoPermissionDenied):
            response=Response({'detail': {"error": "PermissionDenied", "specific_error": str(exc), "fields": {}}}, status=status.HTTP_403_FORBIDDEN)
            response.exception=True
            return response
        else:
            return super(XOSListCreateAPIView, self).handle_exception(exc)


@@ -363,16 +363,92 @@
 onDenied
+%0Afrom django.core.exceptions import ValidationError as DjangoValidationError
 %0A%0Aclass 
@@ -3893,32 +3893,299 @@
 return response%0A
+        elif isinstance(exc, DjangoValidationError):%0A            response=Response(%7B'detail': %7B%22error%22: %22ValidationError%22, %22specific_error%22: str(exc), %22fields%22: %7B%7D%7D%7D, status=status.HTTP_400_BAD_REQUEST)%0A            response.exception=True%0A            return response%0A
         else:%0A  
@@ -5880,32 +5880,299 @@
 return response%0A
+        elif isinstance(exc, DjangoValidationError):%0A            response=Response(%7B'detail': %7B%22error%22: %22ValidationError%22, %22specific_error%22: str(exc), %22fields%22: %7B%7D%7D%7D, status=status.HTTP_400_BAD_REQUEST)%0A            response.exception=True%0A            return response%0A
         else:%0A  

return 400 error response if django validation error raised inside API