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

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

import meta.ampl.runner.AMPLException;
import meta.model.Architecture;
import meta.model.Device;
import meta.util.FileUtils;
public class AMPLmodel {
	private HashMap<String, List<String>> deviceMap;
	private List<String> atmosphere;
	private List<String> core_diffuser;
	private List<String> fan_diffuser;
    private List<String> singlefan_dtc;
    private List<String> singlefan_ndtc;
    private List<String> multifan_dtc;
    private List<String> multifan_ndtc;
    private List<String> singleprop_dtc;
    private List<String> singleprop_ndtc;
    private List<String> multiprop_dtc;
    private List<String> multiprop_ndtc;
    private List<String> core;
    private List<String> fan_nozzle;
    private List<String> core_nozzle;
    private List<String> embedded;
	public AMPLmodel() throws AMPLException {
		deviceMap = new HashMap<String, List<String> >();
		atmosphere = FileUtils.read("data\\amplTemplates\\Model\\Atmosphere.txt");
		core_diffuser = FileUtils.read("data\\amplTemplates\\Model\\Core_Diffuser.txt");
		fan_diffuser = FileUtils.read("data\\amplTemplates\\Model\\Fan_Diffuser.txt");		
		singlefan_dtc = FileUtils.read("data\\amplTemplates\\Model\\Single_Fan_(DTC).txt");
		singlefan_ndtc = FileUtils.read("data\\amplTemplates\\Model\\Single_Fan_(nDTC).txt");
		multifan_dtc = FileUtils.read("data\\amplTemplates\\Model\\Multiple_Fans_(DTC).txt");
		multifan_ndtc = FileUtils.read("data\\amplTemplates\\Model\\Multiple_Fans_(nDTC).txt");
		singleprop_dtc = FileUtils.read("data\\amplTemplates\\Model\\Single_Front_Prop.txt");
		singleprop_ndtc = FileUtils.read("data\\amplTemplates\\Model\\Single_Rear_Prop.txt");
		multiprop_dtc = FileUtils.read("data\\amplTemplates\\Model\\Multiple_Front_Props.txt");
		multiprop_ndtc = FileUtils.read("data\\amplTemplates\\Model\\Multiple_Rear_Props.txt");
		core = FileUtils.read("data\\amplTemplates\\Model\\Core.txt");
		fan_nozzle = FileUtils.read("data\\amplTemplates\\Model\\Fan_Nozzle.txt");
		core_nozzle = FileUtils.read("data\\amplTemplates\\Model\\Core_Nozzle.txt");
		embedded = FileUtils.read("data\\amplTemplates\\Model\\Embedded.txt");
		
		deviceMap.put("Atmosphere", atmosphere);
		deviceMap.put("Core_Diffuser", core_diffuser);
		deviceMap.put("Fan_Diffuser", fan_diffuser);
		deviceMap.put("Single_Fan_(DTC)", singlefan_dtc);
		deviceMap.put("Single_Fan_(nDTC)", singlefan_ndtc);
		deviceMap.put("Multiple_Fans_(DTC)", multifan_dtc);
		deviceMap.put("Multiple_Fans_(nDTC)", multifan_ndtc);
		
		deviceMap.put("Single_Front_Prop", singleprop_dtc);
		deviceMap.put("Single_Rear_Prop", singleprop_ndtc);
		deviceMap.put("Multiple_Front_Props", multiprop_dtc);
		deviceMap.put("Multiple_Rear_Props", multiprop_ndtc);
		
		deviceMap.put("Core_Nozzle", core_nozzle);
		deviceMap.put("Fan_Nozzle", fan_nozzle);
		deviceMap.put("Embedded", embedded);
		deviceMap.put("Core", core);
	}
	/**
	 * 
	 * @param source - source index
	 * @param sink   - sink index
	 * @param template - 
	 * @return
	 */
	private List<String> newSourceDevice(String source, String sink, List<String> template) {
		List<String> lines = new ArrayList<String>();
		for (String line : template) {
			String s = line.replaceAll("\\$", source);
			s = s.replaceAll("%", sink);
			lines.add(s);
		}
		return lines;
	}
	/**
	 * 
	 * @param source - source index
	 * @param sink   - sink index
	 * @param template - 
	 * @return
	 */
	private List<String> newSinkDevice(String source, String sink, List<String> template) {
		List<String> lines = new ArrayList<String>();
		for (String line : template) {
			String s = line.replaceAll("%", source);
			s = s.replaceAll("\\$", sink);
			lines.add(s);
		}
		return lines;
	}
	/**
	 * Build the AMPL model
	 * @param arch
	 * @return ampl model
	 * @throws AMPLException
	 */
	 public List<String> build(Architecture arch) throws AMPLException {
	 // used to decide if device has already been seen	 
     Set<String> deviceSet = new HashSet<String>();	
     boolean srcDiff = false;
     boolean sinkDiff = false;
	 ArrayList<String> model = new ArrayList<String>();
	 int[] sources = arch.getSourceIndexes();
	 int[] sinks = arch.getSinkIndexes();
	 List<String> at = deviceMap.get("Atmosphere");
	 List<String> amplatDevice = newSourceDevice("1","1",at );
	 model.addAll(amplatDevice);
	 for ( int i=0;i<sinks.length; i++ ) {
		 int j = sinks[i];
		 Device device = arch.getDevice(j);
		 String name = device.getName();
		 if ( name.startsWith("Core_Diffuser")) sinkDiff = true;
		 if ( deviceSet.contains(name)) continue;
		 List<String> template = deviceMap.get(name);
		 if ( template == null){
			 System.out.println("**** NO TEMPLATE "+name);
			 continue;
		 }
		 String sink = Integer.toString(j);
		 int k = sources[i];
		 String source = Integer.toString(k);
		 List<String> amplDevice = newSinkDevice(source,sink,template);
		 model.addAll(amplDevice);
		 deviceSet.add(name);
	 }
	 // ***************
	 for ( int i=0; i<sources.length;i++) {
		 int j = sources[i];
		 Device device = arch.getDevice(j);
		 String name = device.getName();
		 if ( name.startsWith("Core_Diffuser"))  {
			 if ( ! sinkDiff ) {
				 srcDiff = true;
			 }
		 }
		 if ( deviceSet.contains(name)) continue;
		 List<String> template = deviceMap.get(name);
		 if ( template == null){
			 System.out.println("**** NO TEMPLATE "+name);
			 continue;
		 }
		 String source = Integer.toString(j);
		 int k = sinks[i];
		 String sink = Integer.toString(k);
		 if ( srcDiff ) sink = "1";
		 List<String> amplDevice = newSourceDevice(source,sink,template);
		 model.addAll(amplDevice);
	 }
	 
	 // if the engine architecture is embedded add the embedded block
	 if ( arch.isEngineEmbedded() ) {
		 List<String> embeddedTemplate = deviceMap.get("Embedded");
		 model.addAll(embeddedTemplate);
	 }
	 Balance balance = FlowBalanceFactory.create(arch);
	 List<String> flowBalance =  balance.getEquations();
	 
	
	 model.addAll(flowBalance);
	 List<String> objective = getObjective();
	 model.addAll(objective);
	 return filter(model);
	//   return model;
	}
	/**
	 * Move the param and var declarations to the top and
	 * filter out duplicate lines.
	 */ 
	private List<String> filter(List<String> model) {
		   Set<String> filter = new LinkedHashSet<String>();
		   for ( String s : model) {
			   if ( s.startsWith("var" ) ) filter.add(s);
			   if ( s.startsWith("param" ) ) filter.add(s);
		   }
		   for ( String s : model) {
			   if ( s.startsWith("var" ) )  continue;
			   if ( s.startsWith("param" ) ) continue;
			   filter.add(s);
		   }
		   List<String> list = new ArrayList<String>(filter); 
		return list;
	}
	/**
	 * Return the objective function template
	 */
	private List<String> getObjective() throws AMPLException {
		return FileUtils.read("data\\amplTemplates\\Model\\Objective.txt");
	}
}
