package jas.common.command;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.entity.Entity;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChunkCoordinates;
import net.minecraft.util.MathHelper;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.World;
import net.sourceforge.jeval.function.math.MathFunctions;

public class CommandEntityStats extends CommandJasBase {

	public String getCommandName() {
		return "entitystats";
	}

	private static class StatsTabulator {
		public float chunksMean;
		public float chunksMedian;
		public float chunksMode;

		public float distanceMean;
		public float distanceMedian;
		public float distanceMode;

		public StatsTabulator(StatsCounter counter) {
			chunksMean = mean(counter.entitiesInChunks);
			chunksMedian = median(counter.entitiesInChunks);
			chunksMode = mode(counter.entitiesInChunks);

			distanceMean = mean(counter.entitiesDistances);
			distanceMedian = median(counter.entitiesDistances);
			distanceMode = mode(counter.entitiesDistances);
		}
		
		private float mean(List<Float> numbers) {
			float sum = 0;
			for (Float number : numbers) {
				sum += number;
			}
			return sum / numbers.size();
		}
		
		private float mode(List<Float> numbers) {
			Collections.sort(numbers);

			Integer longestRepeated = null;
			float longestValue = 0;

			int currentRepeated = 0;
			int currentValue = 0;

			for (Float number : numbers) {
				int value = (int) (number * 10000); // 10000 Chosen for 4 sig digits of accuracy
				if (currentValue == value) {
					currentRepeated++;
					if (longestRepeated == null || longestRepeated < currentRepeated) {
						longestValue = currentValue / 10000;
						longestRepeated = currentRepeated;
					}
				} else {
					currentRepeated = 0;
					currentValue = value;
				}
			}
			return longestRepeated == 1 ? -1 : longestValue;
		}
		
		private float median(List<Float> numbers) {
			Collections.sort(numbers);
			return numbers.get(numbers.size() / 2);
		}
	}

	private static class StatsCounter {
		private final List<Float> entitiesInChunks = new ArrayList<Float>();
		private final List<Float> entitiesDistances = new ArrayList<Float>();
		private final static int maxDistance = 1000000;

		public void addEntitiesFromChunk(Collection<Entity> collection) {
			entitiesInChunks.add((float)collection.size());
			for (Entity entity : collection) {
				World world = entity.worldObj;
				Entity closestEntity = world.getClosestPlayerToEntity(entity, maxDistance);
				entitiesDistances.add(closestEntity != null ? entity.getDistanceToEntity(closestEntity)
						: maxDistance);
			}
		}
	}
	
	/**
	 * Return the required permission level for this command.
	 */
	public int getRequiredPermissionLevel() {
		return 2;
	}

	@Override
	public String getCommandUsage(ICommandSender commandSender) {
		return "commands.jasentitystats.usage";
	}

	@Override
	public void process(ICommandSender commandSender, String[] stringArgs) {
		if (stringArgs.length > 0) {
			throw new WrongUsageException("commands.jasentitystats.usage", new Object[0]);
		}

		World world = commandSender.getEntityWorld();
		
		Multimap<ChunkCoordIntPair, Entity> entitiesPerChunk = ArrayListMultimap.create();

		for (Object object : world.getLoadedEntityList()) {
			Entity entity = (Entity) object;
			entitiesPerChunk.put(
					new ChunkCoordIntPair(MathHelper.floor_double(entity.posX / 16.0D), MathHelper
							.floor_double(entity.posZ / 16.0D)), entity);
		}
		
		StatsCounter counter = new StatsCounter();
		for (ChunkCoordIntPair chunkCoord : entitiesPerChunk.keySet()) {
			counter.addEntitiesFromChunk(entitiesPerChunk.get(chunkCoord));
		}
		
		StatsTabulator tabulator = new StatsTabulator(counter);
		
		StringBuilder disMsg = new StringBuilder();
		disMsg.append("Entity Density: ");
		disMsg.append("Mean [").append(String.format("%.2f", tabulator.chunksMean)).append("], ");
		disMsg.append("Median [").append(String.format("%.2f", tabulator.chunksMedian)).append("], ");
		disMsg.append("Mode [").append(String.format("%.2f", tabulator.chunksMode)).append("]");
		commandSender.addChatMessage(new ChatComponentText(disMsg.toString()));

		disMsg = new StringBuilder();
		disMsg.append("Distance To Player: ");
		disMsg.append("Mean [").append(String.format("%.2f", tabulator.distanceMean)).append("], ");
		disMsg.append("Median [").append(String.format("%.2f", tabulator.distanceMedian)).append("], ");
		disMsg.append("Mode [").append(String.format("%.2f", tabulator.distanceMode)).append("]");
		commandSender.addChatMessage(new ChatComponentText(disMsg.toString()));
	}

	/**
	 * Adds the strings available in this command to the given list of tab completion options.
	 */
	@Override
	public List<String> getTabCompletions(ICommandSender commandSender, String[] stringArgs) {
		return Collections.emptyList();
	}
}