package com.hbm.util;

import com.hbm.interfaces.NotableComments;
import cpw.mods.fml.relauncher.ReflectionHelper;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.stats.StatBase;
import net.minecraft.stats.StatCrafting;
import net.minecraft.stats.StatList;
import net.minecraft.util.ChatComponentTranslation;

import java.util.Iterator;
import java.util.Map;

@NotableComments
public class StatHelper {

	/*
	 * God is dead and we are pissing on his grave
	 */
	public static Map publicReferenceToOneshotStatListPleaseAllPointAndLaugh;

	/**
	 * This is probably the worst fucking way of doing this, but it's the only one I could think of that works.
	 * In short: stats are hilariously broken.
	 * Minecraft registers them at the earliest point during startup right after blocks and items are registered.
	 * The problem in that? Item remapping. Modded items probably don't even exist at that point and even if they did,
	 * the system would break because modded items have dynamic IDs and the stats register fixed IDs instead of item instances.
	 * What did forge do to solve this issue? Well nothing, of course! The injected bits comment on that in vanilla's stat
	 * registering code, but instead of fixing anything it just slaps a fat "TODO" onto it. Wow! Really helpful!
	 *
	 * So what do we do? Every time the world starts and we know the IDs are now correct, we smack that fucker up nice and good.
	 * All ID-bound stats get deep-cleaned out of this mess and registered 1:1 again. Is this terrible and prone to breaking with
	 * mods that do their own stat handling? Hard to say, but the possibility is there.
	 */
	public static void resetStatShitFuck() {

		publicReferenceToOneshotStatListPleaseAllPointAndLaugh = ReflectionHelper.getPrivateValue(StatList.class, null, "field_75942_a", "oneShotStats");

		for(int i = 0; i < StatList.objectCraftStats.length; i++) StatList.objectCraftStats[i] = null;
		for(int i = 0; i < StatList.mineBlockStatArray.length; i++) StatList.mineBlockStatArray[i] = null;
		for(int i = 0; i < StatList.objectUseStats.length; i++) StatList.objectUseStats[i] = null;
		for(int i = 0; i < StatList.objectBreakStats.length; i++) StatList.objectBreakStats[i] = null;
		StatList.objectMineStats.clear();
		StatList.itemStats.clear();

		try {
			initCraftItemStats();
			initBlockMineStats();
			initItemUseStats();
			initItemBreakStats();
		} catch(Throwable ex) { } // just to be sure
	}

	/**
	 * For reasons beyond human comprehension, this bit originally only registered items that are the result
	 * of an IRecipe instead of just all items outright like the item usage stats. The logical consequence of this is:
	 * 1) The code has to iterate over thousands of recipes to get recipe results which is no more performant than just going over
	 *    32k potential items, most of which are going to be null anyway
	 * 2) The system just will never work with items that don't have crafting table recipes
	 */
	private static void initCraftItemStats() {
		Iterator iterator = Item.itemRegistry.iterator();
		while(iterator.hasNext()) {
			Item item = (Item) iterator.next();

			if(item != null) {
				int i = Item.getIdFromItem(item);
				try {
					StatList.objectCraftStats[i] = registerStat(new StatCrafting("stat.craftItem." + i, new ChatComponentTranslation("stat.craftItem", new Object[] { (new ItemStack(item)).func_151000_E() }), item));
				} catch(Throwable ex) { }
			}
		}

		replaceAllSimilarBlocks(StatList.objectCraftStats);
	}

	private static void initBlockMineStats() {
		Iterator iterator = Block.blockRegistry.iterator();

		while(iterator.hasNext()) {
			Block block = (Block) iterator.next();

			if(Item.getItemFromBlock(block) != null) {
				int i = Block.getIdFromBlock(block);
				try {
					if(block.getEnableStats()) {
						StatList.mineBlockStatArray[i] = registerStat(new StatCrafting("stat.mineBlock." + i, new ChatComponentTranslation("stat.mineBlock", new Object[] { (new ItemStack(block)).func_151000_E() }), Item.getItemFromBlock(block)));
						StatList.objectMineStats.add((StatCrafting) StatList.mineBlockStatArray[i]);
					}
				} catch(Throwable ex) { }
			}
		}

		replaceAllSimilarBlocks(StatList.mineBlockStatArray);
	}

	private static void initItemUseStats() {
		Iterator iterator = Item.itemRegistry.iterator();

		while(iterator.hasNext()) {
			Item item = (Item) iterator.next();

			if(item != null) {
				int i = Item.getIdFromItem(item);
				try {
					StatList.objectUseStats[i] = registerStat(new StatCrafting("stat.useItem." + i, new ChatComponentTranslation("stat.useItem", new Object[] { (new ItemStack(item)).func_151000_E() }), item));
					if(!(item instanceof ItemBlock)) {
						StatList.itemStats.add((StatCrafting) StatList.objectUseStats[i]);
					}
				} catch(Throwable ex) { }
			}
		}

		replaceAllSimilarBlocks(StatList.objectUseStats);
	}

	private static void initItemBreakStats() {
		Iterator iterator = Item.itemRegistry.iterator();

		while(iterator.hasNext()) {
			Item item = (Item) iterator.next();

			if(item != null) {
				int i = Item.getIdFromItem(item);
				try {
					if(item.isDamageable()) {
						StatList.objectBreakStats[i] = registerStat(new StatCrafting("stat.breakItem." + i, new ChatComponentTranslation("stat.breakItem", new Object[] { (new ItemStack(item)).func_151000_E() }), item));
					}
				} catch(Throwable ex) { }
			}
		}

		replaceAllSimilarBlocks(StatList.objectBreakStats);
	}

	private static void replaceAllSimilarBlocks(StatBase[] stats) {
		func_151180_a(stats, Blocks.water, Blocks.flowing_water);
		func_151180_a(stats, Blocks.lava, Blocks.flowing_lava);
		func_151180_a(stats, Blocks.lit_pumpkin, Blocks.pumpkin);
		func_151180_a(stats, Blocks.lit_furnace, Blocks.furnace);
		func_151180_a(stats, Blocks.lit_redstone_ore, Blocks.redstone_ore);
		func_151180_a(stats, Blocks.powered_repeater, Blocks.unpowered_repeater);
		func_151180_a(stats, Blocks.powered_comparator, Blocks.unpowered_comparator);
		func_151180_a(stats, Blocks.redstone_torch, Blocks.unlit_redstone_torch);
		func_151180_a(stats, Blocks.lit_redstone_lamp, Blocks.redstone_lamp);
		func_151180_a(stats, Blocks.red_mushroom, Blocks.brown_mushroom);
		func_151180_a(stats, Blocks.double_stone_slab, Blocks.stone_slab);
		func_151180_a(stats, Blocks.double_wooden_slab, Blocks.wooden_slab);
		func_151180_a(stats, Blocks.grass, Blocks.dirt);
		func_151180_a(stats, Blocks.farmland, Blocks.dirt);
	}

	private static void func_151180_a(StatBase[] stats, Block block, Block similarBlock) {
		int i = Block.getIdFromBlock(block);
		int j = Block.getIdFromBlock(similarBlock);

		if(stats[i] != null && stats[j] == null) {
			stats[j] = stats[i];
		} else {
			StatList.allStats.remove(stats[i]);
			StatList.objectMineStats.remove(stats[i]);
			StatList.generalStats.remove(stats[i]);
			stats[i] = stats[j];
		}
	}

	private static StatBase registerStat(StatBase stat) {
		if(publicReferenceToOneshotStatListPleaseAllPointAndLaugh.containsKey(stat.statId)) {
			publicReferenceToOneshotStatListPleaseAllPointAndLaugh.remove(stat.statId);
		}

		StatList.allStats.add(stat);
		publicReferenceToOneshotStatListPleaseAllPointAndLaugh.put(stat.statId, stat);
		return stat;
	}
}
