from parallels.common import MigrationError
from parallels.utils import format_multiline_list
from parallels.utils.ip import is_ipv4, is_ipv6


class IPMapping(object):
	"""Class representing IP mapping"""

	def __init__(self):
		self._ip_mapping = {}

	def set(self, source_ip, target_ip):
		"""Set mapping of source IP address to target IP address

		:type source_ip: basestring
		:type target_ip: basestring
		:rtype: None
		"""
		self._ip_mapping[source_ip] = target_ip

	def get(self, source_ip):
		"""Get target IP address for specified source IP address.
		If there is no such source IP in the mapping - return None.

		:type source_ip: basestring
		:rtype: basestring | None
		"""
		return self._ip_mapping.get(source_ip)


class IPMappingReader(object):
	"""Read IP mapping from a file"""
	def __init__(self, target_ips):
		"""
		:type target_ips: set[basestring]
		"""
		self._target_ips = target_ips

	def read(self, file_obj):
		"""Read IP mapping from a file
		:param file_obj: file-like object
		:rtype: parallels.common.ip_mapping.IPMapping
		"""
		errors = []
		mapping = IPMapping()

		for line_number, line in enumerate(file_obj, start=1):
			line = line.strip()

			if line == '':
				# skip empty lines
				continue

			line_parts = line.split()
			if len(line_parts) != 2:
				errors.append(
					"Line #%s: invalid number of arguments found on line. "
					"Expected: <source-ip> <target-ip>. For example: 10.52.1.17 10.52.12.32" % line_number
				)
				continue

			source_ip, target_ip = line_parts
			source_ip = source_ip.strip()
			target_ip = target_ip.strip()

			if not is_ipv4(source_ip) and not is_ipv6(source_ip):
				errors.append(
					"Line #%s: invalid source IP address '%s'. Specify valid IPv4 or IPv6 address." % (
						line_number, source_ip
					)
				)
				continue

			if not is_ipv4(target_ip) and not is_ipv6(target_ip):
				errors.append(
					"Line #%s: invalid target IP address '%s'. Specify valid IPv4 or IPv6 address." % (
						line_number, target_ip
					)
				)
				continue

			if is_ipv4(source_ip) and is_ipv6(target_ip) or is_ipv6(source_ip) and is_ipv4(target_ip):
				errors.append(
					"Line #%s: IP addresses of different types can not be mapped. "
					"Specify either both IPv4 addresses, or both IPv6 addresses." % (
						line_number
					)
				)
				continue

			if mapping.get(source_ip) is not None:
				errors.append(
					"Line #%s: source IP address '%s' was already specified before." % (
						line_number, source_ip
					)
				)
				continue

			if target_ip not in self._target_ips:
				errors.append(
					"Line #%s: target IP address '%s' does not exist on target server." % (
						line_number, target_ip
					)
				)
				continue

			mapping.set(source_ip, target_ip)

		if len(errors) > 0:
			raise MigrationError("Failed to read IP mapping:\n%s" % format_multiline_list(errors))

		return mapping