package cn.citycraft.TellRaw.internal;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.bukkit.Achievement;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.Statistic.Type;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import cn.citycraft.TellRaw.FancyMessage;
import cn.citycraft.TellRaw.common.Reflection;
import cn.citycraft.TellRaw.common.TextualComponent;

/**
 * Represents a formattable message. Such messages can use elements such as
 * colors, formatting codes, hover and click data, and other features provided
 * by the vanilla Minecraft <a
 * href="http://minecraft.gamepedia.com/Tellraw#Raw_JSON_Text">JSON message
 * formatter</a>.
 * This class allows plugins to emulate the functionality of the vanilla
 * Minecraft <a href="http://minecraft.gamepedia.com/Commands#tellraw">tellraw
 * command</a>.
 * <p>
 * This class follows the builder pattern, allowing for method chaining. It is
 * set up such that invocations of property-setting methods will affect the
 * current editing component, and a call to {@link #then()} or
 * {@link #then(Object)} will append a new editing component to the end of the
 * message, optionally initializing it with text. Further property-setting
 * method calls will affect that editing component.
 * </p>
 */
public class FancyMessageInternal extends FancyMessage {

	/**
	 * 新建自动序列化类
	 */
	public FancyMessageInternal() {
		this("");
	}

	/**
	 * 新建自动序列化类
	 *
	 * @param firstPartText
	 *            第一个文本串
	 */
	public FancyMessageInternal(final String firstPartText) {
		super(TextualComponent.rawText(firstPartText));
	}

	@Override
	public FancyMessage achievementTooltip(final Achievement which) {
		try {
			final Object achievement = getNMSAchievementMethod.invoke(null, which);
			return achievementTooltip((String) nmsAchievement_NameField.get(achievement));
		} catch (final IllegalAccessException e) {
			Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
			return this;
		} catch (final IllegalArgumentException e) {
			Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
			return this;
		} catch (final InvocationTargetException e) {
			Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e);
			return this;
		}
	}

	@Override
	public FancyMessage itemTooltip(final ItemStack itemStack) {
		try {
			final Object nmsItem = getOBCasNMSCopyMethod.invoke(null, itemStack);
			return itemTooltip(getNMSSaveNBTMethod.invoke(nmsItem, nmsNBTTagCompound.newInstance()).toString());
		} catch (final Exception e) {
			e.printStackTrace();
			return this;
		}
	}

	@Override
	public void send(final CommandSender sender, final String jsonString) {
		if (!(sender instanceof Player)) {
			sender.sendMessage(toOldMessageFormat());
			return;
		}
		final Player player = (Player) sender;
		try {
			final Object handle = Reflection.getHandle(player);
			if (nmsPlayerConnectionField == null) {
				try {
					nmsPlayerConnectionField = Reflection.getField(handle.getClass(), "playerConnection");
				} catch (final Exception e) {
					for (final Field field : handle.getClass().getDeclaredFields()) {
						if (field.getType().getSimpleName().contains("NetHandlerPlayServer")) {
							nmsPlayerConnectionField = field;
							break;
						}
					}
				}
			}
			final Object connection = nmsPlayerConnectionField.get(handle);
			if (nmsSendPacketMethod == null) {
				try {
					nmsSendPacketMethod = Reflection.getMethod(connection.getClass(), "sendPacket", nmsPacketClass);
				} catch (final Exception e) {
					nmsSendPacketMethod = Reflection.getMethodByParamsAndType(connection.getClass(), Void.TYPE, nmsPacketClass).get(0);
				}
			}
			if (nmsSendPacketMethod == null) {
				throw new RuntimeException("could find field: sendPacket in EntityPlayer class");
			}
			nmsSendPacketMethod.invoke(connection, createChatPacket(jsonString));
		} catch (final IllegalArgumentException e) {
			Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
		} catch (final IllegalAccessException e) {
			Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
		} catch (final InstantiationException e) {
			Bukkit.getLogger().log(Level.WARNING, "Underlying class is abstract.", e);
		} catch (final InvocationTargetException e) {
			Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e);
		} catch (final NoSuchMethodException e) {
			Bukkit.getLogger().log(Level.WARNING, "Could not find method.", e);
		} catch (final ClassNotFoundException e) {
			Bukkit.getLogger().log(Level.WARNING, "Could not find class.", e);
		} catch (final SecurityException ex) {
			Logger.getLogger(FancyMessageInternal.class.getName()).log(Level.SEVERE, null, ex);
		}
	}

	@Override
	public FancyMessage statisticTooltip(final Statistic which) {
		final Type type = which.getType();
		if (type != Type.UNTYPED) {
			throw new IllegalArgumentException("That statistic requires an additional " + type + " parameter!");
		}
		try {
			final Object statistic = getNMSStatisticMethod.invoke(null, which);
			return achievementTooltip((String) nmsStatistic_NameField.get(statistic));
		} catch (final IllegalAccessException e) {
			Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
			return this;
		} catch (final IllegalArgumentException e) {
			Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
			return this;
		} catch (final InvocationTargetException e) {
			Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e);
			return this;
		}
	}

	@Override
	public FancyMessage statisticTooltip(final Statistic which, final EntityType entity) {
		final Type type = which.getType();
		if (type == Type.UNTYPED) {
			throw new IllegalArgumentException("That statistic needs no additional parameter!");
		}
		if (type != Type.ENTITY) {
			throw new IllegalArgumentException("Wrong parameter type for that statistic - needs " + type + "!");
		}
		try {
			final Object statistic = getNMSEntityStatisticMethod.invoke(null, which, entity);
			return achievementTooltip((String) nmsStatistic_NameField.get(statistic));
		} catch (final IllegalAccessException e) {
			Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
			return this;
		} catch (final IllegalArgumentException e) {
			Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
			return this;
		} catch (final InvocationTargetException e) {
			Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e);
			return this;
		}
	}

	@Override
	public FancyMessage statisticTooltip(final Statistic which, final Material item) {
		final Type type = which.getType();
		if (type == Type.UNTYPED) {
			throw new IllegalArgumentException("That statistic needs no additional parameter!");
		}
		if ((type == Type.BLOCK && item.isBlock()) || type == Type.ENTITY) {
			throw new IllegalArgumentException("Wrong parameter type for that statistic - needs " + type + "!");
		}
		try {
			final Object statistic = getNMSMaterialStatisticMethod.invoke(null, which, item);
			return achievementTooltip((String) nmsStatistic_NameField.get(statistic));
		} catch (final IllegalAccessException e) {
			Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
			return this;
		} catch (final IllegalArgumentException e) {
			Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
			return this;
		} catch (final InvocationTargetException e) {
			Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e);
			return this;
		}
	}

	/**
	 * 创建Chat数据包
	 *
	 * @param json
	 *            需要创建的数据包
	 * @return 创建发包对象
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 */
	private Object createChatPacket(final String json) throws IllegalArgumentException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
		final Object serializedChatComponent = fromJsonMethod.invoke(nmsChatSerializerGsonInstance, json, nmsIChatBaseComponentClass);
		return nmsPacketPlayOutChatConstructor.newInstance(serializedChatComponent);
	}
}
