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

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Map;

import meta.aee.AEE;
import meta.ampl.runner.AMPLException;
import meta.ampl.runner.AMPLprocess;
import meta.ampl.runner.AMPLprocessFactory;
import meta.ampl.runner.SolutionStatus;
import meta.ampl.runner.Solver;
import meta.ampl.runner.Variable;
import meta.model.Architecture;
import meta.model.Link;
import meta.model.ampl.AMPLdata;
import meta.model.ampl.AMPLmodel;
import meta.model.ampl.AMPLresults;
import meta.util.FileUtils;

public class Controller    {
	private static Controller instance = null;
	private static int LEVEL;
	public boolean isInShutdown() {
		return false;
	}
	/**
	 * Get the singleton instance of the controller for the client
	 */
	public static Controller getInstance() {
		if (instance == null)
			instance = new Controller();
		return instance;
	}
	/**
	 * Private constructor for singleton pattern. 
	 * Obtain the abstraction level from the command line -DLEVEL=
	 */
	private Controller() {
		String level = System.getProperty("LEVEL");
		if ( level == null) {
			LEVEL = 0;
		}
		try {
			LEVEL = Integer.parseInt(level);
		}
		catch (NumberFormatException  e ) {
			System.out.println("Warning: invalid abstraction level specified. level set to 0");
			LEVEL = 0;
		}
	}
	public void control() throws AMPLException {
	   AEE aee = new AEE();	
	   Architecture arch;
	   AMPLprocess process = AMPLprocessFactory.create();
	   while ( true) {
	       arch = aee.getNextArchitecture();
	       if ( arch == null) break;
		   arch.toXMI();
		   System.out.println("Processing Architecture:" + arch.getName());
	       List<Link> link1 = arch.getLinkTable();
	       for ( Link l : link1) {
	    	   System.out.println(l.getSource().getName() + "-> " + l.getSink().getName());
	       }
	       String path = System.getProperty("java.io.tmpdir");
	       AMPLmodel amplModel = new AMPLmodel();
		   List<String> model = amplModel.build(arch);
		   String fn = String.format("%s\\%s.mod",path,arch.getName());
		   FileUtils.writeFile(fn,model); 
		   AMPLdata amplDat = new AMPLdata();
		   List<String> data = amplDat.build(arch);
		   fn = String.format("%s\\%s.dat",path,arch.getName());
		   FileUtils.writeFile(fn,data); 
		   process.setModel(model);
		   process.setData(data);
		   process.setSolver(Solver.IPOPT);
	       process.solve();
	       SolutionStatus solutionStatus = process.getSolutionStatus();
	       System.out.println(solutionStatus);
	       Map<String,Variable> map = process.getVariables();
	       new AMPLresults(arch,map).out();
	       new AMPLresults(arch,map).outXML();
		   process.reset();
	   }	 
	   process.shutdown(); 
	}
	/**
	 * Report the abstraction level
	 */
	public int getAbstractionLevel() {
		return LEVEL;
	}
	private static void complete() {
	   StringBuffer msg = new StringBuffer();
	   String CONTENT_LENGTH = "Content-Length: ";
	   String complete = "<status code=\"0\">success</status>";
	   int size = complete.length();
	   String length = String.valueOf(size);
	   msg.append("POST run://localhost:8081\n");
	   msg.append(CONTENT_LENGTH);
	   msg.append(length);
	   msg.append("\n\n");
	   msg.append(complete);
		  String host = "localhost";
		  int port =  8080;
		 
		  try {
			  Socket sock = new Socket(host,port);
			    BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
			    wr.write(msg.toString());
			    wr.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
	}
	public static void main(String [] args) throws AMPLException, IOException {
		  int port = Integer.parseInt(args[0]);
          ServerSocket serverSocket = new ServerSocket(port);
          System.out.println(String.format("Optimization Server waiting on port %d",port));
          Socket socket = serverSocket.accept();
          socket.close();
          System.out.println(String.format("Optimization Server started on port %d",port));
		  Controller.getInstance().control();
		  complete();
	}
}
