package org.pfaa.chemica.model;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import com.google.common.collect.Sets;

public interface Mixture extends IndustrialMaterial {
	List<MixtureComponent> getComponents();
	Mixture removeAll();
	
	default MixtureComponent getComponent(IndustrialMaterial material) {
		for (MixtureComponent comp : this.getComponents()) {
			if (comp.material.equals(material)) {
				return comp;
			}
		}
		return null;
	}
	
	default Mixture without(IndustrialMaterial... materials) {
		Set<IndustrialMaterial> toRemove = Sets.newHashSet(materials);
		List<MixtureComponent> comps = this.getComponents().stream().
				filter((x) -> !toRemove.contains(x)).
				collect(Collectors.toList());
		return this.removeAll().mixAll(comps);
	}
	
	default Mixture concentrate(double factor) {
		List<MixtureComponent> comps = this.getComponents();
		return this.removeAll().mixAll(comps.stream().map((comp) -> {
			if (comp.weight < 1.0)
				return comp.concentrate(factor);
			return comp;
		}).collect(Collectors.toList()));
	}
	
	default Mixture extract(IndustrialMaterial... materials) {
		List<MixtureComponent> comps = Arrays.stream(materials).
				map(this::getComponent).
				filter(Objects::nonNull).
				collect(Collectors.toList());
		return this.removeAll().mixAll(comps);
	}
	
	default List<Mixture> separate() {
		return this.getComponents().stream().map(SimpleMixture::new).collect(Collectors.toList());
	}
	
	class Phases {
		public final Mixture solid, liquid, gas;

		public Phases(Mixture solid, Mixture liquid, Mixture gas) {
			this.solid = solid;
			this.liquid = liquid;
			this.gas = gas;
		}
	}
	
	default Phases separateByState(Condition condition) {
		Mixture gas = this.removeAll(), liquid = this.removeAll(), solid = this.removeAll();
		for (MixtureComponent comp : this.getComponents()) {
			State state = comp.material.getProperties(condition).state;
			if (state == State.GAS) {
				gas = gas.mix(comp);
			} else if (state == State.AQUEOUS || state == State.LIQUID) {
				liquid = liquid.mix(comp);
			} else {
				solid = solid.mix(comp);
			}
		}
		return new Phases(solid, liquid, gas);
	}
	
	default double getTotalWeight() {
		double weight = 0;
		for (MixtureComponent comp : this.getComponents()) {
			weight += comp.weight;
		}
		return weight;
	}

	default Mixture normalize() {
		return this.concentrate(1/this.getTotalWeight());
	}
	
	default IndustrialMaterial simplify() {
		if (this.getComponents().size() == 1)
			return this.getComponents().get(0).material;
		return this;
	}
	
	default Mixture mixAll(Mixture other) {
		return this.mixAll(other.getComponents());
	}

	default Mixture mixAll(List<MixtureComponent> comps) {
		Mixture mixture = this;
		for (MixtureComponent comp : comps) {
			mixture = mixture.mix(comp);
		}
		return mixture;
	}
	
	default Mixture getFraction(Condition cond, State state) {
		return this.removeAll().mixAll(this.getComponents().stream().
			filter((comp) -> comp.getMaterial().getProperties(cond).state == state).
			collect(Collectors.toList()));
	}
}

@@ -555,16 +555,21 @@
 without(
+List%3C
 Industri
@@ -570,35 +570,33 @@
 dustrialMaterial
-...
+%3E
  materials) %7B%0A%09%09
@@ -836,32 +836,146 @@
 ll(comps);%0A%09%7D%0A%09%0A
+%09default Mixture without(IndustrialMaterial... materials) %7B%0A%09%09return this.without(Arrays.asList(materials));%0A%09%7D%0A%09%0A
 %09default Mixture
@@ -1268,16 +1268,21 @@
 extract(
+List%3C
 Industri
@@ -1283,35 +1283,33 @@
 dustrialMaterial
-...
+%3E
  materials) %7B%0A%09%09
@@ -1339,21 +1339,24 @@
 comps = 
-Array
+material
 s.stream
@@ -1356,25 +1356,16 @@
 .stream(
-materials
 ).%0A%09%09%09%09m
@@ -1490,32 +1490,146 @@
 ll(comps);%0A%09%7D%0A%09%0A
+%09default Mixture extract(IndustrialMaterial... materials) %7B%0A%09%09return this.extract(Arrays.asList(materials));%0A%09%7D%0A%09%0A
 %09default List%3CMi
@@ -3186,39 +3186,39 @@
 %7B%0A%09%09
-return this.removeAll().mixAll(
+List%3CMixtureComponent%3E comps = 
 this
@@ -3244,16 +3244,17 @@
 ream().%0A
+%09
 %09%09%09filte
@@ -3324,16 +3324,17 @@
 te).%0A%09%09%09
+%09
 collect(
@@ -3349,20 +3349,60 @@
 rs.toList())
+;%0A%09%09return this.removeAll().mixAll(comps
 );%0A%09%7D%0A%7D%0A

add some List-based methods