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


public class Pla {
		
	ArrayList cubi;
	int versioneH = 1; // 1: DSOP1, 2: DSOP2, 3: DSOP3.
	
	public Pla(int versioneH) {
		this.versioneH = versioneH;
		cubi = 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 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 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);
				else if (versioneH == 2) c = this.seleziona2(i);
				else c = this.seleziona3(i);
				if(c != null) dsop.aggiungi(c);
			}
			else continua = false;
		}		
		return dsop;
	}
	
	
	public Cubo seleziona1(int i) {// elimina tutti i frammenti
		Cubo a = (Cubo) cubi.remove(i);
		if(a.getPeso() == -1) return a;
		Pla frammenti = new Pla(versioneH);
		int j = 0;
		while(j < cubi.size()) {
			Cubo c = this.get(j);
			Cubo inter = c.intersezione(a);
			if(c.equalTo(inter)) this.elimina(j);
			else if(inter != null) {
				Cubo[] pezzi = c.disjointSharp(a);
				this.elimina(j);
				for(int k = 0; k < pezzi.length; k++)
					frammenti.aggiungi(pezzi[k]);
			}
			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) {// aggiorna i pesi, con -1
		Cubo a = (Cubo) cubi.remove(i);
		if(a.getPeso() == -1) return a;
		Pla frammenti = new Pla(versioneH);
		int j = 0;
		while(j < cubi.size()) {
			Cubo c = this.get(j);
			Cubo inter = c.intersezione(a);
			if(c.equalTo(inter)) this.elimina(j);
			else if(inter != null) {
				Cubo[] pezzi = c.disjointSharp(a);
				this.elimina(j);
				c.aggiornaPeso(this);
				for(int k = 0; k < pezzi.length; k++)
					frammenti.aggiungi(pezzi[k]);
			}
			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) {// lavora per "componenti connesse"
		Cubo a = (Cubo) cubi.remove(i);
		if(a.getPeso() == -1) return a;
		Pla frammenti = 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(c.equalTo(inter)) this.elimina(j);
			else if(inter != null) {
				Cubo[] pezzi = c.disjointSharp(a);
				this.elimina(j);
				for(int k = 0; k < pezzi.length; k++)
					frammenti.aggiungi(pezzi[k]);
			}		
			else j++;
		}
		frammenti.controllaIncl();
		while(!frammenti.isEmpty()) {
			Cubo nuovo = frammenti.elimina(0);
			if (!nuovo.incluso(this))
				this.aggiungi(nuovo);
		}
		return a; 
	}
	
	
	
}


