package org.muml.eloquent.algorithm.ilp.gurobi;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;

import org.eclipse.core.runtime.Status;
import org.eclipse.m2m.qvt.oml.util.Dictionary;
import org.muml.psm.allocation.ilp.IntegerLinearProgram;

public class Gurobi {
	private static final String gurobiBinary = "gurobi_cl";
	private static final int success = 0;
	
	private static final String unexpectedLine = "Got unexpected line: %s";
	
	private static final String solSuffix = ".sol";
	private static final String lpSuffix = ".lp";
	
	public static int solve(IntegerLinearProgram ilp,
			Dictionary<String, Integer> solution) {
		int ret = success;
		File solFile = null;
		File lpFile = null;
		try {
			solFile = File.createTempFile("gurobi", solSuffix);
			lpFile = File.createTempFile("gurobi", lpSuffix);
			long startTime = System.currentTimeMillis();
			serializeILP(ilp, lpFile);
			Double finalTime = Double.valueOf(Double.valueOf(System.currentTimeMillis() - startTime)
					.doubleValue() / 1000d);
			
			Status logTransformationTime = new Status(Status.INFO,Activator.PLUGIN_ID,"Time for serialize the ILP as Gurobi Input: "+finalTime+" seconds");
			// writes log into the .log file within the .metadata folder of the workspace
			Activator.getDefault().getLog().log(logTransformationTime);
		
			startTime = System.currentTimeMillis();
			ret = runGurobi(lpFile.getAbsolutePath(),
					solFile.getAbsolutePath());

			finalTime = Double.valueOf(Double.valueOf(System.currentTimeMillis() - startTime)
					.doubleValue() / 1000d);
			
			Status logSolvingTime = new Status(Status.INFO,Activator.PLUGIN_ID,"Time for solve the ILP Model Using Gurobi: "+finalTime+" seconds");
			// writes log into the .log file within the .metadata folder of the workspace
			Activator.getDefault().getLog().log(logSolvingTime);
		
			if (ret == success) {
				parseSolution(solFile, solution);
			}
		} catch (IOException|InterruptedException e) {
			throw new IllegalStateException(e);
		} finally {
			if (solFile != null) {
				solFile.delete();
			}
			// quite unlikely that solFile.delete throws an exception...
			if (lpFile != null) {
				lpFile.delete();
			}
		}
		return ret;
	}
	
	public static void serializeILP(IntegerLinearProgram ilp, File file) throws FileNotFoundException {
		FileOutputStream fos = new FileOutputStream(file);
		new LPM2T().serialize(ilp, fos);
	}
	
	public static int runGurobi(String lpFilename, String solFilename) throws IOException, InterruptedException {
		// keep fingers crossed that we do not eventually block,
		// because we read neither stdout nor stderr
		Process process = new ProcessBuilder(gurobiBinary,
				"ResultFile=" + solFilename, lpFilename).start();
		return process.waitFor();
	}
	
	public static void parseSolution(File solFile, Dictionary<String, Integer> solution) throws FileNotFoundException, IOException {
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new FileReader(solFile));
			String line;
			while ((line = reader.readLine()) != null) {
				if (line.startsWith("#")) {
					// ignore comment
					continue;
				}
				String[] splittedLine = line.split(" ");
				if (splittedLine.length != 2 ||
						!splittedLine[1].equals("0") && !splittedLine[1].equals("1")) {
					throw new IllegalStateException(
							String.format(unexpectedLine, line));
				}
				solution.put(splittedLine[0], Integer.parseInt(splittedLine[1]));
			}
		} finally {
			if (reader != null) {
				reader.close();
			}
		}
	}

}

@@ -219,16 +219,114 @@
 ception;
+%0Aimport java.io.InputStream;%0Aimport java.io.InputStreamReader;%0Aimport java.util.stream.Collectors;
 %0A%0Aimport
@@ -2727,215 +2727,1096 @@
 %09// 
-keep fingers crossed that we do not eventually block,%0A%09%09// because we read neither stdout nor stderr%0A%09%09Process process = new ProcessBuilder(gurobiBinary,%0A%09%09%09%09%22ResultFile=%22 + solFilename, lpFilename).start();
+we read stdout and stderr to avoid blocking%0A%09%09Process process = new ProcessBuilder(gurobiBinary,%0A%09%09%09%09%22ResultFile=%22 + solFilename, lpFilename).start();%0A%09%09InputStream stdout = process.getInputStream();%0A%09%09InputStream stderr = process.getErrorStream();%0A%09%09%0A%0A        if(stderr.available()%3E0)%0A        %7B%0A%09        String resultErr = new BufferedReader(new InputStreamReader(stderr))%0A%09        %09%09  .lines().collect(Collectors.joining(%22%5Cn%22));%0A%09        %0A%09%09%09Status logError = new Status(Status.ERROR,Activator.PLUGIN_ID,resultErr);%0A%09%09%09// writes log into the .log file within the .metadata folder of the workspace%0A%09%09%09Activator.getDefault().getLog().log(logError);%0A        %7D%0A%09%09%0A        if(stdout.available()%3E0)%0A        %7B%0A%09        String result = new BufferedReader(new InputStreamReader(stderr))%0A%09        %09%09  .lines().collect(Collectors.joining(%22%5Cn%22));%0A%09        %0A%09        Status logGurobiOutput = new Status(Status.INFO,Activator.PLUGIN_ID,result);%0A%09%09%09// writes log into the .log file within the .metadata folder of the workspace%0A%09%09%09Activator.getDefault().getLog().log(logGurobiOutput);%0A        %7D%0A%09%09%0A%09%09%09%09
 %0A%09%09r

add reading of stdout and stderr as sometimse the process blocks