import java.io.*;
import java.util.ArrayList;


public class PartialHeur {
	


	private static Runtime R = Runtime.getRuntime(); 
	private static double time0; 
	public static void initTime() {time0 = System.currentTimeMillis();} 
	public static double askTime() {return(System.currentTimeMillis() - time0)/1000.;} 
	static double tempo = 0;
	static int numInput;
	static int numOutput;
	static int numProdottiSop;

	

	public static Pla[] init(String nomeFile, int versioneS, int versioneH) {
		Pla[] plas = new Pla[1];
		plas[0] = new Pla(versioneH);
		String nextLine;
		try {		    
		    BufferedReader in = new BufferedReader(new FileReader(nomeFile+".pla2"));
		    int n = 0;
		    int o = 0;
		    int prodotti = 0;
		    char[] prodotto;
		    nextLine = in.readLine();
		    while (nextLine != null) { 
		    	if(nextLine.charAt(1) == 'i') {
		    		String var = nextLine.substring(3);
		    		n = Integer.parseInt(var);
		    		numInput = n;
		    	}
		    	if(nextLine.charAt(1) == 'o') {
		    		String var = nextLine.substring(3);
		    		o = Integer.parseInt(var);
		    		numOutput = o;
		    		if (o != 1) {
		    			plas = new Pla[o];
		    			for(int k = 0; k < o; k++) plas[k] = new Pla(versioneH);
		    		}
		    	}
		    	else if(nextLine.charAt(0) == '-' || nextLine.charAt(0) == '1' || nextLine.charAt(0) == '0') {
		    		prodotto = new char[n];
		    		for(int j = 0; j < n; j++) 
		    			prodotto[j] = nextLine.charAt(j);
		    		for(int j = n+1; j <= n+o; j++) {
		    			if(nextLine.charAt(j) == '-' || nextLine.charAt(j) == '1')
		    				plas[j-(n+1)].aggiungi(new Cubo(prodotto, versioneS));
		    		}
		    	}
		    	nextLine = in.readLine();
		    }  
		    in.close();
		} catch(IOException e) {
		    System.out.println("Problemi di I/O");
		}
		String s = null;
        try {
        	String outEspresso = nomeFile+".sop";
        	FileWriter fileout = new FileWriter(outEspresso);
    	    BufferedWriter filebuf = new BufferedWriter(fileout);
    	    PrintWriter printout = new PrintWriter(filebuf);
    	    // call to espresso
        	//String esegui = "../../paperi/scontrini/esperimentiDSOP/sis-1.3.6/espresso/./espresso -t "+nomeFile+".pla"; 
    	    String esegui = "espresso.exe -t "+ nomeFile+".pla";
        	Process p = Runtime.getRuntime().exec(esegui);
            BufferedReader stdInput = new BufferedReader(new 
                 InputStreamReader(p.getInputStream()));
            BufferedReader stdError = new BufferedReader(new 
                 InputStreamReader(p.getErrorStream()));
            while ((s = stdInput.readLine()) != null) {
	              printout.println(s);
	              if(s.startsWith("# ESPRESSO")) {
	            		String tempoE = s.substring(20);
	            		if(tempoE.charAt(1) == '.') tempoE = tempoE.substring(0,4);
	            		else tempoE = tempoE.substring(0,5);
	            		//System.out.println(tempoE);
	            		tempo += Double.parseDouble(tempoE);
	              }
            }
	      
            printout.close(); 
    		fileout.close();
        }
        catch (IOException e) {
            System.out.println("exception happened - here's what I know: ");
            e.printStackTrace();
            System.exit(-1);
        }
        return plas;
	}
	
	
	public static  Pla[] lettura(String nomeFile, int versioneS, int versioneH) {
		Pla[] plas = new Pla[1];
		plas[0] = new Pla(versioneH);
		String nextLine;
		try {
		    BufferedReader in = new BufferedReader(new FileReader(nomeFile));
		    int n = 0;
		    int o = 0;
		    int prodotti = 0;
		    char[] prodotto;
		    nextLine = in.readLine();
		    while (nextLine != null) { 
		    	if(nextLine.charAt(1) == 'i') {
		    		String var = nextLine.substring(3);
		    		n = Integer.parseInt(var);
		    		numInput = n;
		    	}
		    	if(nextLine.charAt(1) == 'o') {
		    		String var = nextLine.substring(3);
		    		o = Integer.parseInt(var);
		    		numOutput = o;
		    		if (o != 1) {
		    			plas = new Pla[o];
		    			for(int k = 0; k < o; k++) plas[k] = new Pla(versioneH);
		    		}
		    	}
		    	if(nextLine.charAt(1) == 'p') {
		    		String var = nextLine.substring(3);
		    		numProdottiSop = Integer.parseInt(var);
		    	}
		    	else if(nextLine.charAt(0) == '-' || nextLine.charAt(0) == '1' || nextLine.charAt(0) == '0') {
		    			prodotto = new char[n];
		    			for(int j = 0; j < n; j++) 
		    				prodotto[j] = nextLine.charAt(j);
		    			for(int j = n+1; j <= n+o; j++) {
		    				if(nextLine.charAt(j) == '1')
		    					plas[j-(n+1)].aggiungi(new Cubo(prodotto, versioneS));
		    			}
		    	}
		    	nextLine = in.readLine();
		    }  
		    in.close();
		} catch(IOException e) {
		    System.out.println("Problemi di I/O");
		}
		return plas;
	}
	
	
	public static void scrivi(ArrayList pla, ArrayList dc, String nomeFile) {
		String nextLine;
		try {
			FileWriter fileout = new FileWriter(nomeFile);
	        BufferedWriter filebuf = new BufferedWriter(fileout);
	        PrintWriter printout = new PrintWriter(filebuf);
	        printout.println(".i "+numInput);
	        printout.println(".o "+numOutput);
	        for(int i = 0; i < pla.size(); i++)
	        	printout.println(pla.get(i));
	        for(int i = 0; i < dc.size(); i++)
	        	printout.println(dc.get(i));
	        printout.close(); 
	        fileout.close();
		} catch(IOException e) {
		    System.out.println("Problemi di I/O");
		}
	}
	
	public static void scrivi(ArrayList pla, String nomeFile) {
		String nextLine;
		try {
			FileWriter fileout = new FileWriter(nomeFile);
	        BufferedWriter filebuf = new BufferedWriter(fileout);
	        PrintWriter printout = new PrintWriter(filebuf);
	        printout.println(".i "+numInput);
	        printout.println(".o "+numOutput);
	        for(int i = 0; i < pla.size(); i++)
	        	printout.println(pla.get(i));
	        printout.close(); 
	        fileout.close();
		} catch(IOException e) {
		    System.out.println("Problemi di I/O");
		}
	}
	
	
	
	public static ArrayList calcolaPartialDsop(Pla[] pla, Pla[] pla2, int versioneH) {
		ArrayList dsopMulti = new ArrayList();
		for(int k = 0; k < numOutput; k++) {
			Pla dsop =  pla[k].scegliCubi(versioneH, pla2[k]);
			for(int j = 0; j < dsop.getNumCubi(); j++) {
				String riga = "";
				riga = dsop.get(j).prodottoToString()+ " ";
				for (int l = 0; l < numOutput; l++) {
					if (l == k) riga += '1';
					else riga += '0';
				}
				dsopMulti.add(riga);
			}
		}
		ripulisci(dsopMulti);
		return dsopMulti;
	}
	
	
	public static ArrayList nuovaPla(Pla[] pla) {
		ArrayList plaMulti = new ArrayList();
		for(int k = 0; k < numOutput; k++) {
			Pla sop =  pla[k];
			for(int j = 0; j < sop.getNumCubi(); j++) {
				String riga = "";
				riga = sop.get(j).prodottoToString()+ " ";
				for (int l = 0; l < numOutput; l++) {
					if (l == k) riga += '1';
					else riga += '0';
				}
				plaMulti.add(riga);
			}
		}
		ripulisci(plaMulti);
		return plaMulti;
	}
	
	public static ArrayList getDC(Pla[] pla) {
		ArrayList dcMulti = new ArrayList();
		for(int k = 0; k < numOutput; k++) {
			ArrayList dc =  pla[k].dontcare();
			for(int j = 0; j < dc.size(); j++) {
				String riga = "";
				riga = ((Cubo) dc.get(j)).prodottoToString()+ " ";
				for (int l = 0; l < numOutput; l++) {
					if (l == k) riga += '-';
					else riga += '0';
				}
				dcMulti.add(riga);
			}
		}
		ripulisci(dcMulti);
		return dcMulti;
	}
	
	
	
	public static void ripulisci(ArrayList lista) {
		Object[] righe = lista.toArray();
		java.util.Arrays.sort(righe, 0, righe.length);
		for (int i = 0; i < righe.length; i++) 
			lista.set(i, righe[i]);
		int i = 0;
		int j = i;
		while(i < lista.size()) {
			String riga = (String)(lista.get(i));
			String prodotto = riga.substring(0, numInput);
			j = i+1;
			while(j < lista.size()) {
				String riga2 = (String)(lista.get(j));
				if(!riga2.substring(0, numInput).equalsIgnoreCase(prodotto)) j = lista.size();
				else {
					String output = riga2.substring(numInput+1);
					int l = 0;
					char[] nuovaRiga = riga.toCharArray();
					for(int h = 0; h < numOutput; h++) {
						if(output.charAt(h) == '1')  {
							nuovaRiga[numInput+1+h] = '1';
						}
						if(output.charAt(h) == '-')  {
							nuovaRiga[numInput+1+h] = '-';
						}
					}
					String p = new String(nuovaRiga);
					lista.set(i, new String(nuovaRiga));
					riga = (String)(lista.get(i));
					lista.remove(j);
				}
			}	
			i++;	
		}
	}
	
	
	
	public static void main(String[] args) {
		
		int rimasti = 0;
		ArrayList output = new ArrayList();
		int ciclo = 0;
		String nomeFile;
		String benchmark = args[0].substring(0,args[0].length() - 4); // input: file.pla
		int versioneSort = Integer.parseInt(args[1]);
		int versioneHeur = Integer.parseInt(args[2]);
		String risultato = benchmark + " ";
		Pla[] pla2 = init(benchmark, versioneSort, versioneHeur);
		do {
			if (ciclo == 0) 
				nomeFile = benchmark+".sop"; 
			else nomeFile = benchmark+"."+ciclo+".sop"; 
			Pla[] pla = lettura(nomeFile, versioneSort, versioneHeur);
			initTime(); 
			for (int j = 0; j < pla.length; j++) {
				pla[j].annullaMarcatura();
				pla[j].calcolaPesi();
				pla[j].ordina();		
			}
			ArrayList dsop = calcolaPartialDsop(pla, pla2, versioneHeur);
			
			double sec = askTime();
			tempo += sec;
			
			output.addAll(dsop);	
			
			ArrayList sop = nuovaPla(pla);	
			ArrayList dontcare = getDC(pla);
			ciclo++;
			String nomeFileOutput = benchmark+"."+ciclo;
			scrivi(sop, dontcare, nomeFileOutput);
			rimasti = sop.size();
			String s = null;
	        try {
	        	String outEspresso = nomeFileOutput+".sop";
	        	FileWriter fileout = new FileWriter(outEspresso);
	    	    BufferedWriter filebuf = new BufferedWriter(fileout);
	    	    PrintWriter printout = new PrintWriter(filebuf);
	    	    // call to espresso
	        	// String esegui = "../../paperi/scontrini/esperimentiDSOP/sis-1.3.6/espresso/./espresso -t "+nomeFileOutput; 
	    	    String esegui = "espresso.exe -t "+nomeFileOutput;
	        	
	    	    Process p = Runtime.getRuntime().exec(esegui);
	            BufferedReader stdInput = new BufferedReader(new 
	                 InputStreamReader(p.getInputStream()));
	            BufferedReader stdError = new BufferedReader(new 
	                 InputStreamReader(p.getErrorStream()));
	        	
	            
	            while ((s = stdInput.readLine()) != null) {
	              printout.println(s);
	              if(s.startsWith("# ESPRESSO")) {
	            		String tempoE = s.substring(20);
	            		if(tempoE.charAt(1) == '.') tempoE = tempoE.substring(0,4);
	            		else tempoE = tempoE.substring(0,5);
	            		tempo += Double.parseDouble(tempoE);
	            	}
	            }
	            printout.close(); 
	    		fileout.close();
	        }
	        catch (IOException e) {
	            System.out.println("exception happened - here's what I know: ");
	            e.printStackTrace();
	            System.exit(-1);
	        }
		}
		while(rimasti != 0);
		ripulisci(output);
		String nomeOutput = benchmark;
		if(versioneSort == 0) nomeOutput = benchmark+".wd"+versioneHeur+".dsop";
		else nomeOutput = benchmark+".dw"+versioneHeur+".dsop";
		scrivi(output, nomeOutput);
		int num = ciclo-1;
		String time =tempo + "s"; 
		risultato = nomeOutput + " " + output.size() + " ("+num+")" + " " + tempo + "sec.";
		System.out.println(risultato);
	}
}


	
	
	