from java.util import List, ArrayList
from com.xebialabs.deployit.core.api.dto import ConfigurationItemDto, ConfigurationItemIds, ConfigurationItemDtos, Deployment

###############
# ci.py
#
# This CLI extension module presents a more user-friendly interface to users of the CLI. Objects passing to and from the
# server are automatically wrapped and unwrapped in a Ci() instance. This Ci instance makes it possible to access regular
# properties and synthetic properties in the same way.
# Ci instances also contain methods for control tasks defined on the CI, if proxies are exposed.
#
# Example usage:
#
# Accessing CI synthetic values using dot-notation:
#
#	  env = factory.configurationItem('Environments/testEnv', 'udm.Environment')
#	  env.members = HashSet()
#	  # If 'owner' is a synthetic field defined on udm.Environment:
#	  env.owner = 'OPS'
#	  repository.create(env)
#
# Invoking control tasks:
#
#	  server = factory.configurationItem('Infrastructure/testServer', 'www.Webserver')
#	  # If 'start' is a control task (synthetic or otherwise) on www.Webserver:
#	  server.start()


###############
# Descriptor lookup map

# Map of CI type name to descriptor
descriptorLookup = {}

# Initialize descriptor lookup
try:
	if proxies != None:
		for descriptor in proxies.referenceData.list().entity.descriptors:
			descriptorLookup[descriptor.simpleName] = descriptor
except:
	print "Proxies not exposed so control tasks won't be available on the Cis."


###############
# Plumbing code to decorate, wrap & unwrap objects passing to and from the server

class CliObjectWrapperDecorator:
	delegate = None
	def __init__(self, delegate):
		self.delegate = delegate

	def __getattr__(self,name):
		return self.decorator(getattr(self.delegate,name))

	def decorator(self, target):
		def wrapper(*args):
			#print 'Calling function "%s" with arguments %s' % (target.__name__, args)
			processedArgs = []
			for a in args:
				if isinstance(a, Ci):
					processedArgs.append(WrapperUnwrapper._unwrap(a))
				elif isinstance(a, ConfigurationItemDtos):
					a.objects = WrapperUnwrapper._unwrapList(a.objects)
					processedArgs.append(a)
				elif isinstance(a, DeploymentWrapper):
					processedArgs.append(a.unwrap())
				else:
					processedArgs.append(a)

			return WrapperUnwrapper._wrap(target(*processedArgs))

		return wrapper


# Class that wraps and unwraps objects retrieved from the Deployit server
class WrapperUnwrapper():
	# Wrap an object received from the server in a Ci if appropriate
	def _wrap(serverCi):
		if serverCi is None:
			return None
		if isinstance(serverCi,ConfigurationItemDto):
			return Ci(serverCi)
		if isinstance(serverCi,ConfigurationItemDtos):
			serverCi.objects = WrapperUnwrapper._wrapList(serverCi.objects)
			return serverCi
		if isinstance(serverCi,List):
			return WrapperUnwrapper._wrapList(serverCi)
		if isinstance(serverCi,Deployment):
			return DeploymentWrapper(serverCi.upgrade, Ci(serverCi.deployedApplication), WrapperUnwrapper._wrapList(serverCi.deployeds))
		return serverCi
	_wrap = staticmethod(_wrap)

	def _wrapList(cis):
		ciList = []
		for item in cis:
			if isinstance(item,unicode):
				ciList.append(str(item))
			else:
				ciList.append(Ci(item))
		return ciList
	_wrapList = staticmethod(_wrapList)

	# Unwrap a Ci instance so it can be passed back to the server
	def _unwrap(ci):
		if isinstance(ci,ConfigurationItemDto):
			return ci
		else:
			return ci._ci
	_unwrap = staticmethod(_unwrap)

	def _unwrapList(cis):
		serverCiList = []
		for item in cis:
			if isinstance(item,unicode):
				serverCiList.append(str(item))
			else:
				serverCiList.append(WrapperUnwrapper._unwrap(item))
		return serverCiList
	_unwrapList = staticmethod(_unwrapList)

	
###############
# Classes that wrap objects to/from the server and give them an easier to use interface

# Wrap CIs
class Ci():
	
	def __init__(self,delegate):
		self.__dict__["_ci"] = delegate
		
		# Create control task methods (if proxies exposed)
		try:
			if delegate.type in descriptorLookup:
				descriptor = descriptorLookup[delegate.type]
				controlTasks = descriptor.controlTasks
				for task in controlTasks:
					self.__dict__[task.name] = lambda: deployit.executeControlTask(task.name, self._ci)
		except:
			pass
	
	def __getattr__(self,name):
		if name == "_ci":
			return self.__dict__["_ci"]
		if name == "id":
			return self._ci.id
		elif name == "type":
			return self._ci.type
		elif name == "values":
			return self._ci.values
		elif name == "lastModified":
			return self._ci.lastModified
		elif name == "overrideLastModified":
			return self._ci.overrideLastModified
		elif name == "validations":
			return self._ci.validations
		elif self._ci.values is None:
			self._ci.values = {}
		return self._ci.values[name]
	
	def __setattr__(self,name,val):
		if name == "id":
			self._ci.id = val
		elif name == "type":
			self._ci.type = val
		elif name == "values":
			self._ci.values = val
		elif name == "overrideLastModified":
			self._ci.overrideLastModified = val
		else:
			if self._ci.values is None:
				self._ci.values = {}
			self._ci.values[name] = val
	
	def __str__(self):
		return str(self._ci)

	def toString(self):
		return str(self._ci)
		
	def __repr__(self):
		return repr(self._ci)
	
	def describe(self):
		deployit.describe(self._ci.type)

	def prettyprint(self):
		deployit.print(self._ci)
		
	def create(self):
		return repository.create(self)

	def update(self):
		return repository.update(self)

	def delete(self):
		repository.delete(self.id)
		
	def _resolvePartialNameInListAttr(self,listPropertyName,partialName):
		found = None
		for member in self._ci.values[listPropertyName]:
			if member.endswith(partialName):
				if found is None:
					found = member;
				else:
					raise Exception("Partial name cannot be resolved because there is more that 1 potential match")
		return found

	def _searchByTypeAndParent(ciType,parent):
		return repository.search(ciType,parent)
	_searchByTypeAndParent = staticmethod(_searchByTypeAndParent)

	def _searchByType(ciType):
		return repository.search(ciType)
	_searchByType = staticmethod(_searchByType)



# Wrap the Deployment DTO object
class DeploymentWrapper():
	deployedApplication = None
	deployeds = None
	upgrade = False

	def __init__(self, upgrade, deployedApplication, deployeds):
		self.upgrade = upgrade
		self.deployedApplication = deployedApplication
		self.deployeds = deployeds

	def prettyprint(self):
		deployit.print(self.deployedApplication)
		for deployed in self.deployeds:
			deployit.print(deployed)

	def unwrap(self):
		serverDeployment = Deployment()
		serverDeployment.upgrade = self.upgrade
		serverDeployment.deployedApplication = WrapperUnwrapper._unwrap(self.deployedApplication)
		serverDeployment.deployeds = WrapperUnwrapper._unwrapList(self.deployeds)
		return serverDeployment



###############
# Installation of the wrappers

factory = CliObjectWrapperDecorator(factory)
repository = CliObjectWrapperDecorator(repository)
deployit = CliObjectWrapperDecorator(deployit)
deployment = CliObjectWrapperDecorator(deployment)
security = CliObjectWrapperDecorator(security)
