package jonathan.geoffroy.zorbscity.model.cityObjects.buildings;

import jonathan.geoffroy.zorbscity.model.Simulator;
import jonathan.geoffroy.zorbscity.model.SimulatorEvent;
import jonathan.geoffroy.zorbscity.model.TimeLine;
import jonathan.geoffroy.zorbscity.model.cityObjects.resources.Resource;
import jonathan.geoffroy.zorbscity.model.helpers.NeededList;
import jonathan.geoffroy.zorbscity.model.simulator.events.ConsumeNeededResources;

/**
 * House support inhabitants: it's used to have more inhabitants in a city.
 * @author Jonathan GEOFFROY
 * @version 0.1
 */
public abstract class House extends Building {

	/**
	 * auto-generated serial (using eclipse)
	 */
	private static final long serialVersionUID = -5852205493007290911L;

	public static final int TIME_TO_USE_RESOURCE = 12;

	/**
	 * The list of previous resources, i.e. the resources that previous house needs
	 */
	protected NeededList previousNeeded;

	public House() {
		super();
		previousNeeded = new NeededList();
		addPreviousNeededResources();
		addNeededResources();
	}

	public House(House house) {
		super(house);
		previousNeeded = new NeededList();

		addPreviousNeededResources();
		addNeededResources();

		this.numberPeople = house.numberPeople;
		this.resources = house.resources;
	}

	private void addPreviousNeededResources() {
		House previousHouse = getPreviousHouseType();
		if(previousHouse != null) {
			previousNeeded.addAllNeeded(previousHouse.previousNeeded);
			previousNeeded.addAllNeeded(previousHouse.needed);
		}
	}

	@Override
	public void addResource(Simulator sim, Resource r) {
		super.addResource(sim, r);

		House increasedThis = getNextHouseType();
		if(increasedThis != null && enoughResources()) {
			sim.transform(this, increasedThis);
			increasedThis.justIncreased(sim);
		}
	}

	@Override
	public void onAddedIntoCity(Simulator sim) {
		super.onAddedIntoCity(sim);
		beginToConsumeResources(sim);
	}

	@Override
	public boolean enoughResources() {
		return resources.enoughResources(needed) && resources.enoughResources(previousNeeded);
	}

	@Override
	public boolean enoughResources(int resourceType) {
		return resources.enoughResources(needed, resourceType) && resources.enoughResources(previousNeeded, resourceType);
	}

	/**
	 * Check if the current house should increase
	 * @return true if the current house should increase
	 */
	public boolean shouldIncrease() {
		return resources.enoughResources(needed);
	}

	/**
	 * Check if the current house should decrease
	 * Ask the previous house type and check if it should increase
	 * @return true if the current house should decrease
	 */
	public boolean shouldDecrease() {
		return !resources.enoughResources(previousNeeded);
	}

	/**
	 * Create a new instance of the next House (when the current House upgrade)
	 * @return a new instance of the next House (or null if this is the last house type)
	 */
	protected abstract House getNextHouseType();

	/**
	 * Create a new instance of the previous House
	 * @return a new instance of the previous House (or null if this is the first house type)
	 */
	protected abstract House getPreviousHouseType();

	/**
	 * Allows the needed resources exclusively for this house (and not previous houses) 
	 */
	protected abstract void addNeededResources();

	public abstract int getMaxPeopleNumber();

	/**
	 * 
	 * @return true if this house cann't take an other inhabitant
	 */
	public boolean isFull() {
		assert(numberPeople <= getMaxPeopleNumber());
		return numberPeople == getMaxPeopleNumber();
	}

	@Override
	public void onTriggeredEvent(SimulatorEvent event, Simulator sim) {
		super.onTriggeredEvent(event, sim);
		if(event instanceof ConsumeNeededResources) {
			onTriggeredEvent((ConsumeNeededResources) event, sim);
		}
	}

	public void onTriggeredEvent(ConsumeNeededResources event, Simulator sim) {
		int i = 0;
		int nb = event.getNumberConsumer();
		while (i < nb && resources.enoughResources(previousNeeded)) {
			resources.useNeeded(previousNeeded);
			i++;
		}

		if(!resources.enoughResources(previousNeeded)) {
			House decreasedThis = getPreviousHouseType();
			if(decreasedThis != null) {
				sim.transform(this, decreasedThis);
				decreasedThis.justDecreased(sim);
			}
		}
		else {
			beginToConsumeResources(sim);
		}

	}

	private void beginToConsumeResources(Simulator sim) {
		TimeLine timeLine = sim.getTimeLine();
		timeLine.add(new ConsumeNeededResources(this,timeLine.getTime() + TIME_TO_USE_RESOURCE, this.numberPeople));
	}

	private void justIncreased(Simulator sim) {}

	private void justDecreased(Simulator sim) {}
}
