package princessrtfm.minecraft.core;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Logger;

import net.minecraft.block.Block;
import net.minecraft.command.CommandBase;
import net.minecraft.entity.Entity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatStyle;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.WeightedRandomChestContent;
import net.minecraft.world.World;
import net.minecraftforge.common.ChestGenHooks;
import princessrtfm.core.Version;
import princessrtfm.core.logger.Level;
import princessrtfm.core.logger.MagicFormatter;
import princessrtfm.core.logger.MagicFormatter.FormatCode;
import princessrtfm.core.trace.StackTrace;
import princessrtfm.core.util.StringUtil;
import princessrtfm.core.util.UtilityLogger;
import princessrtfm.minecraft.core.commands.CmdGetCheats;
import princessrtfm.minecraft.core.commands.CmdGetDebug;
import princessrtfm.minecraft.core.commands.CmdRepair;
import princessrtfm.minecraft.core.commands.CmdToggleDebug;
import princessrtfm.minecraft.core.fireworks.Firework;
import princessrtfm.minecraft.core.fireworks.FireworkExplosion;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
import cpw.mods.fml.common.registry.LanguageRegistry;


/**
 * General mod containing utility methods for dealing with Minecraft
 */
@Mod(modid = Coreder.MOD_METADATA_ID, name = Coreder.MOD_METADATA_NAME, version = Coreder.MOD_METADATA_VERSION)
public class Coreder {
	@SuppressWarnings("javadoc")
	public static final String MOD_METADATA_ID = "coreder";
	@SuppressWarnings("javadoc")
	public static final String MOD_METADATA_VERSION = "1.1.0";
	@SuppressWarnings("javadoc")
	public static final String MOD_METADATA_NAME = "Coreder";
	@Instance(MOD_METADATA_ID)
	private static Coreder instance;
	/**
	 * The current version of the Coreder library
	 */
	public static final Version COREDER_VERSION = Version.parse(MOD_METADATA_VERSION);
	private static final List<CommandBase> commands = new ArrayList<CommandBase>();
	private static final CmdToggleDebug toggleDebug = new CmdToggleDebug();
	private static final CmdGetDebug getDebug = new CmdGetDebug();
	private static final CmdGetCheats getCheats = new CmdGetCheats();
	private static final CmdRepair repair = new CmdRepair();
	/**
	 * If <code>false</code>, the {@linkplain #debug(Object)} method will not create output.
	 */
	public static boolean debug = StringUtil.isTrue(System.getProperty("coreder.debug", "false"));
	/**
	 * The {@link Logger} used by Coreder to display logging messages. <!-- Because fuck the Apache
	 * logger. -->
	 */
	public static final Logger LOGGER;
	/**
	 * A {@link UtilityLogger} from ChaosCore, used for things like quick stack trace dumps.
	 */
	public static final UtilityLogger DEBUGGER = new UtilityLogger();
	static {
		// I will not use the bloody Apache logger.
		// It's not compatible with java's built in Logger class,
		// or the built in Levels, which means it won't work in
		// anything that needs to be passed a Logger object, or
		// anything that wraps a Logger object.
		// Like, for example, all of my stuff.
		LOGGER = Logger.getLogger("Coreder");
		Formatter f = new MagicFormatter("[!" + FormatCode.HOUR_24 + "!:!" + FormatCode.MINUTE + "!:!" + FormatCode.SECOND + "!] [!" + FormatCode.LOGGER_NAME + "!/!" + FormatCode.LEVEL_NAME + "!]", "!", false);
		Handler h = new ConsoleHandler();
		h.setFormatter(f);
		h.setLevel(Level.ALL);
		LOGGER.setUseParentHandlers(false);
		LOGGER.addHandler(h);
		LOGGER.setLevel(Level.ALL);
		// Fuck the Apache logger.
		DEBUGGER.LOG = LOGGER;
	}
	@SuppressWarnings("javadoc")
	@EventHandler
	public void init(FMLPreInitializationEvent evt) {
		DEBUGGER.info("Registering chat commands");
		toggleDebug.register();
		getDebug.register();
		getCheats.register();
		repair.register();
		DEBUGGER.info("Injecting localization");
		injectLocalization("coreder.chat.exception", "An exception occurred while executing this command");
		DEBUGGER.info("Coreder loaded - debugging " + (debug ? "en" : "dis") + "abled");
	}
	/**
	 * Create and spawn a firework rocket entity at the given entity's location, with the given
	 * lifetime (in ticks) and the given effects list
	 *
	 * @param source
	 *        The {@link Entity} to spawn the rocket at - the rocket will spawn using this Entity's
	 *        X, Y, and Z coords, in the same world as it
	 * @param life
	 *        The number of ticks the rocket should exist for before exploding
	 * @param effects
	 *        The effects (one or more) that the firework should use when it explodes
	 * @return The {@link Firework} that was created and launched
	 * @see FireworkExplosion
	 * @see Firework
	 */
	public static Firework launchFirework(Entity source, byte life, FireworkExplosion... effects) {
		return launchFirework(source.worldObj, source.posX, source.posY, source.posZ, life, effects);
	}
	/**
	 * Create and spawn a firework rocket entity at the given location (X, Y, Z) in the given
	 * {@link World}, with the given lifetime (in ticks) and the given effects list
	 *
	 * @param target
	 *        The {@link World} to spawn the firework into
	 * @param x
	 *        The X coordinate to spawn the firework at
	 * @param y
	 *        The Y coordinate to spawn the firework at
	 * @param z
	 *        The Z coordinate to spawn the firework at
	 * @param life
	 *        The number of ticks the rocket should exist for before exploding
	 * @param effects
	 *        The effects (one or more) that the firework should use when it explodes
	 * @return The {@link Firework} that was created and launched
	 * @see FireworkExplosion
	 * @see Firework
	 */
	public static Firework launchFirework(World target, double x, double y, double z, byte life, FireworkExplosion... effects) {
		Firework rocket = new Firework(target, x, y, z, effects);
		rocket.lifetime = life;
		rocket.launch();
		return rocket;
	}
	/**
	 * Adds the given key/value pair to the internal localization table used by Forge
	 *
	 * @param key
	 *        The key to localize
	 * @param value
	 *        The value to localize to
	 * @see #injectLocalization(String, String, String)
	 */
	public static void injectLocalization(String key, String value) {
		injectLocalization(key, value, "en_US");
	}
	/**
	 * Adds the given key/value pair to the internal localization table used by Forge, under the
	 * given language
	 *
	 * @param key
	 *        The key to localize
	 * @param value
	 *        The value to localize to
	 * @param lang
	 *        The language code to inject the localization for
	 * @see #injectLocalization(String, String)
	 */
	public static void injectLocalization(String key, String value, String lang) {
		HashMap<String, String> map = new HashMap<String, String>();
		map.put(key, value);
		LanguageRegistry.instance().injectLanguage(lang, map);
	}
	/**
	 * Inject the localization key for the name of the given item with the given value, under the
	 * given language
	 *
	 * @param item
	 *        The item to localize the name of
	 * @param name
	 *        The localized name of the item
	 * @param lang
	 *        The language to inject the localization for
	 */
	public static void injectLocalization(Item item, String name, String lang) {
		injectLocalization(item.getUnlocalizedName() + ".name", name, lang);
	}
	/**
	 * Inject the localization key for the name of the given item with the given value, under the
	 * default language (en_US)
	 *
	 * @param item
	 *        The item to localize the name of
	 * @param name
	 *        The localized name of the item
	 */
	public static void injectLocalization(Item item, String name) {
		injectLocalization(item, name, "en_US");
	}
	/**
	 * Inject the localization key for the name of the given block with the given value, under the
	 * given language
	 *
	 * @param block
	 *        The block to localize the name of
	 * @param name
	 *        The localized name of the block
	 * @param lang
	 *        The language to inject the localization for
	 */
	public static void injectLocalization(Block block, String name, String lang) {
		injectLocalization(block.getUnlocalizedName() + ".name", name, lang);
	}
	/**
	 * Inject the localization key for the name of the given block with the given value, under the
	 * given language
	 *
	 * @param block
	 *        The block to localize the name of
	 * @param name
	 *        The localized name of the block
	 */
	public static void injectLocalization(Block block, String name) {
		injectLocalization(block, name, "en_US");
	}
	public static void addDungeonLoot(String category, Item item, int damage, int weight, int minCount, int maxCount) {
		ChestGenHooks.addItem(category, new WeightedRandomChestContent(item, damage, minCount, maxCount, weight));
	}
	public static void addDungeonLoot(String category, ItemStack item, int weight, int minCount, int maxCount) {
		ChestGenHooks.addItem(category, new WeightedRandomChestContent(item, minCount, maxCount, weight));
	}
	/**
	 * Log a {@link Level#FINER} message containing the class and method name of the method that
	 * called the method that called this.
	 *
	 */
	public static final void trace() {
		debug(StackTrace.parent());
	}
	/**
	 * Log a {@link Level#FINER} message containing the class and method name of the method that
	 * called the method that called this. Appends the string value of the given object.
	 *
	 * @param desc
	 *        The object to stringify and append to the logged message
	 */
	public static final void trace(Object desc) {
		debug(StackTrace.parent() + ": " + desc);
	}
	/**
	 * Logs a {@link Level#FINER} message, <tt>iff {@linkplain #debug}</tt>
	 *
	 * @param msg
	 *        The object to stringify and log
	 */
	public static final void debug(Object msg) {
		if (debug) {
			DEBUGGER.finer(msg);
		}
	}
	/**
	 * Logs a {@link Level#INFO} message
	 *
	 * @param msg
	 *        The object to stringify and log
	 */
	public static final void echo(Object msg) {
		if (debug) {
			DEBUGGER.info(msg);
		}
	}
	/**
	 * Get a Minecraft-formatted message indicating whether Coreder's debug mode is enabled
	 *
	 * @param justToggled
	 *        if <code>true</code>, the message will say that debug mode is <i>now</i>
	 *        enabled/disabled; if <code>false</code> it will just say what mode it's in
	 *
	 * @return A ready-to-send formatted chat message
	 */
	public static final IChatComponent userFriendlyDebugMessage(boolean justToggled) {
		return new ChatComponentText("Debug mode is " + (justToggled ? "now " : "") + (Coreder.debug ? "en" : "dis") + "abled").setChatStyle(new ChatStyle().setItalic(true).setColor(EnumChatFormatting.AQUA));
	}
	/**
	 * Register a command to be automatically be registered on server startup
	 *
	 * @param command
	 *        the {@link CommandBase} to be registered
	 */
	public static void registerCommand(CommandBase command) {
		if (!commands.contains(command)) {
			echo("Registering chat command: " + command.getCommandName());
			commands.add(command);
		}
		else {
			echo("Chat command already registered: " + command.getCommandName());
		}
	}
	/**
	 * Unregister a command so it isn't added on server startup
	 *
	 * @param command
	 *        the {@link CommandBase} to be removed
	 */
	public static void unregisterCommand(CommandBase command) {
		if (commands.contains(command)) {
			echo("Unegistering chat command: " + command.getCommandName());
			commands.remove(command);
		}
		else {
			echo("Chat command already unregistered: " + command.getCommandName());
		}
	}
	@SuppressWarnings("javadoc")
	@EventHandler
	public static final void addCommandsToServer(FMLServerStartingEvent evt) {
		echo("Loading server commands");
		for (CommandBase command : commands) {
			evt.registerServerCommand(command);
			debug("Command registered to server: " + command.getCommandName());
		}
	}
}