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


public class Pla {
		
	ArrayList cubi;
	ArrayList dc;
	int versioneH = 1; // 1: DSOP1, 2: DSOP2, 3: DSOP3.
	
	
	public Pla(int versioneH) {
		this.versioneH = versioneH;
		cubi = new ArrayList();
		dc = new ArrayList();
	}
	
	public Pla(String[] pla, int versioneS, int versioneH) {
		this(versioneH);
		int n = pla[0].length();
		char[] prodotto = new char[n];
		for(int i = 0; i < pla.length; i++) {	
			String cubo = pla[i];
			for(int j = 0; j < n; j++) 
					prodotto[j] = cubo.charAt(j);
			cubi.add(i, new Cubo(prodotto, versioneS));
		}
	}

	public Pla(String nomeFile, int versioneS, int versioneH) {
		
		this(versioneH);
		String nextLine;

		try {
		    BufferedReader in = new BufferedReader(new FileReader(nomeFile));
		    int n = 0;
		    char[] prodotto;
		    nextLine = in.readLine();
		    while (nextLine != null) { 
		    	if(nextLine.charAt(1) == 'i') {
		    		String var = nextLine.substring(3);
		    		n = 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);
		    			cubi.add(new Cubo(prodotto, versioneS));
		    		}		 
		    	nextLine = in.readLine();
		    }  
		    in.close();
		} catch(IOException e) {
		    System.out.println("Problemi di I/O");
		}
	}
	
	
	public void annullaMarcatura() {
		for (int i = 0; i < cubi.size(); i++) {
			this.get(i).setMarca(false);
		}
	}
	
	public void setMarcatura() {
		for (int i = 0; i < cubi.size(); i++) {
			this.get(i).setMarca(true);
		}
	}
	
	
	public int getNumCubi() {
		return cubi.size();
	}
	
	public int selezionaCubo() {
		for (int i = 0; i < cubi.size(); i++) {
			if (!this.get(i).getMarca()) return i;
		}
		return -1;
	}
	
	public int selezionaCuboStessoPeso() {
		boolean trovato = false;
		int i = 0;
		while (i < cubi.size() && !trovato) {
			if (!this.get(i).getMarca()) trovato = true;
			else i++;
		}
		if (trovato) {
			for (int j = 0; j < cubi.size(); j++) {
				if (this.get(j).getPeso() != this.get(i).getPeso()) this.get(j).setMarca(true);
				}
			return i;
		}
		else return -1;
	}
	
	public void print() {
		System.out.println();
		for (int i = 0; i < cubi.size(); i++) {
			Cubo c = (Cubo) cubi.get(i);
			System.out.println(c);
		}
		System.out.println();
	}
	
	public void calcolaPesi() {
		for (int i = 0; i < cubi.size(); i++) {
			Cubo c = (Cubo) cubi.get(i);
			if (!c.getMarca()) c.peso(this);
		}
	}
	
	
	public void calcolaTuttiPesi() {
		for (int i = 0; i < cubi.size(); i++) {
			Cubo c = (Cubo) cubi.get(i);
			c.peso(this);
		}
	}
	
	public void ordina() {
		Object[] pla = cubi.toArray();
		java.util.Arrays.sort(pla, 0, pla.length);
		for (int i = 0; i < pla.length; i++) {
			cubi.set(i, (Cubo) pla[i]);
		}
	}
		
	
	public void aggiungi(Cubo c) {
		cubi.add(c);
	}
	
	
	public Cubo get(int i) {
		Cubo c = (Cubo) cubi.get(i);
		return c;
	}
	
	
	public Cubo elimina(int i) {
		Cubo c = (Cubo) cubi.remove(i);
		return c;
	}
	
	
	public boolean isEmpty() {
		return this.getNumCubi() == 0;
	}
	
	
	
	public Pla selezionaDisgiunti() {
		Pla dsop =  new Pla(versioneH);
		int i = 0;
		while(!this.isEmpty() && this.get(i).getPeso() == -1) {
			dsop.aggiungi(this.get(i));
			this.elimina(i);
		}
		return dsop;
	}
	

	public Pla calcolaIntersezione(Cubo pi) {
		Pla intPla = new Pla(versioneH);
		for (int i = 0; i < this.cubi.size(); i++) {
			Cubo c = (Cubo) this.cubi.get(i);
			Cubo inter = c.intersezione(pi);
			if (inter != null) {
				//dc.add(inter);
				intPla.aggiungi(inter);
			}
		}
		return intPla;
	}

	
	public boolean verificaInclusione(Cubo pi) {
		Pla cofactor = new Pla(versioneH);
		for (int k = 0; k < cubi.size(); k++) {
			Cubo c = (Cubo) cubi.get(k);
			char[] prPi = pi.getProdotto();
			char[] prC = c.getProdotto();
			char[] pr = new char[pi.dim()];
			int j = 0;
			boolean aggiungi = true;
			for(int i = 0; i < prPi.length; i++) {
				if (prPi[i] == '-') {
					pr[j] = prC[i]; 
					j++;
				}
				else if (prPi[i] == '1' && prC[i] == '0') {aggiungi = false; break;}
				else if (prPi[i] == '0' && prC[i] == '1') {aggiungi = false; break;}
				else continue;
			}
			if (aggiungi) {
				Cubo nuovo = new Cubo(pr, pi.getVersione());
				cofactor.aggiungi(nuovo);
			}
		}
		String nextLine;
		try {
			FileWriter fileout = new FileWriter("cofactor");
	        BufferedWriter filebuf = new BufferedWriter(fileout);
	        PrintWriter printout = new PrintWriter(filebuf);
	        printout.println(".i "+pi.dim());
	        printout.println(".o 1");
	        for(int i = 0; i < cofactor.getNumCubi(); i++)
	        	printout.println((cofactor.get(i)).prodottoToString() + " 1");
	        printout.close(); 
		fileout.close();
		} catch(IOException e) {
		    System.out.println("Problemi di I/O");
		}
		boolean incluso = false;
		String s = null;
        try {
    	    // call to espresso
        	//String esegui = "../../paperi/scontrini/esperimentiDSOP/sis-1.3.6/espresso/./espresso -Dexact -t cofactor"; 
        	String esegui = "espresso.exe -Dexact -t cofactor"; 
        	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) {  
	              if(s.startsWith("# exact")) {
	            		String tempoE = s.substring(17);
	            		if(tempoE.charAt(1) == '.') tempoE = tempoE.substring(0,4);
	            		else tempoE = tempoE.substring(0,5);
	            		//System.out.println(tempoE);
	            		PartialHeur.tempo += Double.parseDouble(tempoE);
	              }
	              if (s.startsWith(".p 1")) {
	            	  s = stdInput.readLine();
	            	  String taut = "";
	            	  for (int j = 0; j < pi.dim(); j++) taut +="-";
	            	  if (s.substring(0,pi.dim()).equals(taut)) incluso = true;
	              }
            }
            
        }
        catch (IOException e) {
            System.out.println("exception happened - here's what I know: ");
            e.printStackTrace();
            System.exit(-1);
        }
		return incluso;
	}

	
	public void controllaIncl() {
		int i = 0; 
		while(i < this.getNumCubi()) {
			Cubo a = this.elimina(i);
			if(!a.incluso(this)) {
				this.cubi.add(i,a);  
				i++;
			}
		}
	}
	
	
	public Pla scegliCubi(int versioneH, Pla pla2) {
		Pla dsop = new Pla(versioneH);
		boolean continua = true;
		Cubo c;
		while (continua) {
			int i = this.selezionaCubo(); 
			if (i != -1) {
				if (versioneH == 1) c = this.seleziona1(i, pla2);
				else if (versioneH == 2) c = this.seleziona2(i, pla2);
				else c = this.seleziona3(i, pla2);
				if (c != null) dsop.aggiungi(c);
			}
			else continua = false;
		}		
		return dsop;
	}
	
	public boolean check(int versioneH, Pla pla2) {
		boolean ok = true;
		for (int i = 0; ok && i < cubi.size(); i++) {
			Cubo c = (Cubo) cubi.get(i);
			ok = c.controlloInter(this, pla2);
		}
		return ok;
	}
	
		
	
	
	public ArrayList dontcare() {
		return this.dc;
	}
	
	public Cubo seleziona1(int i, Pla pla2) {// elimina tutti i frammenti
		Cubo a = (Cubo) cubi.remove(i);
		if (a.getPeso() == -1) return a;
		Pla frammenti = new Pla(versioneH);
		Pla intPla = new Pla(versioneH);
		int j = 0;
		while (j < cubi.size()) {
			Cubo c = this.get(j);
			Cubo inter = c.intersezione(a);
		 if (inter != null) intPla = pla2.calcolaIntersezione(inter);
			if (c.equalTo(inter)) this.elimina(j);
			else if (inter != null && !intPla.verificaInclusione(inter)) {
			//else if (inter != null && !pla2.verificaInclusione(inter)) {
				Cubo[] pezzi = c.disjointSharp(a);
				this.elimina(j);
				for (int k = 0; k < pezzi.length; k++)
					frammenti.aggiungi(pezzi[k]);
				for (int l = 0; l < intPla.cubi.size(); l++) {
					Cubo d = (Cubo) intPla.get(l);
					dc.add(d);
				}
			}
			else j++;
		}
		frammenti.controllaIncl();
		while (!frammenti.isEmpty()) {
			Cubo nuovo = frammenti.elimina(0);
			if (!nuovo.incluso(this))
				this.aggiungi(nuovo);
		}
		return a; 
	}
	
	
	public Cubo seleziona2(int i, Pla pla2) {// aggiorna i pesi, con -1
		Cubo a = (Cubo) cubi.remove(i);
		if(a.getPeso() == -1) return a;
		Pla frammenti = new Pla(versioneH);
		Pla intPla = new Pla(versioneH);
		int j = 0;
		while(j < cubi.size()) {
			Cubo c = this.get(j);
			Cubo inter = c.intersezione(a);
			if (inter != null) intPla = pla2.calcolaIntersezione(inter);
			if(c.equalTo(inter)) this.elimina(j);
			else if(inter != null && !intPla.verificaInclusione(inter)) {
			//else if(inter != null && !pla2.verificaInclusione(inter)) {
				Cubo[] pezzi = c.disjointSharp(a);
				this.elimina(j);
				c.aggiornaPeso(this);
				for(int k = 0; k < pezzi.length; k++)
					frammenti.aggiungi(pezzi[k]);
				for (int l = 0; l < intPla.cubi.size(); l++) {
					Cubo d = (Cubo) intPla.get(l);
					dc.add(d);
				}
			}
			else j++;
		}
		frammenti.controllaIncl();
		while(!frammenti.isEmpty()) {
			Cubo nuovo = frammenti.elimina(0);
			if (!nuovo.incluso(this))
				this.aggiungi(nuovo);
		}
		this.ordina();
		return a; 
	}
	

	
	public Cubo seleziona3(int i, Pla pla2) {// lavora per "componenti connesse"
		Cubo a = (Cubo) cubi.remove(i);
		if(a.getPeso() == -1) return a;
		Pla frammenti = new Pla(versioneH);
		Pla intPla = new Pla(versioneH);
		int j = 0;
		while(j < cubi.size()) {
			Cubo c = this.get(j);
			Cubo inter = c.intersezione(a);
			if(inter != null && c.getMarca()) {
				a.setMarca(true);
				this.aggiungi(a);
				return null;
			}
			else j++;
		}
		j = 0;
		while(j < cubi.size()) {
			Cubo c = this.get(j);
			Cubo inter = c.intersezione(a);
			if (inter != null) intPla = pla2.calcolaIntersezione(inter);
			if(c.equalTo(inter)) this.elimina(j);
			else if(inter != null && !intPla.verificaInclusione(inter)) {
			//else if(inter != null && !pla2.verificaInclusione(inter)) {	
				Cubo[] pezzi = c.disjointSharp(a);
				this.elimina(j);
				for(int k = 0; k < pezzi.length; k++)
					frammenti.aggiungi(pezzi[k]);
				for (int l = 0; l < intPla.cubi.size(); l++) {
					Cubo d = (Cubo) intPla.get(l);
					dc.add(d);
				}
			}		
			else j++;
		}
		frammenti.controllaIncl();
		while(!frammenti.isEmpty()) {
			Cubo nuovo = frammenti.elimina(0);
			if (!nuovo.incluso(this))
				this.aggiungi(nuovo);
		}
		return a; 
	}
	
	
	
}


