import json

from django.db.models.query import QuerySet
from django.http import HttpResponse, Http404
from django.shortcuts import render_to_response
from django.template import loader, Context
from django.utils.html import escape

from functions import get_object_data, get_object_list_data
import xml

__NO_CACHE = 'max-age=0, no-cache, no-store, must-revalidate'

def __default_dumps(obj):
	if isinstance(obj, QuerySet):
		# Dumping a QuerySet with this function shouldn't happen, but just in case
		return tuple(obj)
	else:
		data = get_object_data(obj)
		if data:
			return data
		else:
			raise TypeError(repr(obj) + " is not serializable")

def __default_list_dumps(obj):
	if isinstance(obj, QuerySet):
		return tuple(obj)
	else:
		data = get_object_list_data(obj)
		if data:
			return data
		else:
			raise TypeError(repr(obj) + " is not serializable")

def set_response_headers(request, **kwargs):
	if not hasattr(request, 'api_response_headers'):
		request.api_response_headers = kwargs.copy()
	else:
		request.api_response_headers.update(kwargs)

def apply_response_headers(request, response):
	if hasattr(request, 'api_response_headers'):
		for header, value in request.api_response_headers.iteritems():
			response[header] = value
	# Django never automatically adds Content-Length to a response unless ConditionalGetMiddleware is used, so do it here in case the middleware isn't being used
	response['Content-Length'] = str(len(response.content))
	response['Cache-Control'] = __NO_CACHE

def render_data(request, data, status=200):
	"""Render data as xml or json based on the request."""
	if request.api:
		default = __default_list_dumps if isinstance(data, (tuple, list, set, QuerySet)) else __default_dumps
		if not request.api_json:
			response = HttpResponse(content_type='application/xml', status=status)
			xml.dumps(data, response, default=default)
		elif request.api_json is not True:
			response = HttpResponse(content_type='text/javascript', status=status)
			if request.api_callback:
				response.write(request.api_json + '(')
				response.write(json.dumps(data, default=default))
				response.write(');')
			else:
				response.write(request.api_json + ' = ')
				response.write(json.dumps(data, default=default))
				response.write(';')
		else:
			response = HttpResponse(json.dumps(data, default=default), content_type='application/json', status=status)
		apply_response_headers(request, response)
		return response
	else:
		raise Http404

def render_error(request, message, status, code=None):
	"""Render and error message and code."""
	if code is None:
		code = status
	if request.api:
		if not request.api_json:
			response = HttpResponse('<?xml version="1.0" encoding="UTF-8" ?><error><code>%d</code><message>%s</message></error>' % (code, escape(message)), status=status, content_type='application/xml')
		elif request.api_json is not True:
			if request.api_callback:
				response = HttpResponse('%s({"code":%d,"message":%s});' % (request.api_json, code, json.dumps(message)), status=200, content_type='text/javascript')
			else:
				response = HttpResponse('%s={"code":%d,"message":%s};' % (request.api_json, code, json.dumps(message)), status=200, content_type='text/javascript')
		else:
			response = HttpResponse('{"code":%d,"message":%s}' % (code, json.dumps(message)), status=status, content_type='application/json')
		apply_response_headers(request, response)
		return response
	else:
		if code == 404:
			raise Http404
		else:
			return HttpResponse('<html><head><title>Error %d</title></head><body><b>Error %d</b> - %s</body></html>' % (status, code, message), status=status, content_type='text/html')

def render_empty(request):
	"""Render an appropriate empty response."""
	# jQuery will report an error if the empty response isn't given as {} or null
	# see dataType "json" here https://api.jquery.com/jQuery.ajax/
	# 204 No Content requires that nothing be in the body, so cannot be used for a json response, see https://tools.ietf.org/html/rfc2616#section-10.2.5
	if request.api_json:
		response = HttpResponse('null', content_type='application/json')
	else:
		response = HttpResponse(status=204)
	apply_response_headers(request, response)
	return response
