/*
*
* Sample program for use with the Product
* Licensed Materials  - Property of IBM
* 5724-I66
* (c) Copyright IBM Corp.  2006, 2009
*   
*/
package com.ibm.wbit.tel.client.jsf.bean;

import java.util.HashMap;
import java.util.Set;

import com.ibm.bpc.clientcore.DataObjectUtils;
import com.ibm.bpe.jsf.exception.PropertyNotFoundException;
import com.ibm.bpe.jsf.exception.PropertyPopulationException;
import com.ibm.wbit.tel.client.jsf.infrastructure.SharedConstants;
import commonj.sdo.DataObject;

/**
 * BCCreateInstance represents a Business Case to be created on server.
 */
public class BCCreateInstance implements SharedConstants {
    
    /**
     * The value map which contains the values of the input message. 
     * Task input messages can be set by JSF pages. 
     */
	private HashMap<String, Object> inputValues = null;
		
	/**
	 * The value map which contains the values of the input message for arrays.
	 */
	private HashMap<String, ArrayInstance> inputValuesArray;
	
	/**
	 * The map to store the optional BO instance objects.
	 */
	private HashMap<String, OptionalBoInstance> optionalBo;
		
	/**
     * The value map which contains the values of the custom properties. 
     * They can be set by JSF pages. 
     */
	private HashMap<String, String> customPropertyValues = null;
	
	/**
	 * The qualified name of the input message, {NamespaceOfWSDLMessage}LocalNameOfWSDLMessage
	 */
	private String QNameInputMessage = null; 
	
	/**
	 * The type of Business Case to crate, this may be a Human Task or a Business Process.
	 */
	private int type = -1; 
	
	/**
	 * The type Business Process.
	 */
	public final static int TYPE_BUSINESS_PROCESS = 1; 
	
	/**
	 * The type Human Task.
	 */
	public final static int TYPE_HUMAN_TASK = 2; 
		 
    /**
     * The component name of the Business Case to create
     */
	private String componentName = null;
	
	 /**
     * The component name of the start activity to start Business Case 
     */
	private String componentNameStart = null;
	
	/**
     * The component namespace of the Business Case to create
     */
	private String componentNamespace = null;
	
	/**
     * The task ID for template based approach of the Business Case to create
     */
	private String taskID = null;
	
	/**
     * The main ToDo for which to create a sub ToDo
     */
    private ToDoInstance mainToDoInstance;
	
	/**
	 * Constructor
	 */
	public BCCreateInstance() {
	    super();
	    inputValues = new HashMap<String, Object>();
	    customPropertyValues = new HashMap<String, String>();
	    inputValuesArray = new HashMap<String, ArrayInstance>();
	    optionalBo = new HashMap<String, OptionalBoInstance>();
	}
	
	/**
	 * Clear all values of this instance
	 */
	public void clear(){
	    inputValues.clear();
	    inputValuesArray.clear();
	    optionalBo.clear();
	    customPropertyValues.clear();
	    type = -1; 
	    componentName = null; 
	    componentNameStart = null;
	    componentNamespace = null; 	
	    taskID = null;
	    QNameInputMessage = null;
	}

	/**
     * Get the value map which contains the values of the input message. 
     * This can be get by JSF pages. 
     * 
     * @return the value map which contains the values of the input message
     */
    public HashMap<String, Object> getInputValues() {
    		
        return inputValues;
    }
		
    /**
     * Get all values of the input message.
     * If passed <param>dataType</param> is an instance of <code>DataObject</code> the return value will also be of this type.
     * Otherwise the data primitive of input mapped to key <code>SINGLE_PRIMITIVE_TYPE</code> is returned. 
     *  
     * @param dataType the structure (may be empty) of the data to get
     * @return the values / the value of the input message
     * @throws PropertyPopulationException 
     *      		This exception will be thrown if the inputValues of this <code>BCCreateInstance</code> does not match the 
     * 				expected Service Data Object.
     * @throws PropertyNotFoundException 
     *      		This exception will be thrown if the inputValues of this <code>BCCreateInstance</code> does not match the 
     * 				expected Service Data Object.
     */
    public Object getInputValuesAll(final Object dataType) throws PropertyNotFoundException, PropertyPopulationException {
    	Object ret; 
    	
    	if (dataType instanceof DataObject) {
			ret =  DataObjectUtils.populateAll((DataObject) dataType, getValuesAll());
		} else {
			ret = inputValues.get(SINGLE_PRIMITIVE_TYPE);
		}
		return ret;    
    }
 
    /**
     * Get the value map which contains the values of the input message for arrays. 
     * This can be get by JSF pages. 
     * 
     * @return the value map which contains the values of the input message for arrays
     */
    public HashMap<String, ArrayInstance> getInputValuesArray() {
    	return inputValuesArray;
    }
    
    
     
    /**
	 * Set all values for the input message of this BCCreateInstance (array values and non array values). 
	 * If passed <param>inputData</param> is an instance of DataObject or HashMap all 
	 * values will be set to this ToDoInstance. 
	 * Otherwise passed <param>inputData</param> will be mapped to input using key SINGLE_PRIMITIVE_TYPE.
	 * 
	 * @param inputValues the values for the input message
	 */    
    @SuppressWarnings("unchecked")
	public void setInputValuesAll(Object inputData) { 
    	
    	if (inputData == null) {
			this.inputValues = null;
		} else if (inputData instanceof DataObject) {
			this.inputValues = (HashMap<String, Object>) DataObjectUtils.getValueMap((DataObject) inputData);
		} else if (inputData instanceof HashMap) {
			this.inputValues = (HashMap<String, Object>) inputData;
		} else {
			this.inputValues.put(SINGLE_PRIMITIVE_TYPE, inputData);
		}    	
    	setInputValuesArray();
    }
    
    /**
     * Set the input values to all arrays of this BCCreateInstance. 
     * 
     * @param valuesArray to which to set the values
	 * @param values from which to get all values 
     */
    private void setInputValuesArray() {
    	
    	for (ArrayInstance array : inputValuesArray.values()) {
			array.updateValues(inputValues);				
		}	   	
    }
    
    /**
	 * Initialize the input values array associated with passed arrayPrefix.
	 * The structure of nested arrays will be initialized recursively.
	 *  
	 * @param subViewID of the JSF page on which the array is shown 
	 * @param arrayPrefix of the root array to be initialized
	 */
	public void initInputValuesArray(String subViewID, String arrayPrefix) {
		
		if (!inputValuesArray.containsKey(arrayPrefix)) {
			inputValuesArray.put(arrayPrefix, new ArrayInstance(subViewID, arrayPrefix, inputValues));	
		} 
	}
	
		
			
	/**
	 * Get all input values of this BCCreateInstance.
	 * To do this the inputValuesArray and inputValues are merged. 
	 * 
	 * @return the value map which contains all values 
	 */
	private HashMap<String, Object> getValuesAll() {

		if (inputValues != null) {			
			Set<String> keys = (new HashMap<String, Object>(inputValues)).keySet();			
			//remove all old array keys, due to remove they may be invalid
			for (String key : keys ) {
				if (isPartOfArray(key) || isPartOfClosedOptionalBO(key)) {
					inputValues.remove(key);
				} 				
			}		
		} else {
			inputValues = new HashMap<String, Object>();	
		}
		for (ArrayInstance array : inputValuesArray.values()) {
			if(!isPartOfClosedOptionalBO(array.getArrayPrefix())){
				inputValues.putAll( array.transformRelativeToAbsolute() );
			}
		}	
		

		return inputValues;
	}

	/**
	 * This method will return true if the value was part of an array.
	 * 
	 * @param key
	 * 	 		The xpath of the value
	 * @return
	 * 		 True if the element is part of an array.
	 */
	private boolean isPartOfArray(String key) {
		if(this.inputValuesArray.size() == 0){
			return false;
		}
		return key.contains("[");
	}    
     
	/**
	 * This method will return true if the value was part of on closed
	 * optional BO.
	 * 
	 * @param key
	 * 			The xpath of the value
	 * 
	 * @return
	 * 			True if at least one parent BO is closed
	 */
    private boolean isPartOfClosedOptionalBO(String key) {
    	boolean allParentsOpen = true;
    	
    	for(String prefix : optionalBo.keySet()){
    		if(key.startsWith(prefix)){
    			allParentsOpen = allParentsOpen && optionalBo.get(prefix).isVisible();
    		}
    	}
    	
    	return !allParentsOpen;
	}

	/**
     * Get the value map which contains the values of the custom properties.  
     * They can be set by JSF pages. 
     * 
     * @return the value map which contains the values of the custom properties
     */
    public HashMap<String, String> getCustomPropertyValues() {
        return customPropertyValues;
    }
    
    /**
     * Get qualified name of the input message, format: {NamespaceOfWSDLMessage}LocalNameOfWSDLMessage
     * 
     * @return qualified name of the input message
     */
    public String getQNameInputMessage() {
        return QNameInputMessage;
    }
    
    /**
     * Set qualified name of the input message, format: {NamespaceOfWSDLMessage}LocalNameOfWSDLMessage
     * 
     * @param nameInputMessage qualified name of the input message
     */
    public void setQNameInputMessage(String nameInputMessage) {
        QNameInputMessage = nameInputMessage;
    }
    
    /**
     * Get the component name of this Business Case
     * 
     * @return component name of this Business Case
     */
    public String getComponentName() {
        return componentName;
    }
    
    /**
     * Set the component name of this Business Case
     * 
     * @param component name of this Business Case
     */
    public void setComponentName(String componentName) {
        this.componentName = componentName;
    }
    
    /**
     * Get the component name used to start this Business Case
     * 
     * @return component name of this Business Case
     */
    public String getComponentNameStart() {
        return componentNameStart;
    }
    
    /**
     * Set the component name used to start this Business Case
     * 
     * @param component name used to start this Business Case
     */
    public void setComponentNameStart(String componentNameStart) {
        this.componentNameStart = componentNameStart;
    }
    
    /**
     * Get the component namespace of this Business Case
     * 
     * @return component namespace of this Business Case
     */
    public String getComponentNamespace() {
        return componentNamespace;
    }
    
    /**
     * Set the component namespace of this Business Case
     * 
     * @param component namespace of this Business Case
     */
    public void setComponentNamespace(String componentNamespace) {
        this.componentNamespace = componentNamespace;
    }
    
    /**
     * Get the task ID (accroding to template based approach) 
     * of this Business Case
     * 
     * @return task ID of this Business Case
     */
    public String getTaskID() {
        return taskID;
    }
        
    /**
     * Set the task ID (accroding to template based approach) 
     * of this Business Case
     * 
     * @param task ID of this Business Case
     */
    public void setTaskID(String taskID) {
        this.taskID = taskID;
    }
    
    /**
     * The main ToDo for which to create a sub ToDo
     */
    public ToDoInstance getMainToDoInstance() {
		return mainToDoInstance;
	}

    /**
     * The main ToDo for which to create a sub ToDo
     */
	public void setMainToDoInstance(ToDoInstance mainToDoInstance) {
		this.mainToDoInstance = mainToDoInstance;
	}

	/**
     * Get the type of this Business Case
     * 
     * @return type of this Business Case
     */
    public int getType() {
        return type;
    }
    
    /**
     * Set the type of this Business Case
     * 
     * @param type of this Business Case
     */
    public void setType(int type) {
        this.type = type;
    }   
      

    public HashMap<String, OptionalBoInstance> getInputValuesOptionalBO() {
    	return optionalBo;
    }
    
	public void initInputValuesOptionalBO(String xpath) {		
		if (!optionalBo.containsKey(xpath)) {
			OptionalBoInstance instance = new OptionalBoInstance();
			boolean visible = instance.isVisible() | isOptionalVisible(this.inputValues,xpath);
			instance.setVisible(visible);			
			optionalBo.put(xpath, instance);
		} 
	}
	 
    
    private boolean isOptionalVisible(HashMap<String,Object> values, String xpath) {

		for(String key : values.keySet()){				
			if(key.length() > xpath.length() && key.startsWith(xpath) && !"".equals(values.get(key))){
				return true;
   			}	    			    				
		}		
		return false;
	}
}
