/*<copyright                                                          */
/* notice="cics-rm-source-program"                                    */
/* pids="CA0U"                                                        */
/* years="2014,2015"                                                  */
/* crc="1921302770" >                                                 */
/* 	Licensed Materials - Property of IBM                              */
/* 	"Restricted Materials of IBM"                                     */
/* 	CA0U                                                              */
/* 	(C) Copyright IBM Corp. 2014, 2015                                */
/* 	US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */
/* </copyright>                                                       */
package com.ibm.cics.ucd

import org.eclipse.core.runtime.IStatus
import org.eclipse.core.runtime.MultiStatus

import com.ibm.cics.core.model.CICSSystemManagerException
import com.ibm.cics.core.model.Context
import com.ibm.cics.core.model.ICPSM
import com.ibm.cics.core.model.ScopedContext
import com.ibm.cics.core.model.context.CPSMDefinitionPrimaryKey
import com.ibm.cics.core.model.internal.CICSResource
import com.ibm.cics.core.model.CICSObjectPrimaryKey
import com.ibm.cics.core.ui.ExceptionMessageHelper
import com.ibm.cics.model.ICICSAttribute
import com.ibm.cics.model.ICICSDefinition
import com.ibm.cics.model.ICICSObject
import com.ibm.cics.model.ICICSType
import com.ibm.cics.model.IDefinition
import com.ibm.cics.sm.comm.ICICSAction
import com.ibm.cics.sm.comm.IContext
import com.ibm.cics.sm.comm.IFilteredContext
import com.ibm.cics.sm.comm.IPrimaryKey
import com.ibm.cics.sm.comm.IResourceErrorCode
import com.ibm.cics.sm.comm.ISystemManagerResponseValues
import com.ibm.cics.sm.comm.ISystemManagerReasonValues
import com.ibm.cics.sm.comm.IScopedContext
import com.ibm.cics.sm.comm.InstallException
import com.ibm.cics.sm.comm.SystemManagerActions
import com.ibm.cics.sm.comm.SystemManagerConnectionException
import com.ibm.cics.ucd.msg.CICSUCDMessage
import com.ibm.cics.ucd.steplabel.CICSUCDStepLabel

import static com.ibm.cics.ucd.StepLogHelper.printStepLog
import static com.ibm.cics.model.AttributeValue.av;

import com.ibm.cics.core.model.AbstractCICSDefinitionType;

import java.text.MessageFormat

class CPSMHelper {

	// <copyright 
	// notice="cics-rm-source-program" 
	// pids="CA0U" 
	// years="2014,2015" 
	// crc="1921302770" > 
	// 	Licensed Materials - Property of IBM 
	// 	"Restricted Materials of IBM" 
	// 	CA0U 
	// 	(C) Copyright IBM Corp. 2014, 2015 
	// 	US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
	// </copyright> 

	//Return a boolean indicating if the given resource type is caseSensitive
	def isNameCaseSensitive(String resourceType){
		def caseSensitiveType = ['BUNDDEF','EJCODEF','DB2EDEF','DB2TDEF','EJDJDEF','DOCDEF','ENQMDEF','JVMSVDEF','PROCDEF','PROFDEF',
			'RQMDEF','TDQDEF','TERMDEF','TRANDEF','URIMPDEF','WEBSVDEF',
			'BUNDLE', 'DB2ENTRY', 'DB2TRN', 'DOCTEMP', 'JVMSERV', 'PROCTYP', 'EXTRATDQ',
			'INDTDQ', 'INTRATDQ', 'REMTDQ', 'TERMNL', 'LOCTRAN', 'URIMAP', 'WEBSERV',
			'EPADAPT','EPADSET','EVNTBIND']
		return caseSensitiveType.contains(resourceType)
	}

	//Used by all perform actions(newcopy, phasein, enable, disable) and install resource description
	def performAction(ICPSM cpsm, ICICSType type, IContext context, ICICSAction cicsAction,String resourceName){
		cpsm.perform(context, type, cicsAction);
		printStepLog(MessageFormat.format(CICSUCDMessage.actionSucceedWithObjectNameAndType,
				cicsAction.getActionName(), type.getResourceTableName(), resourceName))
		return true;
	}

	//For newcopy step with retrytime > 0
	def performActionWithRetry(ICPSM cpsm, ICICSType type, IContext context, ICICSAction cicsAction, String resourceName, int retryTimes, int retryInterval,  List errorList) throws Exception{
		if(retryInterval < 0) retryInterval = 0
		int numberOfAttempts = retryTimes + 1
		while(numberOfAttempts > 0) {
			try {
				if(performAction(cpsm, type, context,cicsAction, resourceName)) {
					return true
				}
			} catch(CICSSystemManagerException e) {
				handleException(e, type.getResourceTableName(), cicsAction, resourceName,errorList)
				numberOfAttempts--
				if(numberOfAttempts > 0) {
					printStepLog(MessageFormat.format(CICSUCDMessage.retrySecondLater, retryInterval))
					Thread.sleep(retryInterval* 1000)
				}
			}
		}
		return false
	}

	//Use by install all CICS and CPSM resource, resource group etc.
	def installAction(ICPSM cpsm, IDefinition cicsDefinition, IScopedContext target, ICICSType type, String resourceName ) throws CICSSystemManagerException{

		cpsm.install(cicsDefinition, target)
		printStepLog(MessageFormat.format(CICSUCDMessage.actionSucceedWithObjectNameAndType,
				'Install', type.getResourceTableName(), resourceName))
		return true
	}

	//For step 'Install Resource form CSD'
	def installCSDResource(ICPSM cpsm,  ICICSType type, IContext context, String resourceName, String csdGroup) throws CICSSystemManagerException{

		boolean success = false
		ICICSDefinition definition = null
		def primaryKey = createPrimaryKeyForCsdResource(context, resourceName, csdGroup)
		definition = (ICICSDefinition) cpsm.get(type, primaryKey)
		success = installAction(cpsm, definition, context, type, resourceName)
		return success
	}

	//For step  'Install Resource form Data Repository'
	def installDREPResource(ICPSM cpsm,  ICICSType type, IScopedContext context, String resourceName, long version) throws CICSSystemManagerException{

		boolean success = false
		ICICSDefinition definition = null

		//For CPSM DREP install, the context for getting definition and target scope are not the same.
		//Getting CICS definition will only require a context with 'context' and scope null.
		//New a context from given FilteredContext
		IContext contextForDrepResource = new Context(context.getContext())
		def primaryKey = createPrimaryKeyForDrepResource(contextForDrepResource, resourceName, version)
		definition = (ICICSDefinition) cpsm.get(type, primaryKey)
		success = installAction(cpsm, definition ,new ScopedContext(context), type, resourceName)
		return success
	}

	//For step 'Install group or list from CSD'
	def installCSDGroupOrList(ICPSM cpsm,  ICICSType type, IScopedContext context, String groupOrListName) throws CICSSystemManagerException{

		boolean success = false
		IDefinition definition = null
		def primaryKey = createPrimaryKeyForCsdGroupAndList(context, groupOrListName)
		definition = (IDefinition) cpsm.get(type, primaryKey)
		success = installAction(cpsm, definition ,new ScopedContext(context), type, groupOrListName)
		return success
	}

	//For step 'Install resource group from CPSM DREP'
	def installDREPResourceGroup(ICPSM cpsm,  ICICSType type, IScopedContext context, String resourceGroupName) throws CICSSystemManagerException{

		boolean success = false
		IDefinition definition = null
		//For CPSM DREP install, the context for getting definition and target scope are not the same.
		//Getting CICS definition will only require a context with 'context' and scope null.
		//New a context from given FilteredContext
		IContext contextForDrepResource = new Context(context.getContext())
		def primaryKey = createPrimaryKeyForCPSMResource(type, contextForDrepResource, resourceGroupName)
		definition = (IDefinition) cpsm.get(type, primaryKey)
		success = installAction(cpsm, definition ,new ScopedContext(context), type, resourceGroupName)
		return success
	}

	//For step 'Install resource description from CPSM DREP'
	def installResDescription(ICPSM cpsm, ICICSType type, IContext context, ICICSAction cicsAction, String resourceDescName) throws CICSSystemManagerException{
		boolean success = false
		IContext contextForDrepResource = new Context(context.getContext())
		def primaryKey = createPrimaryKeyForCPSMResource(type, contextForDrepResource, resourceDescName)
		success = performAction(cpsm, type, primaryKey, cicsAction, resourceDescName)
		return success
	}

	//For step 'Discard resources'
	def discard(ICPSM cpsm, ICICSType type, IContext context, String resourceName) {
		boolean success = false
		IPrimaryKey primaryKey = createPrimaryKeyForResource(context, type, resourceName)
		success = discardResource(cpsm, type, primaryKey, resourceName)
		return success;
	}

	//Used by discard function
	def discardResource(ICPSM cpsm, ICICSType type, IPrimaryKey primaryKey, String resourceName) throws CICSSystemManagerException{
		cpsm.discard(type, primaryKey)
		printStepLog(MessageFormat.format(CICSUCDMessage.actionSucceedWithObjectNameAndType,
				SystemManagerActions.Discard.actionName, type.resourceTableName, resourceName))
		return true
	}

	//PrimaryKey created with resource definition version, for DREP resource use
	def createPrimaryKeyForDrepResource(IContext context, String name, Long version){
		def key = new CICSObjectPrimaryKey(
				context,
				av(AbstractCICSDefinitionType.NAME, name),
				av(AbstractCICSDefinitionType.VERSION, version),
				av(AbstractCICSDefinitionType.CSDGROUP, ""))
		return key
	}

	//PrimaryKey created with CSD group, for CSD resource use
	def createPrimaryKeyForCsdResource(IContext context, String name, String csdGroup){
		def key = new CICSObjectPrimaryKey(context,
				av(AbstractCICSDefinitionType.NAME, name),
				av(AbstractCICSDefinitionType.VERSION, 0L),
				av(AbstractCICSDefinitionType.CSDGROUP, csdGroup))
		return key
	}

	//PrimaryKey created with CSD group/List, for group/list install
	def createPrimaryKeyForCsdGroupAndList(IContext context, String name){
		def key = new CICSObjectPrimaryKey(context,
				av(AbstractCICSDefinitionType.NAME, name))
		return key
	}

	//PrimaryKey created for CPSM resource type ( resource group , resource description)
	def createPrimaryKeyForCPSMResource(ICICSType type, IContext context, String name){
		def key = new CPSMDefinitionPrimaryKey(type, context, name)
		return key
	}

	//PrimaryKey created with resource name
	def createPrimaryKeyForResource(IContext context, ICICSType type, String name){
		String nameAttribute = type.getNameAttribute()
		ICICSAttribute<?> attribute = type.findAttributeByCicsName(nameAttribute)
		def key = new CICSObjectPrimaryKey(context, av(attribute, name))
		return key
	}

	//Method to handle all Exception
	def boolean handleException(CICSSystemManagerException e, String resourceType, ICICSAction action, String resourceName, List errorlist) throws Exception{
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("action", action.actionName);
		map.put("resourceName",resourceName);
		map.put("resourceType",resourceType);
		map.put("responseName","");
		map.put("reasonName","");
		map.put("attribute","");
		map.put("errorCode","");
		map.put("resp1Name","");
		map.put("resp2","");
		
		if(e.getMessage() == '0') {
			//CICS Explorer returns an Exception with the text '0' to indicate zero records were returned
			printStepLog(MessageFormat.format(CICSUCDMessage.actionFailWithObjectNameAndType, action.actionName, resourceType, resourceName))
			printStepLog(MessageFormat.format(CICSUCDMessage.canNotFindResourceDefinition, resourceType, resourceName))
		} else if (e.getCause() instanceof SystemManagerConnectionException) {
			SystemManagerConnectionException ex = (SystemManagerConnectionException) e.getCause();
			int responseCode = ex.getResponseCode();
			IResourceErrorCode errorCode = ex.getFeedbackErrorCode();
			int resp1 = ex.getResp1();
			String function = ex.getFunction();

			if (resp1 != 0){
				//Get the message when INSTALL action itself get failed, format the message. This fail should not stop STEP from executing
				//CNX0591E EXEC CICS command command RESP resp RESP2 resp2.
				printStepLog(MessageFormat.format(CICSUCDMessage.actionFailWithObjectNameAndType, action.actionName, resourceType, resourceName))
				printStepLog(MessageFormat.format(CICSUCDMessage.commandFailDetailMsg, ex.getFunction(), ex.getResp1Name(), ex.getResp2()))
				map.put("resp1Name", ex.getResp1Name())
				map.put("resp2", ex.getResp2().toString())
				errorlist.add(map.sort());

			} else if (errorCode != null && errorCode.getErrorCode() != 0) {
				//CNX0592E Attribute Name (attribute_name) Error (error)
				printStepLog(MessageFormat.format(CICSUCDMessage.actionFailWithObjectNameAndType, action.actionName, resourceType, resourceName))
				printStepLog(MessageFormat.format(CICSUCDMessage.cpsmCommandFailMsg, ex.getAttribute(), errorCode.getLabel()))
				map.put("attribute", ex.getAttribute())
				map.put("errorCode", errorCode.getLabel())
				errorlist.add(map.sort());

			} else if (ex.getResponse() != ISystemManagerResponseValues.OK) {
				//CNX0593E RESPONSE (response) REASON (reason)
				if (ex.getResponse() == ISystemManagerResponseValues.INVALIDPARM && ex.getReason() == ISystemManagerReasonValues.SCOPE) {
					//'INVALIDPARM, SCOPE' reported in exception when the context and scope are not correct
					printStepLog(MessageFormat.format(CICSUCDMessage.checkContextAndScope,
							CICSUCDStepLabel.CICS_SCOPE, CICSUCDStepLabel.CICSPLEX, CICSUCDStepLabel.CICS_SCOPE))
					//Throw the exception as we need to stop STEP from execution in this situation
					throw ex
				}
				printStepLog(MessageFormat.format(CICSUCDMessage.actionFailWithObjectNameAndType, action.actionName, resourceType, resourceName))
				if(ex.getReason() == 0) {
					printStepLog(MessageFormat.format(CICSUCDMessage.actionFailMsgNoReason, ex.getResponseName()))
					map.put("responseName", ex.getResponseName())
					errorlist.add(map.sort());
				} else {
					printStepLog(MessageFormat.format(CICSUCDMessage.actionFailMsg, ex.getResponseName(), ex.getReasonName()))
					map.put("responseName", ex.getResponseName())
					map.put("reasonName", ex.getReasonName())
					map.sort();
					errorlist.add(map.sort());
				}
			} else {
				//Other unexpected CICSSystemManagerException caught
				printStepLog(MessageFormat.format(CICSUCDMessage.unexpectedException))
				throw ex
			}
		} else {
			//Other unexpected non-CICSSystemManagerException caught
			printStepLog(MessageFormat.format(CICSUCDMessage.unexpectedException))
			throw e
		}
	}
}
