package com.wildex999.tickdynamic.commands;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.wildex999.tickdynamic.TickDynamicMod;
import com.wildex999.tickdynamic.listinject.EntityGroup;
import com.wildex999.tickdynamic.listinject.ListManager;
import com.wildex999.tickdynamic.timemanager.TimeManager;
import com.wildex999.tickdynamic.timemanager.TimedEntities;

import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.world.World;
import net.minecraftforge.common.DimensionManager;

public class CommandWorld implements ICommand {

	private TickDynamicMod mod;
	private int borderWidth;
	private World world;
	
	private int rowsPerPage = 6;
	private int currentPage;
	private int maxPages;
	
	private final DecimalFormat decimalFormat = new DecimalFormat("#.00");
	private final String formatCode = "\u00a7"; //Ignore this when counting length
	
	public CommandWorld(TickDynamicMod mod) {
		this.mod = mod;
		this.borderWidth = 50;
	}

	@Override
	public String getCommandName() {
		return "tickdynamic world";
	}

	@Override
	public String getCommandUsage(ICommandSender p_71518_1_) {
		return "tickdynamic world (world dim) [page]";
	}

	@Override
	public List getCommandAliases() {
		return null;
	}

	@Override
	public void processCommand(ICommandSender sender, String[] args) {
		if(args.length <= 1)
		{
			sender.addChatMessage(new ChatComponentText("Usage: " + getCommandUsage(sender)));
			return;
		}
		
		StringBuilder outputBuilder = new StringBuilder();
		currentPage = 1;
		maxPages = 0;
		
		//Get current page if set
		if(args.length == 3)
		{
			try {
			currentPage = Integer.parseInt(args[2]);
			if(currentPage <= 0)
			{
				sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "Page number must be 1 and up, got: " + args[2]));
				currentPage = 1;
			}
			} catch(Exception e) {
				sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "Expected a page number, got: " + args[2]));
				return;
			}
		}
		
		//Get world by dim
		//TODO: Allow passing in world name(And add auto-complete for it)
		String worldDimStr = args[1];
		int worldDim;
		if(worldDimStr.startsWith("dim"))
			worldDimStr = worldDimStr.substring(3);
		try {
			worldDim = Integer.parseInt(worldDimStr);
		} catch(Exception e) {
			sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "Expected a world dimension(Ex: dim0 or just 0), got: " + worldDimStr));
			return;
		}
		world = DimensionManager.getWorld(worldDim);
		
		if(world == null)
		{
			sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "No world with dimension id: " + worldDimStr));
			return;
		}
		
		
		writeHeader(outputBuilder);
		
		//Get groups from world
		List<EntityGroup> groups = new ArrayList<EntityGroup>();
		if(world.loadedEntityList instanceof ListManager)
			addGroupsFromList(groups, (ListManager)world.loadedEntityList);
		if(world.loadedTileEntityList instanceof ListManager)
			addGroupsFromList(groups, (ListManager)world.loadedTileEntityList);
		
		//Sort the groups, so we don't just have Entities followed by TileEntities
		//TODO: More sorting options
		Collections.sort(groups, new Comparator(){
			public int compare(Object o1, Object o2) {
				EntityGroup group1 = (EntityGroup)o1;
				EntityGroup group2 = (EntityGroup)o2;
				return group1.getName().compareTo(group2.getName());
			}
		});
		
		int listSize = groups.size();
		if(listSize > rowsPerPage)
			maxPages = (int)Math.ceil(listSize/(float)rowsPerPage);
		else
			maxPages = 1;
		
		if(currentPage > maxPages)
			currentPage = maxPages;
		
		//Write stats
		int toSkip = (currentPage-1) * rowsPerPage;
		int toSend = rowsPerPage;
		for(EntityGroup group : groups) {
			//Skip for pages
			if(toSkip-- > 0)
				continue;
			if(toSend-- <= 0)
				break;
			
			writeGroup(outputBuilder, group);
		}
		
		writeFooter(outputBuilder);
		
		splitAndSend(sender, outputBuilder);
	}
	
	private void addGroupsFromList(List<EntityGroup> targetList, ListManager worldList) {
		Iterator<EntityGroup> it = worldList.getGroupIterator();
		while(it.hasNext())
			targetList.add(it.next());
	}

	private void writeHeader(StringBuilder builder) {
		builder.append(EnumChatFormatting.GREEN + "Groups for world: ").append(EnumChatFormatting.RESET + world.provider.getDimensionName()).
				append("(DIM: ").append(world.provider.dimensionId).append(")\n");
		
		builder.append(EnumChatFormatting.GRAY + "+" + StringUtils.repeat("=", borderWidth) + "+\n");
		builder.append(EnumChatFormatting.GRAY + "| ").append(EnumChatFormatting.GOLD + "Group").append(EnumChatFormatting.GRAY);
		
		builder.append(" || " ).append(EnumChatFormatting.GOLD + "Time(Avg.)").append(EnumChatFormatting.GRAY);
		builder.append(" || " ).append(EnumChatFormatting.GOLD + "EntitiesRun(Avg.)").append(EnumChatFormatting.GRAY);
		builder.append(" || " ).append(EnumChatFormatting.GOLD + "MaxSlices").append(EnumChatFormatting.GRAY);
		builder.append(" || " ).append(EnumChatFormatting.GOLD + "TPS(Avg.)").append(EnumChatFormatting.GRAY);
		builder.append("\n");
	}
	
	private void writeGroup(StringBuilder builder, EntityGroup group) {
		TimedEntities timedGroup = group.timedGroup;
		builder.append(EnumChatFormatting.GRAY + "| ").append(EnumChatFormatting.RESET + group.getName());
		
		if(timedGroup == null)
		{ //No Timed data
			builder.append(EnumChatFormatting.RED + " N/A\n");
			return;
		}
		
		String usedTime = decimalFormat.format(timedGroup.getTimeUsedAverage()/(double)TimeManager.timeMilisecond);
		String maxTime = decimalFormat.format(timedGroup.getTimeMax()/(double)TimeManager.timeMilisecond);
		builder.append(EnumChatFormatting.GRAY + " || ").append(EnumChatFormatting.RESET).append(usedTime).append("/").append(maxTime);
		
		int runObjects = timedGroup.getObjectsRunAverage();
		int countObjects = group.entities.size();
		builder.append(EnumChatFormatting.GRAY + " || ").append(EnumChatFormatting.RESET).append(runObjects).append("/").append(countObjects);
		builder.append(EnumChatFormatting.GRAY + " || ").append(EnumChatFormatting.RESET).append(timedGroup.getSliceMax());
		
		//TPS coloring
		String color;
		if(timedGroup.averageTPS >= 19)
			color = EnumChatFormatting.GREEN.toString();
		else if(timedGroup.averageTPS > 10)
			color = EnumChatFormatting.YELLOW.toString();
		else
			color = EnumChatFormatting.RED.toString();
		builder.append(EnumChatFormatting.GRAY + " || ").append(color).append(decimalFormat.format(timedGroup.averageTPS)).append(EnumChatFormatting.RESET + "TPS");
		
		builder.append("\n");
	}
	
	private void writeFooter(StringBuilder builder) {
		if(maxPages == 0)
			builder.append(EnumChatFormatting.GRAY + "+" + StringUtils.repeat("=", borderWidth) + "+\n");
		else
		{
			String pagesStr = EnumChatFormatting.GREEN + "Page " + currentPage + "/" + maxPages;
			int pagesLength = getVisibleLength(pagesStr);
			int otherLength = borderWidth - pagesLength;
			builder.append(EnumChatFormatting.GRAY + "+" + StringUtils.repeat("=", otherLength/2));
			builder.append(pagesStr);
			builder.append(EnumChatFormatting.GRAY + StringUtils.repeat("=", otherLength/2) + "+\n");
		}
	}
	
	public int getVisibleLength(String str) {
		return (str.length() - (StringUtils.countMatches(str, formatCode)*2));
	}
	
	public void splitAndSend(ICommandSender sender, StringBuilder outputBuilder) {
		//Split newline and send
		String[] chatLines = outputBuilder.toString().split("\n");
		for(String chatLine : chatLines)
			sender.addChatMessage(new ChatComponentText(chatLine));
	}

	@Override
	public boolean canCommandSenderUseCommand(ICommandSender sender) {
		return sender.canCommandSenderUseCommand(1, getCommandName());
	}

	@Override
	public List addTabCompletionOptions(ICommandSender p_71516_1_,
			String[] p_71516_2_) {
		return null;
	}

	@Override
	public boolean isUsernameIndex(String[] p_82358_1_, int p_82358_2_) {
		return false;
	}
	
	@Override
	public int compareTo(Object arg0) {
		return 0;
	}

}