/*
 * Delivered to the U.S. Government in Accordance with Contract FA8650-10-C-7080.
 *
 * EXPORT CONTROLLED
 * ECCN#   EAR99
 * Warning: This document contains technical data which is controlled under the
 * Bureau of Industry & Security's Export Administration Regulations and may not be
 * exported or re-exported without authorization by the Bureau of Industry & Security.
 * Diversion contrary to US law is prohibited.
 *
 * (c) 2011 United Technologies Corp.
 * Developed with the sponsorship of the Defense Advanced Research Projects Agency (DARPA).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this data,
 * including any software or models in source or binary form, as well as any drawings, 
 * specifications, and documentation (collectively "the Data"), to deal in the Data without 
 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Data, and to permit persons to whom the Data
 * is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Data.
 *
 * THE DATA IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS, SPONSORS, DEVELOPERS, CONTRIBUTORS, OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE DATA OR THE
 * USE OR OTHER DEALINGS IN THE DATA.
*/

package aee.engine.rule;

import java.util.ArrayList;
import java.util.List;

import org.jdom.Attribute;
import org.jdom.Element;

import aee.engine.TypedObject;
import aee.exception.AeeException;


/**
 * Defines a design rule operation
 */
public class Operation extends TypedObject {
	private OperType     type;
	private String       result;
	private Term         termPrim;
	private List<OpTerm> termSecs;
	private static final String ElementName = "operation";
	
	/**
	 * Constructor
	 * @param root  The element
	 * @throws AeeException if any unexpected XML errors detected
	 */
	@SuppressWarnings("unchecked")
	public Operation(Element root) throws AeeException {
		super(root, ElementName);
		type     = null;
		result   = null;
		termPrim = null;
		termSecs = null;

		// result attribute is optional. type is required
		Attribute attr = root.getAttribute("type");
		if (attr == null)
			throw new AeeException("Missing type for %s element", ElementName);
		try {
			type = OperType.parse(getAttrValue(attr));
		} catch (Exception ex) {
			throw new AeeException(ex);
		}

		attr = root.getAttribute("result");
		if (attr != null) {
			String val = getAttrValue(attr);
			if (! "".equals(val))
				result = val;
		}
		
		// children must be in the order: term [[operator term]...]
		// EXCEPT for certain types. In this case, all we have are terms
		List<Element> children = root.getChildren();
		int           n        = children.size();
		if (n > 0) {
			termPrim = new Term(children.get(0));
			if (n > 1) {
				if (type.nullOperator()) {
					termSecs = new ArrayList<OpTerm>();
					for (int i = 1; i < n; i++) {
						termSecs.add(new OpTerm(null, children.get(i)));
					}
					
				} else {
					if (((n-1)/2)*2 != (n-1))
						throw new AeeException("Invalid %s element. " +
							                     "Terms must be interspersed with operators", 
							                     ElementName);
					termSecs = new ArrayList<OpTerm>();
					for (int i = 1; i < n; i+= 2) {
						termSecs.add(new OpTerm(children.get(i), children.get(i+1)));
					}
				}
			}
		}
		
		if (termPrim == null)
			throw new AeeException("%s element is missing a primary term", ElementName);
	}
	
	/**
	 * Report the operation type
	 * @return The type. Never null
	 */
	public OperType getType() {
		return type;
	}
	
	/**
	 * Report the operation result
	 * @return The result or null if not specified
	 */
	public String getResult() {
		return result;
	}
	
	/**
	 * Report the operation result as a string
	 * @return The result. No result produces ""
	 */
	public String getResultString() {
		return (result == null)? "" : result;
	}
	
	/**
	 * Report the primary term
	 * @return The term. Never null
	 */
	public Term getPrimaryTerm() {
		return termPrim;
	}
	
	/**
	 * Report all secondary terms
	 * @return A list of secondary terms. null if none
	 */
	public List<OpTerm> getSecondaryTerms() {
		return (termSecs == null)? null : new ArrayList<OpTerm>(termSecs);
	}
	
	/**
	 * Report the terms in a format that can be used by external applications
	 * @return An array of term operator term operator ...
	 */
	public String[] getExternalTerms() {
		List<String> terms = new ArrayList<String>();
		
		// First the primary term
		for (String component : termPrim.toExternalString()) {
			terms.add(component);
		}
		
		// Then append all secondary terms
		if (termSecs != null) {
			for (OpTerm term : termSecs) {
				for (String component : term.toExternalString()) {
					terms.add(component);
				}
			}
		}
		return terms.toArray(new String[0]);
	}
}
