/*
 * 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 meta.model;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;

import meta.util.Export;
public class ArchitectureImpl  implements Architecture {
	private List<Link> linkTable;
	private List<String> nodeTable;
	private HashMap<String, Device> devices;
	private HashMap<String, Flow> flows;
	private String name;
	private String engineType;
	private boolean embeddedEngine;
	private boolean rearProp;
	public ArchitectureImpl(String linkTableFilename) {
		embeddedEngine = false;
		rearProp = false;
		linkTable = new ArrayList<Link>();
		devices = new HashMap<String,Device>();
		flows = new HashMap<String,Flow>();
		name = linkTableFilename.replace("AEEOutput_LT", "Arch_");
		name = name.replace(".txt", "");
		engineType = "?";
	}
	public void setEngineType(String et) {
		engineType = et;
	}
	public String getEngineType() {
		return engineType;
	}
	/**
	 * The node table used for output 
	 * @see AMPLresults
	 */
	public void setNodeTable(List<String> nt) {
		nodeTable = nt;
	}
	/**
	 * Report the node table used for output
	 * @see Export
	 */
	public List<String> getNodeTable() {
		return nodeTable;
	}
	/**
	 * Engine is embedded if we find 'Embedded' in the AEEOutput_NTx.txt file
	 * for this architecture
	 */
	public void setEmbeddedEngine(boolean e) {
		embeddedEngine = e;
	}
	/**
	 * Reports if the engine is embedded.
	 */
	public boolean isEngineEmbedded() {
		return embeddedEngine;
	}
	public void setRearProp(boolean r) {
		rearProp = r;
	}
	/**
	 * Reports if the engine has a prop in the back
	 */
	public boolean hasRearProp() {
		return rearProp;
	}
	/**
	 * Report the link table
	 */
	public List<Link> getLinkTable() {
		return linkTable;
	}
	/**
	 * Report the architecture name
	 */
	public String getName() 
	{
		return name;
	}
	/**
	 * Add a device
	 */
	public void add(Device device) {
		int id = device.getIndex();
		String key = Integer.toString(id);
		devices.put(key, device);
	}
	/**
	 * Add a flow
	 */
	public void add(Flow flow) {
		int id = flow.getIndex();
		String key = Integer.toString(id);
		flows.put(key, flow);
	}
	/**
	 * Add a link
	 */
	public void add(Link link) {
		 linkTable.add(link);
	}
	/* (non-Javadoc)
	 * @see meta.model.Architechture#getDevice()
	 */
	public Device getDevice(int id) {
		String key = Integer.toString(id);
		return devices.get(key);
	}
	/* (non-Javadoc)
	 * @see meta.model.Architechture#getFlow()
	 */
	public Flow getFlow(int id) {
		String key = Integer.toString(id);
		return flows.get(key);
	}
	/* (non-Javadoc)
	 * @see meta.model.Architechture#getDeviceIndexes()
	 */
	public int[] getDeviceIndexes() {
		return getVector(devices);
	}
	/* (non-Javadoc)
	 * @see meta.model.Architechture#getDeviceNames()
	 */
	public String[] getDeviceNames() {
		return getNames(devices);
	}
	/* (non-Javadoc)
	 * @see meta.model.Architechture#getFlowNames()
	 */
	public String[] getFlowNames() {
		return getNames(flows);
	}
	/* (non-Javadoc)
	 * @see meta.model.Architechture#getFlowIndexes()
	 */
	public int[] getFlowIndexes() {
		return getVector(flows);
	}
	/* (non-Javadoc)
	 * @see meta.model.Architechture#getFlowTypeIndexes()
	 */
	public int[] getFlowTypeIndexes() {
		int size = linkTable.size();
		int[] ints = new int[size];
		int i = 0;
		for ( Link link : linkTable ) {
		  Flow flow = link.getFlow();
		  int type = flow.getIndex();
		  ints[i] = type;
		  i++;
		}
		return ints;
	}
	/* (non-Javadoc)
	 * @see meta.model.Architechture#getSourceIndexes()
	 */
	public int [] getSourceIndexes() {
		int size = linkTable.size();
		int[] ints = new int[size];
		int i = 0;
		for ( Link link : linkTable ) {
		  Device device = link.getSource();
		  int type = device.getIndex();
		  ints[i] = type;
		  i++;
		}
		return ints;	
	}
	/* (non-Javadoc)
	 * @see meta.model.Architechture#getSinkIndexes()
	 */
	public int [] getSinkIndexes() {
		int size = linkTable.size();
		int[] ints = new int[size];
		int i = 0;
		for ( Link link : linkTable ) {
		  Device device = link.getSink();
		  int type = device.getIndex();
		  ints[i] = type;
		  i++;
		}
		return ints;	
	}
	/**
 * Transform a Type index into an int array to send to MATLAB
 * @param map
 * @return
 */
	private int[] getVector(HashMap<String, ? extends Type> map) {
		Set<String> keySet = map.keySet();
		int size = keySet.size();
		int[] ints = new int[size];
		int i = 0;
		for (String key : keySet) {
			int index = Integer.parseInt(key);
			ints[i] = index;
			i++;
		}
		return ints;
	}
	private String[] getNames(HashMap<String, ? extends Type> map) {
		Set<String> keySet = map.keySet();
		int size = keySet.size();
		String[] names = new String[size];
		int i = 0;
		for (String key : keySet) {
			Type type = map.get(key);
			names[i] = type.getName();
			i++;
		}
		return names;
	}
	/**
	 * Report a List of Devices that are only present in the architecture 
	 * (not all possible devices)
	 */
	public List<Device> getDevices() {
		 int[] src = getSourceIndexes();
		 int[] sink = getSinkIndexes();
		 List<Device> dList = new ArrayList<Device>();
		 // go through the link table and find all the devices.
		 // use a hash so we only have one of each device.
		 Set<Integer> devIndexes = new HashSet<Integer>();
		 for ( int i=0; i< src.length;i++) {
			 int j = src[i];
			 devIndexes.add(j);
		 }
		 for ( int i=0; i< src.length;i++) {
			 int j = sink[i];
			 devIndexes.add(j);
		 }
		 List<Integer> list = new ArrayList<Integer>(devIndexes);
		 for (Integer id : list) {
			 Device device = this.getDevice(id);
			 dList.add(device);
		 }
		 return dList;
	}
	/**
	 * Export the architecture to XMI
	 */
	public void toXMI() {
		new Export().toXMI(this);
	}
}
