/*-
 * ============LICENSE_START=======================================================
 * archetype-closed-loop-demo-rules
 * ================================================================================
 * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
 * ================================================================================
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============LICENSE_END=========================================================
 */

package org.onap.policy.controlloop;

import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.HashMap;
import java.util.UUID;

import org.onap.policy.controlloop.VirtualControlLoopEvent;
import org.onap.policy.controlloop.ControlLoopEventStatus;
import org.onap.policy.controlloop.VirtualControlLoopNotification;
import org.onap.policy.controlloop.ControlLoopNotificationType;
import org.onap.policy.controlloop.ControlLoopOperation;
import org.onap.policy.controlloop.ControlLoopOperationWrapper;
import org.onap.policy.template.demo.ControlLoopException;

import org.onap.policy.aai.AAINQF199.AAINQF199CloudRegion;
import org.onap.policy.aai.AAINQF199.AAINQF199ExtraProperties;
import org.onap.policy.aai.AAINQF199.AAINQF199ExtraProperty;
import org.onap.policy.aai.AAINQF199.AAINQF199GenericVNF;
import org.onap.policy.aai.AAINQF199.AAINQF199InstanceFilters;
import org.onap.policy.aai.AAINQF199.AAINQF199InventoryResponseItem;
import org.onap.policy.aai.AAINQF199.AAINQF199InventoryResponseItems;
import org.onap.policy.aai.AAINQF199.AAINQF199Manager;
import org.onap.policy.aai.AAINQF199.AAINQF199NamedQuery;
import org.onap.policy.aai.AAINQF199.AAINQF199QueryParameters;
import org.onap.policy.aai.AAINQF199.AAINQF199Request;
import org.onap.policy.aai.AAINQF199.AAINQF199RequestWrapper;
import org.onap.policy.aai.AAINQF199.AAINQF199Response;
import org.onap.policy.aai.AAINQF199.AAINQF199ResponseWrapper;
import org.onap.policy.aai.AAINQF199.AAINQF199ServiceInstance;
import org.onap.policy.aai.AAINQF199.AAINQF199Tenant;
import org.onap.policy.aai.AAINQF199.AAINQF199VfModule;
import org.onap.policy.aai.AAINQF199.AAINQF199VServer;
import org.onap.policy.aai.util.Serialization;

import org.onap.policy.appc.CommonHeader;
import org.onap.policy.appc.Request;
import org.onap.policy.appc.Response;
import org.onap.policy.appc.ResponseCode;
import org.onap.policy.appc.ResponseStatus;
import org.onap.policy.appc.ResponseValue;

import org.onap.policy.template.demo.EventManager;
import org.onap.policy.vnf.trafficgenerator.PGRequest;
import org.onap.policy.vnf.trafficgenerator.PGStream;
import org.onap.policy.vnf.trafficgenerator.PGStreams;

import org.onap.policy.mso.MSOManager;
import org.onap.policy.mso.MSORequest;
import org.onap.policy.mso.MSORequestStatus;
import org.onap.policy.mso.MSORequestDetails;
import org.onap.policy.mso.MSOModelInfo;
import org.onap.policy.mso.MSOCloudConfiguration;
import org.onap.policy.mso.MSORequestInfo;
import org.onap.policy.mso.MSORequestParameters;
import org.onap.policy.mso.MSORelatedInstanceListElement;
import org.onap.policy.mso.MSORelatedInstance;
import org.onap.policy.mso.MSOResponse;

import org.onap.policy.drools.system.PolicyEngine;

//
// These parameters are required to build the runtime policy
//
declare Params
    closedLoopControlName : String
    actor : String
    aaiURL : String
    aaiUsername : String
    aaiPassword : String
    msoURL : String
    msoUsername : String
    msoPassword : String
    aaiNamedQueryUUID : String
    aaiPatternMatch : int 
    notificationTopic : String
    appcTopic : String
end

/*
*
* Called once and only once to insert the parameters into working memory for this Closed Loop policy.
* (Comment SETUP rule out for the first ONAP opensource release since policy BRMS_GW already puts a Params fact in there)
*
*
*rule "${policyName}.SETUP"
*	when
*	then
*		System.out.println("rule SETUP is triggered.");
*		Params params = new Params();
*		params.setClosedLoopControlName("${closedLoopControlName}");
*		params.setActor("${actor}");
*		params.setAaiURL("${aaiURL}");
*		params.setAaiUsername("${aaiUsername}");
*		params.setAaiPassword("${aaiPassword}");
*		params.setMsoURL("${msoURL}");
*		params.setMsoUsername("${msoUsername}");
*		params.setMsoPassword("${msoPassword}");
*		params.setAaiNamedQueryUUID("${aaiNamedQueryUUID}");
*		params.setAaiPatternMatch(${aaiPatternMatch});
*		params.setNotificationTopic("${notificationTopic}");
*		params.setAppcTopic("${appcTopic}");
*		//
*		// This stays in memory as long as the rule is alive and running
*		//
*		insert(params);
*end
*/
/*
*
* This rule responds to DCAE Events
*
*/
rule "${policyName}.EVENT"
	when
        $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
        $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
        not ( EventManager( closedLoopControlName == $event.closedLoopControlName ))
	then
		System.out.println("rule EVENT is triggered.");
		try {
			// 
			// Check the requestID in the event to make sure it is not null before we create the EventManager. 
			// The EventManager will do extra syntax checking as well check if the closed loop is disabled/
			//
			if ($event.requestID == null) {
				VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
				notification.notification = ControlLoopNotificationType.REJECTED;
				notification.from = "policy";
				notification.message = "Missing requestID from DCAE event";
				notification.policyName = drools.getRule().getName();
				notification.policyScope = "${policyScope}";
				notification.policyVersion = "${policyVersion}";
				//
				// Let interested parties know
				//
				try {
					System.out.println(Serialization.gsonPretty.toJson(notification));
					PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
				} catch (Exception e) {
					e.printStackTrace();
					System.out.println("Can't deliver notification: " + notification);
				}
				//
				// Retract it from memory
				//
				retract($event);
				System.out.println("Event with requestID=null has been retracted.");
			} else {
				//
				// Create an EventManager
				//
				EventManager manager = new EventManager($params.getClosedLoopControlName(), $event.requestID, $event.target);
				//
				// Determine if EventManager can actively process the event (i.e. syntax)
				//
				VirtualControlLoopNotification notification = manager.activate($event);
				notification.from = "policy"; 
				notification.policyName = drools.getRule().getName();
				notification.policyScope = "${policyScope}";
				notification.policyVersion = "${policyVersion}";
				//
				// Are we actively pursuing this event?
				//
				if (notification.notification == ControlLoopNotificationType.ACTIVE) {
					//
					// Insert Event Manager into memory, this will now kick off processing.
					//
					insert(manager);
					//
					// Let interested parties know
					//
					try {
						System.out.println(Serialization.gsonPretty.toJson(notification));
						PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
					} catch (Exception e) {
						e.printStackTrace();
						System.out.println("Can't deliver notification: " + notification);
					}		
				} else {
					//
					// Let interested parties know
					//
					try {
						System.out.println(Serialization.gsonPretty.toJson(notification));
						PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
					} catch (Exception e) {
						e.printStackTrace();
						System.out.println("Can't deliver notification: " + notification);
					}
					//
					// Retract it from memory
					//
					retract($event);
				}
				//
				// Now that the manager is inserted into Drools working memory, we'll wait for
				// another rule to fire in order to continue processing. This way we can also
				// then screen for additional ONSET and ABATED events for this same RequestIDs 
				// and for different RequestIDs but with the same closedLoopControlName and target.
				//
			}
		//
		} catch (Exception e) {
			e.printStackTrace();
			VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
			notification.notification = ControlLoopNotificationType.REJECTED;
			notification.message = "Exception occurred " + e.getMessage();
			notification.policyName = drools.getRule().getName();
			notification.policyScope = "${policyScope}";
			notification.policyVersion = "${policyVersion}";
			//
			//
			//
			try {
				System.out.println(Serialization.gsonPretty.toJson(notification));
				PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
			} catch (Exception e1) {
				System.out.println("Can't deliver notification: " + notification);
				e1.printStackTrace();
			}
			//
			// Retract the event
			//
			retract($event);
		}
end

/*
*
* This rule happens when we got a valid ONSET, closed loop is enabled and an Event Manager
* is created. We can start the operations for this closed loop.
*
*/
rule "${policyName}.EVENT.MANAGER"
	when
		$params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
		$event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
		$manager : EventManager( closedLoopControlName == $event.closedLoopControlName, controlLoopResult == null)
	then
		System.out.println("rule EVENT.MANAGER is triggered.");
		//
		// Check which event this is.
		//
		EventManager.NEW_EVENT_STATUS eventStatus = $manager.onNewEvent($event);
		//
		// We only want the initial ONSET event in memory,
		// all the other events need to be retracted to support
		// cleanup and avoid the other rules being fired for this event.
		//
		if (eventStatus != EventManager.NEW_EVENT_STATUS.FIRST_ONSET) {
			System.out.println("Retracting "+eventStatus+" Event.");
			retract($event);
			return;
		}
		//
		// Now the event in memory is first onset event
		//
		try {
			//
			// Pull the known AAI field from the Event
			//
			// generic-vnf is needed for vFirewall case
			// vserver-name is needed for vLoadBalancer case
			//
			String genericVNF = $event.AAI.get("generic-vnf.vnf-id");
			String vserver = $event.AAI.get("vserver.vserver-name");
			//
			// Check if we are implementing a simple pattern match.
			//
			if ($params.getAaiPatternMatch() == 1) {
				//
				// Yes
				//
				//Basic naming characteristics:
				//VF Name (9 char)+VM name (13 char total)+VFC (19 char total)
				//Example: 
				//VF Name (9 characters):    cscf0001v
				//VM Name(13 characters): cscf0001vm001
				//VFC name(19 characters): cscf0001vm001cfg001
				//
				// zdfw1fwl01fwl02 or zdfw1fwl01fwl01  
				// replaced with
				// zdfw1fwl01pgn02 or zdfw1fwl01pgn01
				//
				int index = genericVNF.lastIndexOf("fwl");
				if (index == -1) {
					System.err.println("The generic-vnf.vnf-id from DCAE Event is not valid.");
				} else {
					genericVNF = genericVNF.substring(0, index) + "pgn" + genericVNF.substring(index+"fwl".length());
				}
				//
				// Construct an APPC request
				//
				ControlLoopOperation operation = new ControlLoopOperation();
				operation.actor = $params.getActor();
				operation.operation = "ModifyConfig";
				operation.target = $event.target;
				//
				// Create operationWrapper
				//
				ControlLoopOperationWrapper operationWrapper = new ControlLoopOperationWrapper($event.requestID, operation);
				//
				// insert operationWrapper into memory
				//
				insert(operationWrapper);
				//
				Request request = new Request();
				request.CommonHeader = new CommonHeader();
				request.CommonHeader.RequestID = $event.requestID;
				request.Action = operation.operation;
				request.Payload = new HashMap<String, Object>();
				//
				// Fill in the payload
				//
				request.Payload.put("generic-vnf.vnf-id", genericVNF);
				//
				PGRequest pgRequest = new PGRequest();
				pgRequest.pgStreams = new PGStreams();
				
				PGStream pgStream;
				for(int i = 0; i < 5; i++){
					pgStream = new PGStream();
					pgStream.streamId = "fw_udp"+(i+1);
					pgStream.isEnabled = "true";
					pgRequest.pgStreams.pgStream.add(pgStream);
				}
				request.Payload.put("pg-streams", pgRequest.pgStreams);
				
				if (request != null) {
					//
					// Insert request into memory
					//
					insert(request);
					//
					// Tell interested parties we are performing this Operation
					//
					VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
					notification.notification = ControlLoopNotificationType.OPERATION;
					// message and history ??
					notification.from = "policy";
					notification.policyName = drools.getRule().getName();
					notification.policyScope = "${policyScope}";
					notification.policyVersion = "${policyVersion}";
					try {
						System.out.println(Serialization.gsonPretty.toJson(notification));
						PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
					} catch (Exception e) {
						System.out.println("Can't deliver notification: " + notification);
						e.printStackTrace();
					}
					//
					// Now send the operation request
					//
					if (request instanceof Request) {
						try {
							System.out.println("APPC request sent:");
							System.out.println(Serialization.gsonPretty.toJson(request));
							PolicyEngine.manager.deliver($params.getAppcTopic(), request);
						} catch (Exception e) {
							e.printStackTrace();
							System.out.println("Can't deliver request: " + request);
						}
					}
				} else {
					//
					// what happens if it is null
					//
				}
				//
			} else {
				//
				// create AAI named-query request with UUID started with "F199"
				//
				AAINQF199Request aainqf199request = new AAINQF199Request();
				AAINQF199QueryParameters aainqf199queryparam = new AAINQF199QueryParameters();
				AAINQF199NamedQuery aainqf199namedquery = new AAINQF199NamedQuery();
				AAINQF199InstanceFilters aainqf199instancefilter = new AAINQF199InstanceFilters();
				//
				// queryParameters
				//
				aainqf199namedquery.namedQueryUUID = UUID.fromString($params.getAaiNamedQueryUUID()); 
				aainqf199queryparam.namedQuery = aainqf199namedquery;
				aainqf199request.queryParameters = aainqf199queryparam;
				//
				// instanceFilters
				//
				Map aainqf199instancefiltermap = new HashMap();
				Map aainqf199instancefiltermapitem = new HashMap();
				aainqf199instancefiltermapitem.put("vserver-name", vserver); 
				aainqf199instancefiltermap.put("vserver", aainqf199instancefiltermapitem);
				aainqf199instancefilter.instanceFilter.add(aainqf199instancefiltermap);
				aainqf199request.instanceFilters = aainqf199instancefilter;
				//
				// print aainqf199request for debug
				//
				System.out.println("AAI Request sent:");
				System.out.println(Serialization.gsonPretty.toJson(aainqf199request));
				//
				// Create AAINQF199RequestWrapper
				//
				AAINQF199RequestWrapper aainqf199RequestWrapper = new AAINQF199RequestWrapper($event.requestID, aainqf199request);
				//
				// insert aainqf199request into memory
				//
				insert(aainqf199RequestWrapper);
			}
			//
		} catch (Exception e) {
		 e.printStackTrace();
		}
end

/*
*
* This rule happens when we got a valid ONSET, closed loop is enabled, an Event Manager
* is created, AAI Manager and AAI Request are ready in memory. We can start sending query to AAI and then wait for response.
*
*/
rule "${policyName}.EVENT.MANAGER.AAINQF199REQUEST"
	when
		$params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
		$event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
		$manager : EventManager( closedLoopControlName == $event.closedLoopControlName )
		$aainqf199RequestWrapper : AAINQF199RequestWrapper(requestID == $event.requestID)
	then
		System.out.println("rule EVENT.MANAGER.AAINQF199REQUEST is triggered.");
		//
		// send the request
		//
		AAINQF199Response aainqf199response = AAINQF199Manager.postQuery($params.getAaiURL(), $params.getAaiUsername(), $params.getAaiPassword(),
													   $aainqf199RequestWrapper.aainqf199request, $event.requestID);
		//
		// Check AAI response
		//
		if (aainqf199response == null) {
			System.err.println("Failed to get AAI response");
			//
			// Fail and retract everything
			//
			retract($event);
			retract($manager);
			retract($aainqf199RequestWrapper);
		} else {
			//
			// Create AAINQF199ResponseWrapper
			//
			AAINQF199ResponseWrapper aainqf199ResponseWrapper = new AAINQF199ResponseWrapper($event.requestID, aainqf199response);
			//
			// insert aainqf199ResponseWrapper to memeory
			//
			insert(aainqf199ResponseWrapper);
		}
end

/*
*
* This rule happens when we got a valid AAI response. We can start sending request to APPC or MSO now.
*
*/
rule "${policyName}.EVENT.MANAGER.AAINQF199RESPONSE"
	when 
		$params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
		$event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
		$manager : EventManager( closedLoopControlName == $event.closedLoopControlName )
		$aainqf199RequestWrapper : AAINQF199RequestWrapper(requestID == $event.requestID)
		$aainqf199ResponseWrapper : AAINQF199ResponseWrapper(requestID == $event.requestID)
	then
		System.out.println("rule EVENT.MANAGER.AAINQF199RESPONSE is triggered.");
		//
		// Extract related fields out of AAINQF199RESPONSE
		//
		String vnfItemVnfId, vnfItemVnfType, vnfItemPersonaModelId, vnfItemPersonaModelVersion, vnfItemModelName, 
		       vnfItemModelVersion, vnfItemModelNameVersionId, serviceItemServiceInstanceId, serviceItemPersonaModelId,
		       serviceItemModelName, serviceItemModelType, serviceItemModelVersion, serviceItemModelNameVersionId,
		       vfModuleItemVfModuleName, vfModuleItemPersonaModelId, vfModuleItemPersonaModelVersion, vfModuleItemModelName, 
		       vfModuleItemModelNameVersionId, tenantItemTenantId, cloudRegionItemCloudRegionId;
		try {
			//
			// vnfItem
			//
			vnfItemVnfId = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).genericVNF.vnfID;
			vnfItemVnfType = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).genericVNF.vnfType;
			vnfItemVnfType = vnfItemVnfType.substring(vnfItemVnfType.lastIndexOf("/")+1);
			vnfItemPersonaModelId = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).genericVNF.personaModelId;
			vnfItemPersonaModelVersion = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).genericVNF.personaModelVersion;
			vnfItemModelName = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).extraProperties.extraProperty.get(0).propertyValue;
			vnfItemModelVersion = 		$aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).extraProperties.extraProperty.get(2).propertyValue;
			vnfItemModelNameVersionId = 	$aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).extraProperties.extraProperty.get(4).propertyValue;
			//
			// serviceItem
			//
			serviceItemServiceInstanceId = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).serviceInstance.serviceInstanceID;
			serviceItemPersonaModelId = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).serviceInstance.personaModelId;
			serviceItemModelName = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).extraProperties.extraProperty.get(0).propertyValue;
			serviceItemModelType = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).extraProperties.extraProperty.get(1).propertyValue;
			serviceItemModelVersion = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).serviceInstance.personaModelVersion;
			serviceItemModelNameVersionId = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).extraProperties.extraProperty.get(4).propertyValue;
			//
			// Find the index for base vf module and non-base vf module
			//
			int baseIndex = -1;
			int nonBaseIndex = -1;
			List<AAINQF199InventoryResponseItem> inventoryItems = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems;
			for (AAINQF199InventoryResponseItem m : inventoryItems) {
				if (m.vfModule != null && m.vfModule.isBaseVfModule == true) {
					baseIndex = inventoryItems.indexOf(m);
				} else if (m.vfModule != null && m.vfModule.isBaseVfModule == false && m.vfModule.orchestrationStatus == null) {
					nonBaseIndex = inventoryItems.indexOf(m);
				}
				//
				if (baseIndex != -1 && nonBaseIndex != -1) {
					break;
				}
			}
			//
			// Report the error if either base vf module or non-base vf module is not found
			//
			if (baseIndex == -1 || nonBaseIndex == -1) {
				System.err.println("Either base or non-base vf module is not found from AAI response.");
				retract($aainqf199RequestWrapper);
				retract($aainqf199ResponseWrapper);
				retract($manager);
				retract($event);
				return;
			}
			//
			// This comes from the base module
			//
			vfModuleItemVfModuleName = 			$aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(baseIndex).vfModule.vfModuleName;
			vfModuleItemVfModuleName = vfModuleItemVfModuleName.replace("Vfmodule", "vDNS");
			//
			// vfModuleItem - NOT the base module
			//
			vfModuleItemPersonaModelId = 		$aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(nonBaseIndex).vfModule.personaModelId;
			vfModuleItemPersonaModelVersion = 	$aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(nonBaseIndex).vfModule.personaModelVersion;
			vfModuleItemModelName = 			$aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(nonBaseIndex).extraProperties.extraProperty.get(0).propertyValue;
			vfModuleItemModelNameVersionId = 	$aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(0).items.inventoryResponseItems.get(nonBaseIndex).extraProperties.extraProperty.get(4).propertyValue;
			//
			// tenantItem
			//
			tenantItemTenantId = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(1).tenant.tenantId;
			//
			// cloudRegionItem
			//
			cloudRegionItemCloudRegionId = $aainqf199ResponseWrapper.aainqf199response.inventoryResponseItems.get(0).items.inventoryResponseItems.get(1).items.inventoryResponseItems.get(0).cloudRegion.cloudRegionId;
			//
		} catch (Exception e) {
			e.printStackTrace();
			VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
			notification.notification = ControlLoopNotificationType.REJECTED;
			notification.message = "Exception occurred " + e.getMessage();
			notification.policyName = drools.getRule().getName();
			notification.policyScope = "${policyScope}";
			notification.policyVersion = "${policyVersion}";
			//
			try {
				System.out.println(Serialization.gsonPretty.toJson(notification));
				PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
			} catch (Exception e1) {
				System.out.println("Can't deliver notification: " + notification);
				e1.printStackTrace();
			}
			//
			notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
			notification.message = "Invalid named-query response from AAI";
            //
            try {
            	System.out.println(Serialization.gsonPretty.toJson(notification));
                PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
            } catch (Exception e1) {
                System.out.println("Can't deliver notification: " + notification);
                e1.printStackTrace();
            }
			//
			// Retract everything
			//
			retract($aainqf199RequestWrapper);
			retract($aainqf199ResponseWrapper);
			retract($manager);
			retract($event);
			return;
		}	
		//
		// Extracted fields should not be null
		//
		if ((vnfItemVnfId == null) || (vnfItemVnfType == null) ||
		    (vnfItemPersonaModelId == null) || (vnfItemModelName == null) ||
		    (vnfItemModelVersion == null) || (vnfItemModelNameVersionId == null) ||
		    (serviceItemServiceInstanceId == null) || (serviceItemModelName == null) ||
		    (serviceItemModelType == null) || (serviceItemModelVersion == null) ||
		    (serviceItemModelNameVersionId == null) || (vfModuleItemVfModuleName == null) ||
		    (vfModuleItemPersonaModelId == null) || (vfModuleItemPersonaModelVersion == null) ||
		    (vfModuleItemModelName == null) || (vfModuleItemModelNameVersionId == null) ||
		    (tenantItemTenantId == null) || (cloudRegionItemCloudRegionId == null)) {
			//
			System.err.println("some fields are missing from AAI response.");
			//
			// Fail and retract everything
			//
			retract($aainqf199RequestWrapper);
			retract($aainqf199ResponseWrapper);
			retract($manager);
			retract($event);
			return;
		}
		//
		// We don't need them any more
		//
		retract($aainqf199ResponseWrapper);
		retract($aainqf199RequestWrapper);  
		//
		// check the actor of this closed loop
		//
		switch ($params.getActor()) {
			case "APPC":
			{
				//
				// Construct an APPC request
				//
				ControlLoopOperation operation = new ControlLoopOperation();
				operation.actor = $params.getActor();
				operation.operation = "ModifyConfig";
				operation.target = $event.target;
				//
				// Create operationWrapper
				//
				ControlLoopOperationWrapper operationWrapper = new ControlLoopOperationWrapper($event.requestID, operation);
				//
				// insert operationWrapper into memory
				//
				insert(operationWrapper);
				//
				Request request = new Request();
				request.CommonHeader = new CommonHeader();
				request.CommonHeader.RequestID = $event.requestID;
				request.Action = operation.operation;
				request.Payload = new HashMap<String, Object>();
				//
				// Fill in the payload
				// Hardcode genericVNF for now since AAI has not been ready for vFirewall demo case
				//
				String genericVNF = "zdfw1fwl01pgn02";
				request.Payload.put("generic-vnf.vnf-id", genericVNF);
				//
				PGRequest pgRequest = new PGRequest();
				pgRequest.pgStreams = new PGStreams();
				
				PGStream pgStream;
				for(int i = 0; i < 5; i++){
					pgStream = new PGStream();
					pgStream.streamId = "fw_udp"+(i+1);
					pgStream.isEnabled = "true";
					pgRequest.pgStreams.pgStream.add(pgStream);
				}
				request.Payload.put("pg-streams", pgRequest.pgStreams);
				
				if (request != null) {
					//
					// Insert request into memory
					//
					insert(request);
					//
					// Tell interested parties we are performing this Operation
					//
					VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
					notification.notification = ControlLoopNotificationType.OPERATION;
					// message and history ??
					notification.from = "policy";
					notification.policyName = drools.getRule().getName();
					notification.policyScope = "${policyScope}";
					notification.policyVersion = "${policyVersion}";
					try {
						System.out.println(Serialization.gsonPretty.toJson(notification));
						PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
					} catch (Exception e) {
						System.out.println("Can't deliver notification: " + notification);
						e.printStackTrace();
					}
					//
					// Now send the operation request
					//
					if (request instanceof Request) {
						try {
							System.out.println("APPC request sent:");
							System.out.println(Serialization.gsonPretty.toJson(request));
							PolicyEngine.manager.deliver($params.getAppcTopic(), request);
						} catch (Exception e) {
							e.printStackTrace();
							System.out.println("Can't deliver request: " + request);
						}
					}
				} else {
					//
					// what happens if it is null
					//
				}
			}
			break;
			case "MSO":
			{
				//
				// Construct an operation
				//
				ControlLoopOperation operation = new ControlLoopOperation();
				operation.actor = $params.getActor();
				operation.operation = "createModuleInstance";
				operation.target = $event.target;
				//
				// Create operationWrapper
				//
				ControlLoopOperationWrapper operationWrapper = new ControlLoopOperationWrapper($event.requestID, operation);
				//
				// Construct an MSO request
				//
				MSORequest request = new MSORequest();
				request.requestDetails = new MSORequestDetails();
				request.requestDetails.modelInfo = new MSOModelInfo();
				request.requestDetails.cloudConfiguration = new MSOCloudConfiguration();
				request.requestDetails.requestInfo = new MSORequestInfo();
				request.requestDetails.requestParameters = new MSORequestParameters();
				request.requestDetails.requestParameters.userParams = null;
				//
				// cloudConfiguration
				//
				request.requestDetails.cloudConfiguration.lcpCloudRegionId = cloudRegionItemCloudRegionId;
				request.requestDetails.cloudConfiguration.tenantId = tenantItemTenantId;
				//
				// modelInfo
				//
				request.requestDetails.modelInfo.modelType = "vfModule";
				request.requestDetails.modelInfo.modelInvariantId = vfModuleItemPersonaModelId;
				request.requestDetails.modelInfo.modelNameVersionId = vfModuleItemModelNameVersionId;
				request.requestDetails.modelInfo.modelName = vfModuleItemModelName;
				request.requestDetails.modelInfo.modelVersion = vfModuleItemPersonaModelVersion;
				//
				// requestInfo
				//
				request.requestDetails.requestInfo.instanceName = vfModuleItemVfModuleName;
				request.requestDetails.requestInfo.source = "POLICY";
				request.requestDetails.requestInfo.suppressRollback = false;
				//
				// relatedInstanceList
				//
				MSORelatedInstanceListElement relatedInstanceListElement1 = new MSORelatedInstanceListElement();
				MSORelatedInstanceListElement relatedInstanceListElement2 = new MSORelatedInstanceListElement();
				relatedInstanceListElement1.relatedInstance = new MSORelatedInstance();
				relatedInstanceListElement2.relatedInstance = new MSORelatedInstance();
				//
				relatedInstanceListElement1.relatedInstance.instanceId = serviceItemServiceInstanceId;
				relatedInstanceListElement1.relatedInstance.modelInfo = new MSOModelInfo();
				relatedInstanceListElement1.relatedInstance.modelInfo.modelType = "service";
				relatedInstanceListElement1.relatedInstance.modelInfo.modelInvariantId = serviceItemPersonaModelId;
				relatedInstanceListElement1.relatedInstance.modelInfo.modelNameVersionId = serviceItemModelNameVersionId;
				relatedInstanceListElement1.relatedInstance.modelInfo.modelName = serviceItemModelName;
				relatedInstanceListElement1.relatedInstance.modelInfo.modelVersion = serviceItemModelVersion;
				//
				relatedInstanceListElement2.relatedInstance.instanceId = vnfItemVnfId;
				relatedInstanceListElement2.relatedInstance.modelInfo = new MSOModelInfo();
				relatedInstanceListElement2.relatedInstance.modelInfo.modelType = "vnf";
				relatedInstanceListElement2.relatedInstance.modelInfo.modelInvariantId = vnfItemPersonaModelId;
				relatedInstanceListElement2.relatedInstance.modelInfo.modelNameVersionId = vnfItemModelNameVersionId;
				relatedInstanceListElement2.relatedInstance.modelInfo.modelName = vnfItemModelName;
				relatedInstanceListElement2.relatedInstance.modelInfo.modelVersion = vnfItemModelVersion;
				relatedInstanceListElement2.relatedInstance.modelInfo.modelCustomizationName = vnfItemVnfType;
				//	
				request.requestDetails.relatedInstanceList.add(relatedInstanceListElement1);
				request.requestDetails.relatedInstanceList.add(relatedInstanceListElement2);
				//
				// print MSO request for debug
				//
				System.out.println("MSO request sent:");
				System.out.println(Serialization.gsonPretty.toJson(request));
				//
				//
				//
				if (request != null) {
					//
					// Tell interested parties we are performing this Operation
					//
					VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
					notification.notification = ControlLoopNotificationType.OPERATION;
					notification.from = "policy";
					notification.policyName = drools.getRule().getName();
					notification.policyScope = "${policyScope}";
					notification.policyVersion = "${policyVersion}";
					try {
						System.out.println(Serialization.gsonPretty.toJson(notification));
						PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
					} catch (Exception e) {
						System.out.println("Can't deliver notification: " + notification);
						e.printStackTrace();
					}
					//
					// Concatenate serviceItemServiceInstanceId and vnfItemVnfId to msoURL
					//
					String MSOUrl = $params.getMsoURL() + "/serviceInstances/v2/" + serviceItemServiceInstanceId + "/vnfs/" + vnfItemVnfId + "/vfModules";
					//
					// Call MSO
					//
					MSOResponse response = MSOManager.createModuleInstance(MSOUrl, $params.getMsoURL(), $params.getMsoUsername(), $params.getMsoPassword(), request);
					//
					if (response != null) {
						//
						// Assign requestId
						//
						request.requestId = $event.requestID.toString();						
						response.request.requestId = $event.requestID.toString();
						//
						// Insert facts
						//
						insert(operationWrapper);
						insert(request);
						insert(response);
					} else {
						//
						// MSO request not even accepted
						//
						notification.message = operationWrapper.operation.toMessage();
						operationWrapper.operation.message = operationWrapper.operation.toMessage();
						operationWrapper.operation.outcome = "FAILURE_EXCEPTION";
						$manager.setControlLoopResult("FAILURE_EXCEPTION");
						notification.history.add(operationWrapper.operation);
						notification.notification = ControlLoopNotificationType.OPERATION_FAILURE;
						//
						// Let interested parties know
						//
						try {
							System.out.println(Serialization.gsonPretty.toJson(notification));
							PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
						} catch (Exception e) {
							System.out.println("Can't deliver notification: " + notification);
							e.printStackTrace();
						}
	                    notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
                        try {
                        	System.out.println(Serialization.gsonPretty.toJson(notification));
                        	PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
                        } catch (Exception e) {
                        	System.out.println("Can't deliver notification: " + notification);
                        	e.printStackTrace();
                        }
						//
						// Retract everything
						//
						retract($event);
						retract($manager);
					}
				} else {
					System.err.println("constructed MSO request is invalid.");
				}
			}
			break; 
		} 
end
		
/*
*
* This rule responds to APPC Response Events
*
*/
rule "${policyName}.APPC.RESPONSE"
	when
		$params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
		$event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
		$manager : EventManager( closedLoopControlName == $event.closedLoopControlName )
		$operationWrapper : ControlLoopOperationWrapper( requestID == $event.requestID )
		$request : Request( getCommonHeader().RequestID == $event.requestID )
		$response : Response( getCommonHeader().RequestID == $event.requestID )	
	then
		System.out.println("rule APPC.RESPONSE is triggered.");
		if ($response.Status == null) {
			$operationWrapper.operation.outcome = "FAILURE_EXCEPTION";
			$manager.setControlLoopResult("FAILURE_EXCEPTION");
		} 
		//
		// Get the Response Code
		//
		ResponseCode code = ResponseCode.toResponseCode($response.Status.Code);
		if (code == null) {
			$operationWrapper.operation.outcome = "FAILURE_EXCEPTION";
			$manager.setControlLoopResult("FAILURE_EXCEPTION");
		}
		//
		// Construct notification
		//
		VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
		notification.from = "policy";
		notification.policyName = drools.getRule().getName();
		notification.policyScope = "${policyScope}";
		notification.policyVersion = "${policyVersion}";
		notification.message = $operationWrapper.operation.toMessage();
		$operationWrapper.operation.message = $operationWrapper.operation.toMessage();
		//
		// Ok, let's figure out what APP-C's response is
		//
		switch (code) {
			case ACCEPT:
				$operationWrapper.operation.outcome = "PROCESSING";
				break;
			case ERROR:
			case REJECT:
				$operationWrapper.operation.outcome = "FAILURE_EXCEPTION";
				$manager.setControlLoopResult("FAILURE_EXCEPTION");
				break;
			case SUCCESS:
				$operationWrapper.operation.outcome = "SUCCESS";
				$manager.setControlLoopResult("SUCCESS");
				break;
			case FAILURE:
				$operationWrapper.operation.outcome = "FAILURE";
				$manager.setControlLoopResult("FAILURE");
				break;
		}
		if ($operationWrapper.operation.outcome.equals("SUCCESS")) {
			notification.history.add($operationWrapper.operation);
			notification.notification = ControlLoopNotificationType.OPERATION_SUCCESS;
			//
			// Let interested parties know
			//
			try {
				System.out.println(Serialization.gsonPretty.toJson(notification));
				PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
			} catch (Exception e) {
				System.out.println("Can't deliver notification: " + notification);
				e.printStackTrace();
			}
            notification.notification = ControlLoopNotificationType.FINAL_SUCCESS;
            try {
            	System.out.println(Serialization.gsonPretty.toJson(notification));
            	PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
            } catch (Exception e) {
            	System.out.println("Can't deliver notification: " + notification);
            	e.printStackTrace();
            }

			//
			// We are going to retract these objects from memory
			//
			System.out.println("Retracting everything");
			retract($operationWrapper);
			retract($request);
			retract($response);
			retract($event);
			retract($manager);
		} else if ($operationWrapper.operation.outcome.equals("PROCESSING")) {
			retract($response);
		} else {
			notification.history.add($operationWrapper.operation);
			notification.notification = ControlLoopNotificationType.OPERATION_FAILURE;
			//
			// Let interested parties know
			//
			try {
				System.out.println(Serialization.gsonPretty.toJson(notification));
				PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
			} catch (Exception e) {
				System.out.println("Can't deliver notification: " + notification);
				e.printStackTrace();
			}
            notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
            //
            // Let interested parties know
            //
            try {
            	System.out.println(Serialization.gsonPretty.toJson(notification));
            	PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
            } catch (Exception e) {
            	System.out.println("Can't deliver notification: " + notification);
            	e.printStackTrace();
            }
			//
			// We are going to retract these objects from memory
			//
			System.out.println("Retracting everything");
			retract($operationWrapper);
			retract($request);
			retract($response);
			retract($event);
			retract($manager);
		}
		
end		

/*
*
* This rule is used to clean up APPC response
*
*/		
rule "${policyName}.APPC.RESPONSE.CLEANUP"
	when
		$params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
        $response : Response($id : getCommonHeader().RequestID )
		not ( VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), requestID == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) ) 
	then
		System.out.println("rule APPC.RESPONSE.CLEANUP is triggered.");
		retract($response);
end

/*
*
* This rule responds to MSO Response Events
*
*/
rule "${policyName}.MSO.RESPONSE"
	when
		$params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
		$event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
		$manager : EventManager( closedLoopControlName == $event.closedLoopControlName )
		$operationWrapper : ControlLoopOperationWrapper( requestID == $event.requestID )
		$request : MSORequest( requestId == $event.requestID.toString() )
		$response : MSOResponse( request.requestId == $event.requestID.toString() )	
	then
		System.out.println("rule MSO.RESPONSE is triggered.");
		//
		// Construct notification
		//
		VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
		notification.from = "policy";
		notification.policyName = drools.getRule().getName();
		notification.policyScope = "${policyScope}";
		notification.policyVersion = "${policyVersion}";
		notification.message = $operationWrapper.operation.toMessage();
		$operationWrapper.operation.message = $operationWrapper.operation.toMessage();
		//
		// The operation can either be succeeded or failed
		// 
		if($response.request.requestStatus.requestState.equals("Completed")) {
			$operationWrapper.operation.outcome = "SUCCESS";
			$manager.setControlLoopResult("SUCCESS");
			notification.history.add($operationWrapper.operation);
			notification.notification = ControlLoopNotificationType.OPERATION_SUCCESS;
			//
			// Let interested parties know
			//
			try {
				System.out.println(Serialization.gsonPretty.toJson(notification));
				PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
			} catch (Exception e) {
				System.out.println("Can't deliver notification: " + notification);
				e.printStackTrace();
			}
			notification.notification = ControlLoopNotificationType.FINAL_SUCCESS;
            //
            // Let interested parties know
            //
            try {
            	System.out.println(Serialization.gsonPretty.toJson(notification));
            	PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
            } catch (Exception e) {
            	System.out.println("Can't deliver notification: " + notification);
            	e.printStackTrace();
            }
            //
			// We are going to retract these objects from memory
			//
			System.out.println("Retracting everything");
			retract($operationWrapper);
			retract($request);
			retract($response);
			retract($event);
			retract($manager);
		} else {
			$operationWrapper.operation.outcome = "FAILURE";
			$manager.setControlLoopResult("FAILURE");
			notification.history.add($operationWrapper.operation);
			notification.notification = ControlLoopNotificationType.OPERATION_FAILURE;
			//
			// Let interested parties know
			//
			try {
				System.out.println(Serialization.gsonPretty.toJson(notification));
				PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
			} catch (Exception e) {
				System.out.println("Can't deliver notification: " + notification);
				e.printStackTrace();
			}
			notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
            //
            // Let interested parties know
            //
            try {
            	System.out.println(Serialization.gsonPretty.toJson(notification));
            	PolicyEngine.manager.deliver($params.getNotificationTopic(), notification);
            } catch (Exception e) {
            	System.out.println("Can't deliver notification: " + notification);
            	e.printStackTrace();
            }
            //
			// We are going to retract these objects from memory
			//
			System.out.println("Retracting everything");
			retract($operationWrapper);
			retract($request);
			retract($response);
			retract($event);
			retract($manager);
		}
		
end