package games.strategy.engine.data;
import games.strategy.triplea.Constants;
import games.strategy.util.IntegerMap;
public class ResourceCollection extends GameDataComponent {
private static final long serialVersionUID = -1247795977888113757L;
private final IntegerMap<Resource> m_resources = new IntegerMap<>();
/**
* Creates new ResourceCollection.
*
* @param data
* game data
*/
public ResourceCollection(final GameData data) {
super(data);
}
public ResourceCollection(final ResourceCollection other) {
super(other.getData());
m_resources.add(other.m_resources);
}
public ResourceCollection(final ResourceCollection[] others, final GameData data) {
super(data);
for (final ResourceCollection other : others) {
m_resources.add(other.m_resources);
}
}
public ResourceCollection(final GameData data, final IntegerMap<Resource> resources) {
this(data);
m_resources.add(resources);
}
public void addResource(final Resource resource, final int quantity) {
if (quantity < 0) {
throw new IllegalArgumentException("quantity must be positive");
}
change(resource, quantity);
}
public void add(final ResourceCollection otherResources) {
m_resources.add(otherResources.m_resources);
}
/**
* You cannot remove more than the collection contains.
*
* @param resource
* referring resource
* @param quantity
* quantity of the resource that should be removed
*/
public void removeResource(final Resource resource, final int quantity) {
if (quantity < 0) {
throw new IllegalArgumentException("quantity must be positive");
}
final int current = getQuantity(resource);
if ((current - quantity) < 0) {
throw new IllegalArgumentException("Cant remove more than player has of resource: " + resource.getName()
+ ". current:" + current + " toRemove: " + quantity);
}
change(resource, -quantity);
}
public void removeAllOfResource(final Resource resource) {
m_resources.removeKey(resource);
}
private void change(final Resource resource, final int quantity) {
m_resources.add(resource, quantity);
}
/**
* Overwrites any current resource with the same name.
*/
public void putResource(final Resource resource, final int quantity) {
if (quantity < 0) {
throw new IllegalArgumentException("quantity must be positive");
}
m_resources.put(resource, quantity);
}
public int getQuantity(final Resource resource) {
return m_resources.getInt(resource);
}
public IntegerMap<Resource> getResourcesCopy() {
return new IntegerMap<>(m_resources);
}
public int getQuantity(final String name) {
getData().acquireReadLock();
try {
final Resource resource = getData().getResourceList().getResource(name);
if (resource == null) {
throw new IllegalArgumentException("No resource named:" + name);
}
return getQuantity(resource);
} finally {
getData().releaseReadLock();
}
}
public boolean has(final IntegerMap<Resource> map) {
return m_resources.greaterThanOrEqualTo(map);
}
/**
* @return new ResourceCollection containing the difference between both collections.
*/
public ResourceCollection difference(final ResourceCollection otherCollection) {
final ResourceCollection returnCollection = new ResourceCollection(getData(), m_resources);
returnCollection.subtract(otherCollection);
return returnCollection;
}
private void subtract(final ResourceCollection resourceCollection) {
subtract(resourceCollection.m_resources);
}
public void subtract(final IntegerMap<Resource> cost) {
for (final Resource resource : cost.keySet()) {
removeResource(resource, cost.getInt(resource));
}
}
public void subtract(final IntegerMap<Resource> cost, final int quantity) {
for (int i = 0; i < quantity; i++) {
subtract(cost);
}
}
public void add(final IntegerMap<Resource> resources) {
for (final Resource resource : resources.keySet()) {
addResource(resource, resources.getInt(resource));
}
}
public void add(final IntegerMap<Resource> resources, final int quantity) {
for (int i = 0; i < quantity; i++) {
add(resources);
}
}
/**
* Will apply a discount if giving a fractional double (ie: 0.5 = 50% discount). Will round up remainder.
*/
public void discount(final double discount) {
multiplyAllValuesBy(discount, 3);
}
/**
* Will multiply all values by a given double. Can be used to divide all numbers, if given a fractional double (ie: to
* divide by 2, use
* 0.5 as the double)
*
* @param roundType
* (1 = floor, 2 = round, 3 = ceil)
*/
public void multiplyAllValuesBy(final double multiplyBy, final int roundType) {
m_resources.multiplyAllValuesBy(multiplyBy, roundType);
}
/**
* @return will return 10000 if it can fit more times than 10000. will return Integer MaxValue if cost is zero.
*/
public int fitsHowOften(final IntegerMap<Resource> cost) {
if (cost.size() == 0 || (cost.totalValues() <= 0 && cost.isPositive())) {
return Integer.MAX_VALUE;
}
final ResourceCollection resources = new ResourceCollection(getData(), m_resources);
for (int i = 0; i <= 10000; i++) {
try {
resources.subtract(cost);
} catch (final IllegalArgumentException iae) {
// when the subtraction isn't possible it will throw an exception, which means we can return i;
return i;
}
}
// throw new IllegalArgumentException("Unlimited purchases shouldn't be possible");
// System.out.println("Can purchase more than 10,000 of unit - Unlimited purchases shouldn't be possible");
return 10000;
}
@Override
public String toString() {
return toString(m_resources, getData(), ", ");
}
public static String toString(final IntegerMap<Resource> resources, final GameData data, final String lineSeparator) {
if (resources == null || resources.isEmpty() || resources.allValuesEqual(0)) {
return "nothing";
}
final StringBuilder sb = new StringBuilder();
Resource pus = null;
data.acquireReadLock();
try {
pus = data.getResourceList().getResource(Constants.PUS);
} catch (final NullPointerException e) {
// we are getting null pointers here occasionally on deserializing gamesaves, because data.getResourceList() is
// still null at this
// point
for (final Resource r : resources.keySet()) {
if (r.getName().equals(Constants.PUS)) {
pus = r;
break;
}
}
} finally {
data.releaseReadLock();
}
if (pus == null) {
throw new IllegalStateException("Possible deserialization error: PUs is null");
}
if (resources.getInt(pus) != 0) {
sb.append(lineSeparator);
sb.append(resources.getInt(pus));
sb.append(" ");
sb.append(pus.getName());
}
for (final Resource resource : resources.keySet()) {
if (resource.equals(pus)) {
continue;
}
sb.append(lineSeparator);
sb.append(resources.getInt(resource));
sb.append(" ");
sb.append(resource.getName());
}
return sb.toString().replaceFirst(lineSeparator, "");
}
public String toStringForHTML() {
return toStringForHTML(m_resources, getData());
}
public static String toStringForHTML(final IntegerMap<Resource> resources, final GameData data) {
return toString(resources, data, "<br />");
}
/**
* @param times
* multiply this Collection times times.
*/
public void multiply(final int times) {
final IntegerMap<Resource> base = new IntegerMap<>(m_resources);
add(base, times - 1);
}
public boolean isEmpty() {
return m_resources.isEmpty();
}
}