# -*- coding: utf-8 -*-

def _getProductStates_hash(self, objectIds=[], options={}, productType=None):
	if productType:
		productType = forceProductType(productType)
	else:
		productType = None
	result = {}

	productHasPriority = {}
	priorityClasses = {}

	for product in self.product_getObjects():
		prio = product.priority
		if not prio:
			prio = 0
		productHasPriority[product.id]= prio

		if not priorityClasses.has_key(prio):
			priorityClasses[prio] = []
		priorityClasses[prio].append(product.id)

	for productOnClient in self.productOnClient_getObjects(clientId = objectIds, productType = productType):
		if not result.has_key(productOnClient.clientId):
			result[productOnClient.clientId] = []
		lastStateChange = productOnClient.modificationTime or u''
		match = re.search('^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$', lastStateChange)
		if match:
			lastStateChange = u'%s%s%s%s%s%s' % ( match.group(1), match.group(2), match.group(3), match.group(4), match.group(5), match.group(6) )

		result[productOnClient.clientId].append({
			u"lastStateChange":     lastStateChange,
			u"productVersion":      productOnClient.productVersion or u'',
			u"packageVersion":      productOnClient.packageVersion or u'',
			u"targetConfiguration": productOnClient.targetConfiguration or u'',
			u"lastAction":          productOnClient.lastAction or u'',
			u"installationStatus":  productOnClient.installationStatus,
			u"actionRequest":       productOnClient.actionRequest,
			u"actionProgress":      productOnClient.actionProgress or u'',
			u"actionResult":        productOnClient.actionResult or u'',
			u"productId":           productOnClient.productId,
			u"productPriority":	productHasPriority.get(productOnClient.productId, 0),
			u"actionSequence": 	productHasPriority.get(productOnClient.productId, 0)
			#u"actionSequence": 	productOnClient.actionSequence
		})
	return result

def getLocalBootProductStates_hash(self, objectIds=[], options={}):
	return self._getProductStates_hash(objectIds = objectIds, options = options, productType = 'LocalbootProduct')

def getNetBootProductStates_hash(self, objectIds=[], options={}):
	return self._getProductStates_hash(objectIds = objectIds, options = options, productType = 'NetbootProduct')

def getProductStates_hash(self, objectIds=[], options={}):
	return self._getProductStates_hash(objectIds = objectIds, options = options)

def getProductProperties(self, depotId, objectIds):
	depotId = forceHostId(depotId)
	objectIds = forceObjectIdList(objectIds)
	objectIds.append(depotId)

	hashPerObject = {}
	for objectId in objectIds:
		hashPerObject[objectId] = {}

	for pps in self.productPropertyState_getObjects(objectId = depotId):
		values = forceList(pps.getValues())
		#take depot values as default for each client
		for objectId in objectIds:
			if not hashPerObject[objectId].has_key(pps.productId):
				hashPerObject[objectId][pps.productId] = {}
			hashPerObject[objectId][pps.productId][pps.propertyId] = values

	for pps in self.productPropertyState_getObjects(objectId = objectIds):
		if hashPerObject.get(pps.objectId, {}).get(pps.productId, {}).has_key(pps.propertyId):
			hashPerObject[pps.objectId][pps.productId][pps.propertyId] = forceList(pps.getValues())

	return hashPerObject


def _getProducts_hash(self, depotIds=[]):
	result = {}
	for depotId in depotIds:
		result[depotId] = {}

	for productOnDepot in self.productOnDepot_getObjects(depotId = depotIds):
		if not result.has_key(productOnDepot.depotId):
			result[productOnDepot.depotId] = {}
		product = products.get(productOnDepot.productId, {}).get(productOnDepot.productVersion, {}).get(productOnDepot.packageVersion)
		if not product:
			raise BackendMissingDataError(u"Product with id '%s', productVersion '%s', packageVersion '%s' not found" \
				% (productOnDepot.productId, productOnDepot.productVersion, productOnDepot.packageVersion))
		result[productOnDepot.depotId][productOnDepot.productId] = self._productToHash(product)
	return result


def getProductsInDepot(self, depotId=None, productType=None):
	depotIds = self.host_getIdents(type = 'OpsiDepotserver', returnType = 'unicode')
	products = self.product_getObjects()

	if depotId in depotIds:
		filteredProducts = []
		productsByIdAndVersion = {}
		for product in products:
			if not productsByIdAndVersion.has_key(product.id):
				productsByIdAndVersion[product.id] = {}
			if not productsByIdAndVersion[product.id].has_key(product.productVersion):
				productsByIdAndVersion[product.id][product.productVersion] = {}
			productsByIdAndVersion[product.id][product.productVersion][product.packageVersion] = product

		for productOnDepot in self.productOnDepot_getObjects(depotId = depotId, productType = productType):
			product = productsByIdAndVersion.get(productOnDepot.productId, {}).get(productOnDepot.productVersion, {}).get(productOnDepot.packageVersion)
			if product:
				filteredProducts.append(product)
		products = filteredProducts

	return products

def getProductInfos_hash(self, depotId=None):
	products = self.getProductsInDepot(depotId)

	result = {}
	for product in products:
		result[product.id] = {
			'name':            product.name,
			'description':     product.description,
			'advice':          product.advice,
			'priority':        product.priority,
			'productVersion':  product.productVersion,
			'packageVersion':  product.packageVersion,
			'actions':         [ u'none' ]
		}
		if product.setupScript:     result[product.id]['actions'].append(u'setup')
		if product.uninstallScript: result[product.id]['actions'].append(u'uninstall')
		if product.updateScript:    result[product.id]['actions'].append(u'update')
		if product.alwaysScript:    result[product.id]['actions'].append(u'always')
		if product.onceScript:      result[product.id]['actions'].append(u'once')
		if product.customScript:    result[product.id]['actions'].append(u'custom')
	return result

productOrderingCache = {}
def getProductOrdering(self, depotId, sortAlgorithm = None):
	import OPSI.SharedAlgorithm
	global productOrderingCache
	if not sortAlgorithm:
		sortAlgorithm = "algorithm2"
		configs = self.config_getObjects(id = "product_sort_algorithm")
		if configs and ("product_on_client" in configs[0].getDefaultValues() or "algorithm1" in configs[0].getDefaultValues()):
			sortAlgorithm = "algorithm1"

	productOnClients    = []
	availableProducts   = []
	productDependencies = []
	productIds          = []

	productsByIdAndVersion = {}
	for product in self.product_getObjects(type = "LocalbootProduct"):
		if not productsByIdAndVersion.has_key(product.id):
			productsByIdAndVersion[product.id] = {}
		if not productsByIdAndVersion[product.id].has_key(product.productVersion):
			productsByIdAndVersion[product.id][product.productVersion] = {}
		productsByIdAndVersion[product.id][product.productVersion][product.packageVersion] = product

	productsDependenciesByIdAndVersion = {}
	for productDependency in self.productDependency_getObjects(productAction = "setup"):
		if not productsDependenciesByIdAndVersion.has_key(productDependency.productId):
			productsDependenciesByIdAndVersion[productDependency.productId] = {}
		if not productsDependenciesByIdAndVersion[productDependency.productId].has_key(productDependency.productVersion):
			productsDependenciesByIdAndVersion[productDependency.productId][productDependency.productVersion] = {}
		if not productsDependenciesByIdAndVersion[productDependency.productId][productDependency.productVersion].has_key(productDependency.packageVersion):
			productsDependenciesByIdAndVersion[productDependency.productId][productDependency.productVersion][productDependency.packageVersion] = []
		productsDependenciesByIdAndVersion[productDependency.productId][productDependency.productVersion][productDependency.packageVersion].append(productDependency)

	for productOnDepot in self.productOnDepot_getObjects(depotId = depotId, productType = "LocalbootProduct"):
		product = productsByIdAndVersion.get(productOnDepot.productId, {}).get(productOnDepot.productVersion, {}).get(productOnDepot.packageVersion)
		if not product:
			continue
		availableProducts.append(product)
		productIds.append(product.id)
		if not product.setupScript:
			continue
		productDependencies.extend(productsDependenciesByIdAndVersion.get(productOnDepot.productId, {}).get(productOnDepot.productVersion, {}).get(productOnDepot.packageVersion, []))

	productIds.sort()
	
	if sortAlgorithm == "algorithm1":
		sortedList = OPSI.SharedAlgorithm.generateProductSequence_algorithm1(availableProducts, productDependencies)
	else:
		sortedList = OPSI.SharedAlgorithm.generateProductSequence_algorithm2(availableProducts, productDependencies)	
	
	return {
		"not_sorted": productIds,
		"sorted":     sortedList
	}

def getConfigOptions_hash(self):
	result = {}

	for config in self.config_getObjects():
		result[config.id] = config

	return result

def getConfigs(self, objectIds):
	objectIds = forceObjectIdList(objectIds)

	result = {}
	addConfigStateDefaults = self._backend.backend_getOptions().get('addConfigStateDefaults', False)
	try:
		self._backend.backend_setOptions( { 'addConfigStateDefaults': True } )
		for configState in self.configState_getObjects( objectId = objectIds ):
			if not result.has_key(configState.objectId):
				result[configState.objectId] = {}
			result[configState.objectId][configState.configId] = configState.values
	finally:
		self._backend.backend_setOptions( { 'addConfigStateDefaults': addConfigStateDefaults } )
	return result

def getConfig_hash(self, objectId=None):
	if objectId:
		objectId = forceObjectId(objectId)

	result = {}

	if objectId:
		addConfigStateDefaults = self._backend.backend_getOptions().get('addConfigStateDefaults', False)
		try:
			self._backend.backend_setOptions( { 'addConfigStateDefaults': True } )
			for configState in self.configState_getObjects(objectId = objectId):
				result[configState.configId] = configState.getValues()
		finally:
			self._backend.backend_setOptions( { 'addConfigStateDefaults': addConfigStateDefaults } )
	else:
		for config in self.config_getObjects():
			result[config.id] = config.defaultValues
	return result

def getSoftwareAudit(self, objectId):
	objectId = forceObjectId(objectId)
	result = []
	software = {}
	for sw in self.auditSoftware_getObjects():
		software[ sw.getIdent(returnType = 'unicode') ] = sw
	for sw in self.auditSoftwareOnClient_getObjects(clientId = objectId):
		item = sw.toHash()
		ident = BaseObject.identSeparator.join([sw.name, sw.version, sw.subVersion, sw.language, sw.architecture])
		item["windowsSoftwareId"] = software[ident].windowsSoftwareId
		result.append(item)
	return result

def getAuditSoftwareUsage(self):
	result = {}
	for sw in self.auditSoftwareOnClient_getHashes():
		ident = BaseObject.identSeparator.join([sw['name'], sw['version'], sw['subVersion'], sw['language'], sw['architecture']])
		if not result.has_key(ident):
			result[ident] = []
		result[ident].append(sw['clientId'])
	return result

def getClientsWithOtherProductVersion_list(self, productId, productVersion, packageVersion):
	result = []

	for inst in self.productOnClient_getHashes([], productId=productId, installationStatus="installed"):
		if (inst.get("productVersion") != productVersion) or (inst.get("packageVersion") != packageVersion):
			result.append(inst.get("clientId"))

	return result


def getClientsWithFailed_hash(self):
	result = {}
	fil = {}
	fil["actionResult"]="failed"
	installations = self.productOnClient_getHashes([], **fil)
	for inst in installations:
		clientId=inst.get("clientId")
		if not result.has_key(clientId):
			result[clientId]=[]
		result[clientId].append(inst.get("productId"))
	return result

def getClientsWithFailed_list(self, interestingProducts=[]):
	resultmap = {}
	fil = {}
	fil["actionResult"]="failed"
	fil["productId"]=interestingProducts
	installations = self.productOnClient_getHashes([], **fil)
	for inst in installations:
		clientId=inst.get("clientId")
		if not resultmap.has_key(clientId):
			resultmap[clientId]=""
	return resultmap.keys()


def setRights(self, path=None):
	"""
	Setting rights for a specified path.
	If no path is given it will try to set the rights for the current depot.

	The current implementation requires "sudo opsi-setup --patch-sudoers-file"
	to be run before.
	"""
	import subprocess

	if path is None:
		oldDepotPath = u'/opt/pcbin/install/'
		newDepotPath = u'/var/lib/opsi/depot/'
		try:
			if os.path.exists(newDepotPath) and os.path.islink(newDepotPath):
				linkedPath = os.readlink(newDepotPath)
				if os.path.isabs(linkedPath):
					path = linkedPath
				else:
					path = os.path.join(os.path.dirname(newDepotPath), linkedPath)
			else:
				path = oldDepotPath
		except OSError, oserr:
			if 'operation not permitted' in str(oserr).lower():
				path = oldDepotPath
			else:
				raise oserr

	if not os.path.exists(path):
		raise Exception('The path "{0}" does not exist.'.format(path))

	logger.debug('Going to set rights for path "{0}"'.format(path))
	callResult = subprocess.call(['sudo', 'opsi-set-rights', path])
	logger.debug('Finished setting rights. Exit Code: {0}'.format(callResult))

	if callResult:
		raise Exception("Setting rights on {0} failed. Did you run "
						"'opsi-setup --patch-sudoers-file'?".format(path))

	return 'Changing rights at "{0}" successful.'.format(path)

def getPossibleMethods_listOfHashes(self):
	possibleMethods = []
	for method in self.backend_getInterface():
		compatible = True
		for param in method["params"]:
			if param.startswith('**'):
				compatible = False
				break
		if compatible:
			possibleMethods.append({"name": method["name"], "params": method["params"]})
	return possibleMethods

def getServerIds_list(self):
	return self.host_getIdents(type = 'OpsiConfigserver')

def getDomain(self):
	from OPSI.Backend.Backend import OPSI_GLOBAL_CONF
	return forceUnicodeLower('.'.join(getfqdn(conf=OPSI_GLOBAL_CONF).split('.')[1:]))

def getOpsiHWAuditConf(self, locale=None):
	return self.auditHardware_getConfig(locale)
