package ch.sahits.game.openpatrician.model.ship.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import ch.sahits.game.openpatrician.model.product.AmountablePrice;
import ch.sahits.game.openpatrician.model.product.IWare;
import ch.sahits.game.openpatrician.model.ship.EShipSide;
import ch.sahits.game.openpatrician.model.ship.EShipUpgrade;
import ch.sahits.game.openpatrician.model.ship.IShip;
import ch.sahits.game.openpatrician.model.weapon.IWeapon;
abstract class Ship implements IShip{
/** State of the design standard level */
protected EShipUpgrade upgradeLevel;
/** Name of the ship */
protected String name;
/** Store the wares loaded on the ship together with their amount. The amount is ware specific*/
private Map<IWare,AmountablePrice> loadedWare = new HashMap<IWare, AmountablePrice>();
/** List of the weapons on board together with their location */
private final ArrayList<WeaponsLocation> weapons = new ArrayList<WeaponsLocation>();
/**
* Fitness of the ship. This is reduced as it is damaged
*/
protected int fitness = 100;
/** Initial value of the ship */
private final int initialValue;
public Ship(int value) {
super();
initialValue=value;
}
@Override
public boolean isUpgradable() {
return upgradeLevel!=EShipUpgrade.LEVEL2;
}
@Override
public void upgrade() {
EShipUpgrade[] levels = EShipUpgrade.values();
for (int i = 0; i < levels.length; i++) {
if (levels[i]==upgradeLevel){
upgradeLevel = levels[i+1];
break; // only one update allowed
}
}
}
@Override
public String getName() {
return name;
}
@Override
public Set<IWare> getLoadedWares() {
return loadedWare.keySet();
}
/**
* {@inheritDoc}
* This method is not thread safe as it is only intended to be accessed by one thread at a time
*/
@Override
public int load(IWare ware, int amount, int avgPrice) {
final short sizeInBarrels = ware.getSizeAsBarrels();
amount = Math.abs(amount);
int cap = ensureCapacity(amount*sizeInBarrels);
// Make sure that for wares in other sizes than barrels we only load
// complete loads.
if (cap!=amount && sizeInBarrels!=1){
cap = (cap/sizeInBarrels)*sizeInBarrels;
}
AmountablePrice available = getWare((IWare) ware);
available.add(cap/ware.getSizeAsBarrels(), avgPrice);
// }
return cap/sizeInBarrels;
}
/**
* Check if the amount can be loaded
* @param amount
* @return
*/
private int ensureCapacity(int amount) {
return Math.min(amount, getCapacity());
}
/**
* {@inheritDoc}
* This method is not thread safe as it is only intended to be accessed by one thread at a time
*/
@Override
public int unload(IWare ware, int amount) {
if (!loadedWare.containsKey(ware)){
return 0; // nothing to unload
}
amount = Math.abs(amount);
// convert to barrels
amount = amount*ware.getSizeAsBarrels();
final int loaded = loadedWare.get(ware).getAmount()*ware.getSizeAsBarrels();
int unloaded = Math.min(loaded, amount);
if (unloaded==loaded){ // unloaded completely
loadedWare.remove(ware);
} else {
AmountablePrice available = loadedWare.get(ware);
available.remove(unloaded/ware.getSizeAsBarrels());
}
return unloaded/ware.getSizeAsBarrels();
}
@Override
public void setName(String name) {
this.name=name;
}
@Override
public int getLoad() {
int sum=0;
for (Entry<IWare,AmountablePrice> entry : loadedWare.entrySet()) {
int amount = entry.getValue().getAmount();
int barrelSize = entry.getKey().getSizeAsBarrels();
sum += amount*barrelSize;
}
return sum;
}
/**
* Clear all loaded wares. This method is only intended for testing
*/
protected void clearLoadedWares() {
loadedWare.clear();
}
@Override
public AmountablePrice getWare(IWare ware) {
if (!loadedWare.containsKey(ware)){
loadedWare.put(ware, new AmountablePrice());
}
return loadedWare.get(ware);
}
/**
* Retrieve the side of the next free slot where the weapon can be placed.
* check first port and then starboard. There is no free solt on either side,
* null will be returned.
* @param weapon to be placed
* @return {@link EShipSide#PORT}, {@link EShipSide#STARBOARD} or null
*/
@SuppressWarnings("unused")
private EShipSide getNextFreeSide(IWeapon weapon){
// TODO implement
return null;
}
/**
* Retrieve the next free slot for the weapon on the side. The slots are checked
* from stern to bow. If there is no free slot a negative number will be returned
* @param weapon to be placed
* @param side to be checked {@link EShipSide#PORT} or {@link EShipSide#STARBOARD}
* @return slot index or negative number
*/
@SuppressWarnings("unused")
private int getNextFreeSlot(IWeapon weapon, EShipSide side){
// TODO implement
return -1;
}
@Override
public boolean hasWeapons() {
return !weapons.isEmpty();
}
@Override
public int getValue() {
return (int)Math.rint(initialValue*fitness/100.0);
}
}