import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;

public class Osa {

    private String nimi;
    private Osa kokonaisuus;
    private Set aliosat;
    private double paino;

    public Osa(String nimi) {
	this.nimi = nimi;
	aliosat = new HashSet();
	this.paino = 0.0;
    }

    public Osa(String nimi, double paino) {
	this(nimi);
	this.paino = paino;
    }

    public String nimi() {
	return nimi;
    }

    public Osa kokonaisuus() {
	return kokonaisuus;
    }

    public Set aliosat() {
	return aliosat;
    }

    public double paino() {
	return paino;
    }

    public double laskePaino() {
	double tulos = 0.0;
	Iterator it = aliosat.iterator();
	while (it.hasNext()) tulos += ((Osa)it.next()).paino();
	return tulos;
    }

    public void korjaaPaino() {
	paino = laskePaino();
	if (kokonaisuus != null) kokonaisuus.korjaaPaino();
    }
    
    public void lisääAliosa(Osa uusiAliosa) {
	Osa uudenKokonaisuus = uusiAliosa.kokonaisuus();
	if (!aliosat.contains(uusiAliosa)) {
	    if (uusiAliosa.kokonaisuus() != null) {
		uusiAliosa.kokonaisuus().poistaAliosa(uusiAliosa);
	    }
	    aliosat.add(uusiAliosa);
	    uusiAliosa.asetaKokonaisuus(this);
	    korjaaPaino();
	}
    }

    public void asetaKokonaisuus(Osa uusiKokonaisuus) {
	if (kokonaisuus != uusiKokonaisuus) {
	    if (kokonaisuus != null) {
		kokonaisuus.poistaAliosa(this);
	    }
	    kokonaisuus = uusiKokonaisuus;
	    if (uusiKokonaisuus != null) {
		uusiKokonaisuus.lisääAliosa(this);
	    }
	}
    }

    public void poistaAliosa(Osa poistettavaAliosa) {
	if (aliosat.contains(poistettavaAliosa)) {
	    aliosat.remove(poistettavaAliosa);
	    poistettavaAliosa.asetaKokonaisuus(null);
	    korjaaPaino();
	}
    }

    public boolean sisältyykö(Osa osa) {
	if (this == osa) return true;
	Iterator it = aliosat.iterator();
	while (it.hasNext()) {
	    if (((Osa)it.next()).sisältyykö(osa)) return true;
	}
	return false;
    }

    public Set kaikkiAliosat() {
	Set tulos = new HashSet();
	tulos.add(this);
	Iterator it = aliosat.iterator();
	while (it.hasNext()) {
	    tulos.addAll(((Osa)it.next()).kaikkiAliosat());
	}
	return tulos;
    }

    public Set kaikkiYksinkertaisetAliosat() {
	Set tulos = new HashSet();
	if (aliosat.isEmpty()) tulos.add(this);
	Iterator it = aliosat.iterator();
	while (it.hasNext()) {
	    tulos.addAll(((Osa)it.next()).kaikkiYksinkertaisetAliosat());
	}
	return tulos;
    }

    public static String osienNimet(Set osaJoukko) {
	String tulos = "[";
	boolean in = false;
	Iterator it = osaJoukko.iterator();
	while (it.hasNext()) {
	    if (in) tulos += ", ";
	    else in = true;
	    tulos += ((Osa)it.next()).nimi();
	}
	return tulos + "]";
    }

    public String toString() {
	String tulos = "Osa " + nimi() + ", paino " + paino();
	if (kokonaisuus() != null) tulos += ", kokonaisuus " + kokonaisuus().nimi();
	if (!aliosat.isEmpty())    tulos += ", aliosat " + osienNimet(aliosat);
	return tulos;
    }

    private static void test1() {
	Osa osa1 = new Osa("Osa1");
	Osa osa11 = new Osa("Osa11");
	Osa osa12 = new Osa("Osa12");
	Osa osa121 = new Osa("Osa121", 200.0);
	Osa osa122 = new Osa("Osa122", 150.0);
	Osa osa123 = new Osa("Osa123", 100.0);
	Osa osa111 = new Osa("Osa111", 40.0);
	Osa osa112 = new Osa("Osa112", 500.0);
	Osa osa113 = new Osa("Osa113", 120.0);
	osa1.lisääAliosa(osa12);
	osa1.lisääAliosa(osa11);
	osa12.lisääAliosa(osa122);
	osa12.lisääAliosa(osa121);
	osa12.lisääAliosa(osa123);
	osa11.lisääAliosa(osa111);
	osa11.lisääAliosa(osa112);
	osa11.lisääAliosa(osa113);
	System.out.println(osa1);
	System.out.println(osa12);
	System.out.println(osa11);
	System.out.println(osa122);
	System.out.println(osa111);
	System.out.println(osa112);
	System.out.println("Osa1:n kaikki osat: " + 
			   osienNimet(osa1.kaikkiAliosat()));
	System.out.println("Osa1:n kaikki yksinkertaiset osat: " + 
			   osienNimet(osa1.kaikkiYksinkertaisetAliosat()));
	System.out.println("Sisältyykö Osa122 Osa1:een? " + 
			   osa1.sisältyykö(osa122));
	System.out.println("Sisältyykö Osa111 Osa12:een? " + 
			   osa12.sisältyykö(osa111));
	System.out.println("Poistetaan osa122, paino " + osa122.paino());
	osa12.poistaAliosa(osa122);
	System.out.println(osa122);
	System.out.println(osa12);
	System.out.println(osa1);
    }

    public static void main(String[] a) {
	test1();
    }
}
