from __future__ import absolute_import
import logging
import urllib2
from contextlib import closing
import xml.etree.ElementTree as et
from StringIO import StringIO

class HttpClientError(Exception):
	def __init__(self, cause, content):
		self.cause = cause
		self.content = content

	def __str__(self): return repr(self)

	def __repr__(self):
		return "HttpClientError(%s, %r)" % (self.cause, self.content)

class HttpXmlClient(object):
	logger = logging.getLogger(__name__)
	
	def __init__(self, url):
		self.url = url
		
	def send_raw(self, data, extra=None):
		"""Send raw string.
		
		Returns file-like object. It is caller's responsibility to close it.
		"""
		self.logger.debug(u"Request to %s: %s\n", self.url, unicode(data, encoding='utf-8'))
		request = self._prepare_request_object(data, extra)
		try:
			return urllib2.urlopen(request)
		except urllib2.HTTPError as e:
			content = e.fp.read() if e.fp is not None else None
			raise HttpClientError(e, content)
	
	def send_raw_and_read(self, data, extra=None):
		"""Send raw string and read whole response.
		
		Returns response as string.
		"""
		with closing(self.send_raw(data, extra)) as response:
			res = response.read()
			self.logger.debug(u"Response from %s: %s\n", self.url, unicode(res, encoding='utf-8'))
			return res
		
	def send_xml(self, xml, extra=None):
		request = self._compose_xml(xml)
		
		if self.logger.isEnabledFor(logging.DEBUG):
			# 'send_raw_and_read' will log response.
			response = StringIO(self.send_raw_and_read(request, extra))
		else:
			response = self.send_raw(request, extra)

		with closing(response):
			tree = self._parse_xml(response)
		
		return tree
	
	def _compose_xml(self, xml):
		with closing(StringIO()) as buf:
			xml.write(buf, encoding='utf-8', xml_declaration=True)
			return buf.getvalue()

	def _parse_xml(self, response):
		tree = et.ElementTree()
		tree.parse(response)
		return tree

	def _prepare_request_object(self, data, extra=None):
		headers = {
			'Content-Type': 'text/xml',
			'Pragma': 'no-cache',
		}
		return urllib2.Request(self.url, data, headers)
		
