from collections import namedtuple
from ConfigParser import NoOptionError
from parallels.core import MigrationError
from parallels.core.registry import Registry

Auth = namedtuple('Auth', ('username', 'password'))
HttpConnectionSettings = namedtuple('HttpConnectionSettings', ('url', 'auth'))


class ConfigSection(object):
	"""Dictionary-like interface to configuration section.

	Also allows to refer part of keys starting with given prefix.
	"""
	def __init__(self, config, section_name, prefix=''):
		self.config = config
		self.section_name = section_name
		self.prefix = prefix

	def __getitem__(self, name):
		return self.config.get(self.section_name, self.prefix+name)

	def get(self, name, default=None):
		return self.get_or_exec(name, lambda: default)

	def get_or_exec(self, name, get_default):
		try:
			return self.config.get(self.section_name, self.prefix+str(name))
		except NoOptionError:
			return get_default()

	def keys(self):
		return [key for key in self.config.options(self.section_name) if key.startswith(self.prefix)]

	def items(self):
		return [(k, v) for (k, v) in self.config.items(self.section_name) if k.startswith(self.prefix)]

	def __contains__(self, name):
		return self.config.has_option(self.section_name, self.prefix+name)

	def prefixed(self, prefix):
		return ConfigSection(self.config, self.section_name, self.prefix+prefix)

	def with_defaults(self, defaults):
		return ConfigSectionWithDefaults(self, defaults)


class ConfigSectionWithDefaults(object):
	"""Configuration section with default values.
	"""
	def __init__(self, section, defaults):
		self.section = section
		self.defaults = defaults

	def __getitem__(self, name):
		return self.section.get_or_exec(name, lambda: self.defaults[name])

	def get(self, name, default=None):
		return self.get_or_exec(name, lambda: default)

	def get_or_exec(self, name, get_default):
		try:
			return self.section.get_or_exec(name, lambda: self.defaults[name])
		except ValueError:
			return get_default()

	def keys(self):
		return set(self.section.keys()) | set(self.defaults.keys())

	def __contains__(self, name):
		return name in self.section or name in self.defaults

	def with_defaults(self, defaults):
		return ConfigSectionWithDefaults(self, defaults)


def config_get(config, section, option, default):
	if option in config.options(section):
		return config.get(section, option)
	else:
		return default


def get_option(section, option, default):
	context = Registry.get_instance().get_context()
	if context is not None and option in context.config.options(section):
		return context.config.get(section, option)
	else:
		return default


def read_auth(section):
	return Auth(section.get('username'), section.get('password'))


def read_url(section):
	import urlparse
	
	if 'url' in section:
		url = section['url']
		
	else:
		host = section['host']
		protocol = section['protocol']
		port = int(section['port'])
		path = section['path']
		
		url = urlparse.urlunparse((protocol, '%s:%d' % (host, port), path, '', '', ''))
		
	return url


def read_http_connection_settings(section):
	return HttpConnectionSettings(url=read_url(section), auth=read_auth(section))


def get_sections_list(config, section, option):
	"""Parse list of sections in specified option of configuration file and check that each section actually exists

	Consider we have list of sections in option
	=============================================
	[GLOBAL]
	source-servers: source1, source2, source3
	=============================================

	If you call this function (for with arguments (config, 'GLOBAL', 'source-servers')), it will:
	1) Return list of sections: ['source1', 'source2', 'source3']
	2) Check that each of these sections exists in configuration file.
	If section does not exist - exception will be raised. For example, if there is no section like:
	=============================================
	[source3]
	options
	=============================================
	exception will be raised.

	:rtype: list[basestring]
	"""
	if option in config.options(section):
		list_str = config.get(section, option)
		list_str = list_str.strip()
		if list_str == '':
			return []
		sections = [s.strip() for s in list_str.split()]

		for section in sections:
			if not config.has_section(section):
				raise MigrationError(
					"Section '{section}' specified in '{option}' option does not exist in config".format(
						section=section, option=option
					)
				)
		return sections
	else:
		return []