package eu.dnetlib.data;

import java.util.List;
import java.util.Map;

import org.apache.commons.lang.math.RandomUtils;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.WriteConcern;

import eu.dnetlib.data.PaceDatasetGenerator.Field;
import eu.dnetlib.data.function.Date1Gen;
import eu.dnetlib.data.function.DateSwitch;
import eu.dnetlib.data.function.IdentityFunction;
import eu.dnetlib.data.function.OneCharErrWeight;
import eu.dnetlib.data.function.OneCharMiss;
import eu.dnetlib.data.function.TwoCharSwap;

public class ErrorGenerator {
	
	private Map<ErrorKind, Function<String, String>> functionMap = Maps.newHashMap();
	
	public ErrorGenerator() {
		//functionMap.put(ErrorKind.nameAbbreviation, new NameAbbreviation());
		//functionMap.put(ErrorKind.oneCharErr, new OneCharError());
		functionMap.put(ErrorKind.oneCharErrWeight, new OneCharErrWeight(80));
		functionMap.put(ErrorKind.oneCharMiss, new OneCharMiss());
		functionMap.put(ErrorKind.twoCharSwap, new TwoCharSwap());
		functionMap.put(ErrorKind.date1Gen, new Date1Gen());
		functionMap.put(ErrorKind.dateSwitch, new DateSwitch());
		functionMap.put(ErrorKind.none, new IdentityFunction());
	}
	
	public boolean insertError(final DBCollection base, final Map<String, Object> objMap, int i) {
		
		final ErrorKind e = (i % 10 == 0) ? pickError() : ErrorKind.none;
		
		Function<String, String> f = functionMap.get(e);
		objMap.put("error", e.name());
		
		switch (e) {
//			case nameAbbreviation:
//				objMap.put(Field.firstName.name(), f.apply(objMap.get(Field.firstName.name())));
			
			case twoCharSwap:
			//case oneCharErr:
			case oneCharErrWeight:
			case oneCharMiss:
				for(Field field : pickFields()) {
					String s = (String) objMap.get(field.name());
					objMap.put(field.name(), f.apply(s));
				}
				break;
				
			case dateSwitch:
			case date1Gen:				
				objMap.put(Field.birthDate.name(), f.apply((String) objMap.get(Field.birthDate.name())));
				break;
				
			case none:
			default:
				return false;
		}	
		
		objMap.put("kind", "duplicate");
		objMap.put("relatedTo", objMap.get("n"));
		objMap.put("n", i + 1);
		base.insert(new BasicDBObject(objMap), WriteConcern.NORMAL);
		
		return true;
	}
	
	public boolean dice(int percentage) {
		int r = RandomUtils.nextInt(100) + 1;
		boolean b = r <= percentage;
		return b;
	}
	
	public ErrorKind pickError() {
		return ErrorKind.values()[RandomUtils.nextInt(ErrorKind.values().length - 1) + 1];
	}
	
	public List<Field> pickFields() {
		switch(RandomUtils.nextInt(3)) {
			case 0: return Lists.newArrayList(Field.firstName);
			case 1: return Lists.newArrayList(Field.lastName);
			case 2: return Lists.newArrayList(Field.firstName, Field.lastName);
			default: return Lists.newArrayList(Field.firstName, Field.lastName);
		}
	}
	
	public Function<String, String> getFunction(ErrorKind e) {
		return functionMap.get(e);
	}
	
}
