package dk.itu.mario.level.matcher;

import java.util.HashMap;
import java.util.Map;

import dk.itu.mario.MarioInterface.GamePlay;
import dk.itu.mario.engine.DataRecorder;
import dk.itu.mario.level.PlayerProfile;
import dk.itu.mario.level.PlayerProfile.SKILL_LEVEL;

public class ProfileMatcher {
	public enum SKILL { BUMP, COLLECT, JUMP, RUN, SHOOT, STOMP }
	
	private static Map<PlayerProfile.TYPE, Map<SKILL,Integer>> referenceMetrics = new HashMap<PlayerProfile.TYPE, Map<SKILL,Integer>>();
	
	static {
		Map<SKILL,Integer> canonicalVector = new HashMap<SKILL,Integer>();		
		canonicalVector.put(SKILL.BUMP, 100);
		canonicalVector.put(SKILL.COLLECT, 50);
		canonicalVector.put(SKILL.JUMP, 50);
		canonicalVector.put(SKILL.RUN, 0);
		canonicalVector.put(SKILL.SHOOT, 0);
		canonicalVector.put(SKILL.STOMP, 0);
		referenceMetrics.put(PlayerProfile.TYPE.BUMPER, canonicalVector);
		
		canonicalVector = new HashMap<SKILL,Integer>();
		canonicalVector.put(SKILL.BUMP, 50);
		canonicalVector.put(SKILL.COLLECT, 100);
		canonicalVector.put(SKILL.JUMP, 50);
		canonicalVector.put(SKILL.RUN, 0);
		canonicalVector.put(SKILL.SHOOT, 0);
		canonicalVector.put(SKILL.STOMP, 0);	
		referenceMetrics.put(PlayerProfile.TYPE.COLLECTOR, canonicalVector);
		
		canonicalVector = new HashMap<SKILL,Integer>();
		canonicalVector.put(SKILL.BUMP, 50);
		canonicalVector.put(SKILL.COLLECT, 0);
		canonicalVector.put(SKILL.JUMP, 100);
		canonicalVector.put(SKILL.RUN, 25);
		canonicalVector.put(SKILL.SHOOT, 0);
		canonicalVector.put(SKILL.STOMP, 0);
		referenceMetrics.put(PlayerProfile.TYPE.JUMPER, canonicalVector);
		
		canonicalVector = new HashMap<SKILL,Integer>();
		canonicalVector.put(SKILL.BUMP, 0);
		canonicalVector.put(SKILL.COLLECT, 0);
		canonicalVector.put(SKILL.JUMP, 25);
		canonicalVector.put(SKILL.RUN, 100);
		canonicalVector.put(SKILL.SHOOT, 25);
		canonicalVector.put(SKILL.STOMP, 0);	
		referenceMetrics.put(PlayerProfile.TYPE.RUNNER, canonicalVector);
		
		canonicalVector = new HashMap<SKILL,Integer>();
		canonicalVector.put(SKILL.BUMP, 0);
		canonicalVector.put(SKILL.COLLECT, 0);
		canonicalVector.put(SKILL.JUMP, 25);
		canonicalVector.put(SKILL.RUN, 50);
		canonicalVector.put(SKILL.SHOOT, 100);
		canonicalVector.put(SKILL.STOMP, 0);
		referenceMetrics.put(PlayerProfile.TYPE.SHOOTER, canonicalVector);
	}
	
	public static PlayerProfile getMatchingProfile(GamePlay playerMetrics, DataRecorder detailedInfo) {
		System.out.println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs.");
		
		int bumperScore = 0;
		int collectorScore = 0;
		int jumperScore = 0;
		int runnerScore = 0;
		int shooterScore = 0;
		int stomperScore = 0;
		
		bumperScore += playerMetrics.percentageBlocksDestroyed * 20;
		bumperScore += playerMetrics.percentageCoinBlocksDestroyed * 20;
		bumperScore += playerMetrics.percentageEmptyBlockesDestroyed * 20;
		bumperScore += playerMetrics.percentagePowerBlockDestroyed * 20;
		bumperScore += 4 * Math.min(5, playerMetrics.kickedShells);
		
		collectorScore += playerMetrics.coinsCollected / 35.0;
		
		jumperScore = Math.min(100, playerMetrics.jumpsNumber);
		
		runnerScore = playerMetrics.timeRunningRight / playerMetrics.totalTime;
		
		shooterScore += 10 * Math.min(10, playerMetrics.enemyKillByFire / 10.0);
		
		stomperScore += Math.min(100, 2 * (playerMetrics.GoombasKilled + playerMetrics.GreenTurtlesKilled + playerMetrics.RedTurtlesKilled));
		
		clampToPercentRange(bumperScore);
		clampToPercentRange(collectorScore);
		clampToPercentRange(jumperScore);
		clampToPercentRange(runnerScore);
		clampToPercentRange(shooterScore);
		clampToPercentRange(stomperScore);
		
		System.out.println("bumperScore: " + bumperScore);
		System.out.println("collectorScore: " + collectorScore);
		System.out.println("jumperScore: " + jumperScore);
		System.out.println("shooterScore: " + shooterScore);
		System.out.println("stomperScore: " + stomperScore);
		
		Map<SKILL,Integer> playerProfileVector = new HashMap<SKILL,Integer>();
		playerProfileVector.put(SKILL.BUMP, bumperScore);
		playerProfileVector.put(SKILL.COLLECT, collectorScore);
		playerProfileVector.put(SKILL.JUMP, jumperScore);
		playerProfileVector.put(SKILL.RUN, runnerScore);
		playerProfileVector.put(SKILL.SHOOT, shooterScore);
		playerProfileVector.put(SKILL.STOMP, stomperScore);
		
		double minDist = Double.MAX_VALUE;
		PlayerProfile.TYPE closestMatch = PlayerProfile.TYPE.RUNNER;
		
		for (PlayerProfile.TYPE type : PlayerProfile.TYPE.values()) {
			Map<SKILL,Integer> canonicalVector = ProfileMatcher.referenceMetrics.get(type);
			double distance = 0.0;
			for (SKILL skill : SKILL.values()) {
				distance += Math.pow(canonicalVector.get(skill) - playerProfileVector.get(skill), 2);
			}
			if (distance < minDist) {
				minDist = distance;
				closestMatch = type;
			}
		}
		
		SKILL_LEVEL skillLevel;
		
		int keyScore = getKeyScore(playerProfileVector, closestMatch);
		
		if (keyScore <= 20) {
			skillLevel = SKILL_LEVEL.NOVICE;
		} else if (keyScore <= 40) {
			skillLevel = SKILL_LEVEL.BEGINNER;
		} else if (keyScore <= 60) {
			skillLevel = SKILL_LEVEL.COMPETENT;
		} else if (keyScore <= 80) {
			skillLevel = SKILL_LEVEL.PROFICIENT;
		} else {
			skillLevel = SKILL_LEVEL.EXPERT;
		}
		
		System.out.println("Skill level for this " + closestMatch + " is " + skillLevel + " (based on a score of " + keyScore + " out of 100.)");
		
		return new PlayerProfile(skillLevel, closestMatch, playerProfileVector);
	}
	
	private static int getKeyScore(Map<SKILL, Integer> playerProfileVector, PlayerProfile.TYPE playerProfileType) {
		switch (playerProfileType) {
		case BUMPER :
			return playerProfileVector.get(SKILL.BUMP);
		case COLLECTOR :
			return playerProfileVector.get(SKILL.COLLECT);
		case JUMPER :
			return playerProfileVector.get(SKILL.JUMP);
		case RUNNER :
			return playerProfileVector.get(SKILL.RUN);
		case SHOOTER :
			return playerProfileVector.get(SKILL.SHOOT);
		default :
			return 0;
		}
	}
	
	private static int clampToPercentRange(int value) {
		if (value < 0) {
			return 0;
		} else if (value > 100) {
			return 100;
		} else {
			return value;
		}
	}
}
