package mior.model;

import java.util.ArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public abstract class AbstractMiorModel implements IMiorModel {
	
	private int step;
	private boolean batchModeEnabled;
	private boolean randomEnabled;
	private boolean topologyDone;
	private boolean finished;
	
	private ArrayList<IMiorUpdateListener> updateListeners;
	private ArrayList<IMiorFinishListener> finishListeners;
	
	private final static Logger logger = LoggerFactory.getLogger(AbstractMiorModel.class);
	
	public AbstractMiorModel() {
		this.step = 0;
		this.batchModeEnabled = false;
		this.randomEnabled = true;
		this.finished = false;
		this.updateListeners = new ArrayList<IMiorUpdateListener>();
		this.finishListeners = new ArrayList<IMiorFinishListener>();
	}
	
	@Override
	public final boolean isBatchModeEnabled() {
		return batchModeEnabled;
	}
	
	@Override
	public final void setBatchModeEnabled(boolean b) {
		this.batchModeEnabled = b;	
	}
	
	@Override
	public final boolean isRandomEnabled() {
		return randomEnabled;
	}
	
	@Override
	public final void setRandomEnabled(boolean b) {
		this.randomEnabled = b;	
	}
	
	@Override
	public int getBlockSize() {
		throw new UnsupportedOperationException("Explicit block size is not supported on this implementation.");
	}
	
	@Override
	public void setBlockSize(int blockSize) {
		throw new UnsupportedOperationException("Setting an explicit block size is not supported on this implementation.");
	}
	
	@Override
	public boolean isFinished() {
		return finished;
	}
	
	@Override
	public final void reset() {
		this.step = 0;
		this.finished = false;
		resetImpl();
		fireMiorUpdated();
	}
	
	@Override
	public final int getStep() {
		return step;
	}
	
	@Override
	public final void addUpdateListener(IMiorUpdateListener listener) {
		updateListeners.add(listener);
	}
	
	@Override
	public final void removeUpdateListener(IMiorUpdateListener listener) {
		updateListeners.remove(listener);
	}
	
	protected final void fireMiorUpdated() {
		for (IMiorUpdateListener listener : updateListeners) {
			listener.onMiorModelUpdated(this);
		}
	}
	
	protected final void fireMiorFinished() {
		for (IMiorFinishListener listener : finishListeners) {
			listener.onMiorModelFinished(this);
		}
	}
	
	@Override
	public final void doTopology() {
		logger.trace("doTopology");
		
		step++;
		doTopologyImpl();
		this.topologyDone = true;
		fireMiorUpdated();
		
		logger.trace("doTopologyEnd");
	}
	
	@Override
	public final void doLive() {
		logger.trace("doLive");
		
		if (! topologyDone) {
			doTopology();
		}
		
		step ++;
		doLiveImpl();
		fireMiorUpdated();
		
		logger.trace("doLiveEnd");
	}
	
	@Override
	public void doAutoLive() {
		int notChangedSince = 0;
		int oldWorldCO2 = getWorld().CO2;
		
		if (step != 0) {
			doLive();
		}
		
		if (oldWorldCO2 == getWorld().CO2) {
			reset();
			doTopology();
			doLive();
		}
		
		while (notChangedSince < 2) {
			if (getWorld().CO2 == oldWorldCO2) {
				notChangedSince++;
			} else {
				oldWorldCO2 = getWorld().CO2;
				notChangedSince = 0;
			}
			
			doLive();
		}
		
		this.finished = true;
		onSimulationFinished();
	}
	
	@Override
	public final void release() {
		releaseImpl();
	}
	
	protected abstract void resetImpl();
	protected abstract void doTopologyImpl();
	protected abstract void doLiveImpl();
	protected abstract void releaseImpl();
	
	protected void onSimulationFinished() {
		fireMiorUpdated();
		fireMiorFinished();
	}
}
