from __future__ import unicode_literals

'''Add some missing HttpResponse sub-classes'''
from django.core.exceptions import SuspiciousOperation
from django.http import HttpResponse, Http404
from django.utils.encoding import iri_to_uri

try:
    from urlparse import urlparse
except ImportError:
    from urllib.parse import urlparse
try:
    from collections import OrderedDict
except ImportError:
    from ordereddict import OrderedDict

import json
import re

STATUS_CODES = (
    (100, 'Continue'),
    (101, 'Switching Protocols'),

    (200, 'OK'),
    (201, 'Created'),
    (202, 'Accepted'),
    #(203, Non-Authoritative Information),
    (204, 'No Content'),
    (205, 'Reset Content'),
    (206, 'Partial Content'),

    (300, 'Multiple Choices'),
    (301, 'Moved Permanently'),
    (302, 'Found'),
    (303, 'See Other'),
    (304, 'Not Modified'),
    (305, 'Use Proxy'),
    #306 Deprecated
    (307, 'Temporary Redirect'),
    (308, 'Permanent Redirect'),

    (400, 'Bad Request'),
    (401, 'Unauthorized'),
    (402, 'Payment Required'),
    (403, 'Forbidden'),
    (404, 'Not Found'),
    (405, 'Method Not Allowed'),
    (406, 'Not Acceptable'),
    (407, 'Proxy Authentication Required'),
    (408, 'Request Timeout'),
    (409, 'Conflict'),
    (410, 'Gone'),
    (411, 'Length Required'),
    (412, 'Precondition Failed'),
    (413, 'Request Entity Too Large'),
    (414, 'Request-URI Too Long'),
    (415, 'Unsupported Media Type'),
    (416, 'Requested Range Not Satisfiable'),
    (417, 'Expectation Failed'),

    (500, 'Internal Server Error'),
    (501, 'Not Implemented'),
    (502, 'Bad Gateway'),
    (503, 'Service Unavailable'),
    (504, 'Gateway Timeout'),
    (505, 'HTTP Version Not Supported'),
)

STATUS = OrderedDict(STATUS_CODES)
# Set constant-like properties for reverse lookup
for code, label in STATUS_CODES:
    setattr(STATUS, re.sub(r'\W', '_', label.upper()), code)

class BaseHttpResponse(HttpResponse, Exception):
    '''
    A sub-class of HttpResponse that is also an Exception, allowing us to
    raise/catch it.

    With thanks to schinkel's repose.
    '''

#
# Success Responses (2xx)
#

class HttpResponseSuccess(BaseHttpResponse):
    '''A base class for all 2xx responses, so we can issubclass test.'''

class OK(HttpResponseSuccess):
    status_code = STATUS.OK

class Created(HttpResponseSuccess):
    status_code = STATUS.CREATED

class Accepted(HttpResponseSuccess):
    status_code = STATUS.ACCEPTED

class NoContent(HttpResponseSuccess):
    status_code = STATUS.NO_CONTENT

class ResetContent(HttpResponseSuccess):
    status_code = STATUS.RESET_CONTENT

class PartialContent(HttpResponseSuccess):
    status_code = STATUS.PARTIAL_CONTENT

#
# Redirection Responses (3xx)
#

class HttpResponseRedirection(BaseHttpResponse):
    '''A base class for all 3xx responses.'''

class LocationHeaderMixin(object):
    '''Many 3xx responses require a Location header'''
    def __init__(self, location, *args, **kwargs):
        super(LocationHeaderMixin, self).__init__(*args, **kwargs)
        parsed = urlparse(location)
        if parsed.scheme and parsed.scheme not in self.allowed_schemes:
            raise SuspiciousOperation(
                "Unsafe redirect to URL with protocol '%s'" % parsed.scheme
            )
        self['Location'] = iri_to_uri(location)

    url = property(lambda self: self['Location'])


class MultipleChoices(HttpResponseRedirection):
    status_code = STATUS.MULTIPLE_CHOICES

class MovedPermanently(LocationHeaderMixin, HttpResponseRedirection):
    status_code = STATUS.MOVED_PERMANENTLY

class Found(LocationHeaderMixin, HttpResponseRedirection):
    status_code = STATUS.FOUND

class SeeOther(LocationHeaderMixin, HttpResponseRedirection):
    status_code = STATUS.SEE_OTHER

class NotModified(HttpResponseRedirection):
    status_code = STATUS.NOT_MODIFIED

class UseProxy(LocationHeaderMixin, HttpResponseRedirection):
    status_code = STATUS.USE_PROXY

class TemporaryRedirect(HttpResponseRedirection):
    status_code = STATUS.TEMPORARY_REDIRECT

class PermanentRedirect(HttpResponseRedirection):
    status_code = STATUS.PERMANENT_REDIRECT

#
# Client Error Responses (4xx)
#

class HttpResponseError(BaseHttpResponse):
    '''A base class for all 4xx responses.'''

class BadRequest(HttpResponseError):
    status_code = STATUS.BAD_REQUEST

# XXX Auth-Realm ?
class Unauthorized(HttpResponseError):
    status_code = STATUS.UNAUTHORIZED

class PaymentRequired(HttpResponseError):
    status_code = STATUS.PAYMENT_REQUIRED

class Forbidden(HttpResponseError):
    status_code = STATUS.FORBIDDEN

class NotFound(HttpResponseError):
    status_code = STATUS.NOT_FOUND

class MethodNotAllowed(HttpResponseError):
    def __init__(self, permitted_methods, *args, **kwargs):
        super(MethodNotAllowed, self).__init__(*args, **kwargs)
        self['Allow'] = ', '.join(permitted_methods)

    status_code = STATUS.METHOD_NOT_ALLOWED

class NotAcceptable(HttpResponseError):
    status_code = STATUS.NOT_ACCEPTABLE

class ProxyAuthenticationRequired(HttpResponseError):
    status_code = STATUS.PROXY_AUTHENTICATION_REQUIRED

class RequestTimeout(HttpResponseError):
    status_code = STATUS.REQUEST_TIMEOUT

class Conflict(HttpResponseError):
    status_code = STATUS.CONFLICT

class Gone(HttpResponseError):
    status_code = STATUS.GONE

class LengthRequired(HttpResponseError):
    status_code = STATUS.LENGTH_REQUIRED

class PreconditionFailed(HttpResponseError):
    status_code = STATUS.PRECONDITION_FAILED

class RequestEntityTooLarge(HttpResponseError):
    status_code = STATUS.REQUEST_ENTITY_TOO_LARGE

class RequestURITooLong(HttpResponseError):
    status_code = STATUS.REQUEST_URI_TOO_LONG

class UnsupportedMediaType(HttpResponseError):
    status_code = STATUS.UNSUPPORTED_MEDIA_TYPE

class RequestedRangeNotSatisfiable(HttpResponseError):
    status_code = STATUS.REQUESTED_RANGE_NOT_SATISFIABLE

class ExpectationFailed(HttpResponseError):
    status_code = STATUS.EXPECTATION_FAILED

#
# Server Error (5xx)
#

class HttpResponseServerError(BaseHttpResponse):
    '''A base class for 5xx responses.'''

class InternalServerError(HttpResponseServerError):
    status_code = STATUS.INTERNAL_SERVER_ERROR

class NotImplemented(HttpResponseServerError):
    status_code = STATUS.NOT_IMPLEMENTED

class BadGateway(HttpResponseServerError):
    status_code = STATUS.BAD_GATEWAY

class ServiceUnavailable(HttpResponseServerError):
    status_code = STATUS.SERVICE_UNAVAILABLE

class GatewayTimeout(HttpResponseServerError):
    status_code = STATUS.GATEWAY_TIMEOUT

class HttpVersiontNotSupported(HttpResponseServerError):
    status_code = STATUS.HTTP_VERSION_NOT_SUPPORTED

#
# General Helpers
#

class JsonResponse(HttpResponse):
    '''Handy shortcut for dumping JSON data'''
    def __init__(self, content, *args, **kwargs):
        kwargs.setdefault('content_type', 'application/json')
        super(JsonResponse, self).__init__(json.dumps(content), *args, **kwargs)
