package mior.model;

import mior.model.dist.LinearDistribution;
import mior.model.dist.StaticDistribution;

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

public class ModelFactory {
	
	private boolean batchModeEnabled;
	private boolean randomizePopulations;
	private int groupSize;
	private int blockSize;
	
	private final static Logger logger = LoggerFactory.getLogger(ModelFactory.class);
	
	public ModelFactory() {
		this.batchModeEnabled = true;
		this.randomizePopulations = false;
		this.groupSize = 1;
		this.blockSize = -1;
	}
	
	public IMiorModel createModel(int nbMM, int nbOM, int scale, int modelVersion) {
		final MiorWorld world = new MiorWorld(nbMM, nbOM, scale);
		final IMiorModel model;
		
		logger.info("Using model version v" + modelVersion);
		
		if (modelVersion <= 5 && groupSize > 1) {
			throw new UnsupportedOperationException("Mior implementation "
					+ modelVersion + " does not support this degree ("
					+ groupSize + ") of parallelism.");
		}
		
		switch (modelVersion) {
		case 0:
			model = new CPUMiorModel(world);
			break;
		case 1:
			model = new OCLMiorModel(world);
			break;
		case 2:
			model = new OCLMiorModel2(world);
			break;
		case 3:
			model = new OCLCSRModel(world, false);
			break;
		case 4:
			model = new OCLCSRModel(world, true);
			break;
		case 5:
			model = new OCLCSRModel2(world);
			break;
		case 6:
			model = new OCLParaCSRModel2(nbMM, nbOM, groupSize);
			if (blockSize != -1) model.setBlockSize(blockSize);
			break;
		case 7:
			if (randomizePopulations) {
				model = new OCLParaCSRModel3(nbMM, nbOM, groupSize, new LinearDistribution(nbMM, nbOM, 0.3));
			} else {
				model = new OCLParaCSRModel3(nbMM, nbOM, groupSize, new StaticDistribution(nbMM, nbOM));
			}
			if (blockSize != -1) model.setBlockSize(blockSize);
			break;
		case 8:
			if (randomizePopulations) {
				model = new OCLParaCSRModelAlt(nbMM, nbOM, groupSize, new LinearDistribution(nbMM, nbOM, 0.3));
			} else {
				model = new OCLParaCSRModelAlt(nbMM, nbOM, groupSize, new StaticDistribution(nbMM, nbOM));
			}
			if (blockSize != -1) model.setBlockSize(blockSize);
			break;
		case 9:
			if (randomizePopulations) {
				model = new OCLParaCSRModel4(nbMM, nbOM, groupSize, new LinearDistribution(nbMM, nbOM, 0.3));
			} else {
				model = new OCLParaCSRModel4(nbMM, nbOM, groupSize, new StaticDistribution(nbMM, nbOM));
			}
			if (blockSize != -1) model.setBlockSize(blockSize);
			break;
		default:
			throw new RuntimeException("Invalid model version: " + modelVersion);
		}
		
		model.setBatchModeEnabled(batchModeEnabled);
		model.setRandomEnabled(randomizePopulations);
		
		return model;
	} 
	
	public void setBatchModeEnabled(boolean batchModeEnabled) {
		this.batchModeEnabled = batchModeEnabled;
	}
	
	public boolean isBatchModeEnabled() {
		return batchModeEnabled;
	}
	
	public void setRandomEnabled(boolean randomEnabled) {
		this.randomizePopulations = randomEnabled;
	}
	
	public boolean isRandomEnabled() {
		return randomizePopulations;
	}
	
	public void setBlockSize(int blockSize) {
		this.blockSize = blockSize;
	}
	
	public int getBlockSize() {
		return blockSize;
	}
	
	public void setGroupSize(int kernelSize) {
		this.groupSize = kernelSize;
	}
	
	public int getGroupSize() {
		return groupSize;
	}
	
}
