package ch.sahits.game.openpatrician.model.city.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.ResourceBundle;
import ch.sahits.game.openpatrician.model.IPlayer;
import ch.sahits.game.openpatrician.model.building.IBuilding;
import ch.sahits.game.openpatrician.model.city.EKontorType;
import ch.sahits.game.openpatrician.model.city.EPopulationClass;
import ch.sahits.game.openpatrician.model.city.ICity;
import ch.sahits.game.openpatrician.model.city.impl.Contributions;
import ch.sahits.game.openpatrician.model.impl.WareHolding;
import ch.sahits.game.openpatrician.model.personal.ESocialRank;
import ch.sahits.game.openpatrician.model.personal.IReputation;
import ch.sahits.game.openpatrician.model.personal.impl.Reputation;
import ch.sahits.game.openpatrician.model.product.EWare;
import ch.sahits.game.openpatrician.model.product.IWare;
import ch.sahits.game.openpatrician.util.PropertyLoader;
import ch.sahits.game.openpatrician.util.l10n.Locale;
/**
* Implementation of the city model. The model of the city should only be instanciated once.
* A city is unique. therefore equality can be tested by identity.
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Jan 18, 2011
*
*/
abstract class City extends WareHolding implements ICity {
// TODO set the polulation structure based on the settings from the properties and the start year
// TODO initialize the city with wares
private final IWare[] effectiveProduction;
private final IWare[] ineffectiveProduction;
private final String name;
private final EKontorType kontorType;
private final Locale locale = Locale.getInstance();
// /** Store the amount of wares in the city in the ware specific sizes */
// final HashMap<IWare, AmountablePrice> wares = new HashMap<IWare, AmountablePrice>();
/** Store the buildings in the city */
private List<IBuilding> buildings = new ArrayList<IBuilding>();
/** Map holding the reputation of the different players */
private Map<IPlayer,IReputation> reputation = new HashMap<IPlayer, IReputation>();
/** Store the contibutions of the players */
private Map<IPlayer,Contributions> playersContributions = new HashMap<IPlayer, Contributions>();
/** Holding the population split by population classes */
private final HashMap<EPopulationClass, Integer> population = new HashMap<EPopulationClass, Integer>();
public City(String configFileName) throws IOException {
Properties props = PropertyLoader.loadProperties(configFileName);
if (props.getProperty("effectiveProduction")==null){
throw new IOException("The property file "+configFileName+" does not contain the effectiveProduction property");
}
if (props.getProperty("ineffectiveProduction")==null){
throw new IOException("The property file "+configFileName+" does not contain the ineffectiveProduction property");
}
if (props.getProperty("name")==null){
throw new IOException("The property file "+configFileName+" does not contain the name property");
}
if (props.getProperty("kontorType")==null){
throw new IOException("The property file "+configFileName+" does not contain the kontorType property");
}
if (props.getProperty("effectiveProduction").trim().length()>0){
String[] wareNames = props.getProperty("effectiveProduction").split(",");
this.effectiveProduction = new EWare[wareNames.length];
for (int i = 0; i < wareNames.length; i++) {
this.effectiveProduction[i]=EWare.valueOf(wareNames[i]);
}
} else {
this.effectiveProduction=null;
}
if (props.getProperty("ineffectiveProduction").trim().length()>0){
String[] wareNames = props.getProperty("ineffectiveProduction").split(",");
this.ineffectiveProduction = new EWare[wareNames.length];
for (int i = 0; i < wareNames.length; i++) {
this.ineffectiveProduction[i]=EWare.valueOf(wareNames[i]);
}
} else {
this.ineffectiveProduction=null;
}
String n = props.getProperty("name");
ResourceBundle messages = ResourceBundle.getBundle("ModelMessages", locale.getCurrentLocal());
this.name=messages.getString(n);
this.kontorType = EKontorType.valueOf(props.getProperty("kontorType"));
initPopulation(props);
initWares();
}
/**
* Init the amount of wares available in the city
* This method is protected so it can be overriden by subclasses for testing
*/
protected void initWares() {
// TODO initialize the wares in a more controlled way
Random rnd = new Random(System.nanoTime());
for (EWare ware : EWare.values()) {
boolean hasWare = rnd.nextInt(7)%7!=0;
if (hasWare){
int amount = rnd.nextInt(159)+1; // 1..150
addNewWare(ware, amount);
}
}
}
/**
* Initialize the population of the different classes based on the properties
* @param props
*/
private void initPopulation(Properties props) {
// TODO this must be done based on properties and starting year
Random rnd = new Random(System.nanoTime());
int pop = 1000+(int)Math.abs(rnd.nextGaussian()*4000); // value between 1000 and 5000
int diffPoor = (int) ((rnd.nextGaussian()-0.5)*7);
int poor = (int)((60.0+diffPoor)/100*pop); // about 60% poor
setPopulation(poor, EPopulationClass.POOR);
int medium = (pop-poor)*2/3;
int rich = pop-poor-medium;
setPopulation(medium, EPopulationClass.MEDIUM);
setPopulation(rich, EPopulationClass.RICH);
}
/**
* Retrieve the total population
* @return
*/
@Override
public int getPopulation() {
int count = 0;
for (Integer i : population.values()) {
count += i;
}
return count;
}
/**
* Set the population count for a apopulation class
* @param population count
* @param popClass population class
*/
@Override
public void setPopulation(int population, EPopulationClass popClass) {
this.population.put(popClass,population);
}
/**
* Retrieve the population count for a class
* @param popclass population class
* @return
*/
@Override
public int getPopulation(EPopulationClass popclass){
return population.get(popclass);
}
@Override
public String getName() {
return name;
}
@Override
public EKontorType getKontorType() {
return kontorType;
}
/**
* Retrieve the wares that are produced efficiently
* @return
*/
@Override
public IWare[] getEffectiveProduction() {
return effectiveProduction;
}
/**
* Retrieve the wares that are produced inefficiently
* @return
*/
@Override
public IWare[] getIneffectiveProduction() {
return ineffectiveProduction;
}
@Override
public List<IBuilding> getBuildings(){
return Collections.unmodifiableList(buildings);
}
/**
* Add a new building to the city
* @param building
*/
@Override
public void build(IBuilding building){
buildings.add(building);
}
/**
* Remove a building from the list of buildings in the city
* @param building
*/
@Override
public void tearDown(IBuilding building){
buildings.remove(building);
}
@Override
public IReputation getReputation(IPlayer player){
return reputation.get(player);
}
@Override
public void moveIn(IPlayer player){
IReputation rep = new Reputation(this,player);
reputation.put(player, rep);
playersContributions.put(player, new Contributions());
}
@Override
public ESocialRank getSocialRank(){
return ESocialRank.CHANDLER; // TODO implement this correctly
}
/**
* {@inheritDoc}
* Update the contributions as the ware is moved
*/
@Override
public int move(IWare ware, int amount,IPlayer player) {
int moved = super.move(ware, amount,player);
if (player!=null){ // possible from test or from the city itself
Contributions contrib = playersContributions.get(player);
contrib.contribute(ware, moved);
}
return moved;
}
@Override
public int getContribution(IPlayer player, IWare ware){
Contributions contribs = playersContributions.get(player);
return contribs.getContribution(ware);
}
}