from __future__ import absolute_import
import sys
import logging
import urllib2
import ssl
from contextlib import closing
import xml.etree.ElementTree as et
from StringIO import StringIO
from parallels.core.utils.config_utils import get_option
from parallels.core.utils.common.logging import BleepingLogger
from parallels.core import messages


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):
	def __init__(self, url):
		log_context_length = get_option(
			'GLOBAL', 'log-message-context-length', 10)
		self.logger = BleepingLogger(
			logging.getLogger(__name__), xml_serializer=self._compose_xml,
			context_length=log_context_length)
		self.url = url
		
	def send_raw(self, data, extra=None):
		"""Send raw string.
		
		Returns a file-like object. It is caller's responsibility to close it.
		"""
		request = self._prepare_request_object(data, extra)
		context = self._prepare_context()
		try:
			if context is None:
				return urllib2.urlopen(request)
			return urllib2.urlopen(request, context=context)
		except urllib2.HTTPError as e:
			content = e.fp.read() if e.fp is not None else None
			raise HttpClientError(e, content)
		
	def send_xml(self, xml, extra=None):
		"""Serialize XML object and send an API request."""
		self.logger.debug(messages.DEBUG_API_REQUEST_TO, self.url, xml)
		request = self._compose_xml(xml)
		response = self.send_raw(request, extra)
		with closing(response):
			tree = self._parse_xml(response)
		self.logger.debug(messages.DEBUG_API_RESPONSE_FROM, self.url, tree)
		return tree
	
	def _compose_xml(self, xml):
		"""Serialize ElementTree XML object to string."""
		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)

	@staticmethod
	def _prepare_context():
		if sys.version_info.major < 2 or sys.version_info.minor < 7 or sys.version_info.micro < 9:
			return None
		context = ssl.create_default_context()
		context.check_hostname = False
		context.verify_mode = ssl.CERT_NONE
		return context