/*
 * Copyright (C) 2017 - 2019 | Wurst-Imperium | All rights reserved.
 *
 * This source code is subject to the terms of the GNU General Public
 * License, version 3. If a copy of the GPL was not distributed with this
 * file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
 */
package net.wurstclient.forge.hacks;

import java.lang.reflect.Method;

import org.lwjgl.opengl.GL11;

import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.entity.projectile.EntityFishHook;
import net.minecraft.init.Enchantments;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.ItemFishingRod;
import net.minecraft.item.ItemStack;
import net.minecraft.network.play.server.SPacketSoundEffect;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.wurstclient.fmlevents.WPacketInputEvent;
import net.wurstclient.fmlevents.WUpdateEvent;
import net.wurstclient.forge.Category;
import net.wurstclient.forge.Hack;
import net.wurstclient.forge.compatibility.WEnchantments;
import net.wurstclient.forge.compatibility.WItem;
import net.wurstclient.forge.compatibility.WMinecraft;
import net.wurstclient.forge.compatibility.WVec3d;
import net.wurstclient.forge.settings.CheckboxSetting;
import net.wurstclient.forge.settings.SliderSetting;
import net.wurstclient.forge.settings.SliderSetting.ValueDisplay;
import net.wurstclient.forge.utils.ChatUtils;
import net.wurstclient.forge.utils.PlayerControllerUtils;
import net.wurstclient.forge.utils.RenderUtils;

public final class AutoFishHack extends Hack
{
	private final SliderSetting validRange = new SliderSetting("Valid range",
		"Any bites that occur outside of this range\n" + "will be ignored.\n\n"
			+ "Increase your range if bites are not being\n"
			+ "detected, decrease it if other people's\n"
			+ "bites are being detected as yours.",
		1.5, 0.25, 8, 0.25, ValueDisplay.DECIMAL);
	private CheckboxSetting debugDraw = new CheckboxSetting("Debug draw",
		"Shows where bites are occurring and where\n"
			+ "they will be detected. Useful for optimizing\n"
			+ "your 'Valid range' setting.",
		false);
	
	private int timer;
	private Vec3d lastSoundPos;
	private int box;
	private int cross;
	
	public AutoFishHack()
	{
		super("AutoFish", "Automatically catches fish using your\n"
			+ "best fishing rod. If it finds a better\n"
			+ "rod while fishing, it will automatically\n" + "switch to it.");
		setCategory(Category.OTHER);
		addSetting(validRange);
		addSetting(debugDraw);
	}
	
	@Override
	protected void onEnable()
	{
		timer = 0;
		lastSoundPos = null;
		
		box = GL11.glGenLists(1);
		
		cross = GL11.glGenLists(1);
		GL11.glNewList(cross, GL11.GL_COMPILE);
		GL11.glColor4f(1, 0, 0, 0.5F);
		GL11.glBegin(GL11.GL_LINES);
		GL11.glVertex3d(-0.125, 0, -0.125);
		GL11.glVertex3d(0.125, 0, 0.125);
		GL11.glVertex3d(0.125, 0, -0.125);
		GL11.glVertex3d(-0.125, 0, 0.125);
		GL11.glEnd();
		GL11.glEndList();
		
		MinecraftForge.EVENT_BUS.register(this);
	}
	
	@Override
	protected void onDisable()
	{
		MinecraftForge.EVENT_BUS.unregister(this);
		GL11.glDeleteLists(box, 1);
		GL11.glDeleteLists(cross, 1);
	}
	
	@SubscribeEvent
	public void onUpdate(WUpdateEvent event)
	{
		// update range box
		if(debugDraw.isChecked())
		{
			GL11.glNewList(box, GL11.GL_COMPILE);
			AxisAlignedBB box = new AxisAlignedBB(-validRange.getValue(),
				-1 / 16.0, -validRange.getValue(), validRange.getValue(),
				1 / 16.0, validRange.getValue());
			GL11.glColor4f(1, 0, 0, 0.5F);
			GL11.glBegin(GL11.GL_LINES);
			RenderUtils.drawOutlinedBox(box);
			GL11.glEnd();
			GL11.glEndList();
		}
		
		EntityPlayerSP player = event.getPlayer();
		InventoryPlayer inventory = player.inventory;
		
		if(timer < 0)
		{
			PlayerControllerUtils.windowClick_PICKUP(-timer);
			timer = 15;
			return;
		}
		
		int bestRodValue =
			getRodValue(inventory.getStackInSlot(inventory.currentItem));
		int bestRodSlot = bestRodValue > -1 ? inventory.currentItem : -1;
		
		// search inventory for better rod
		for(int slot = 0; slot < 36; slot++)
		{
			ItemStack stack = inventory.getStackInSlot(slot);
			int rodValue = getRodValue(stack);
			
			if(rodValue > bestRodValue)
			{
				bestRodValue = rodValue;
				bestRodSlot = slot;
			}
		}
		
		if(bestRodSlot == inventory.currentItem)
		{
			// wait for timer
			if(timer > 0)
			{
				timer--;
				return;
			}
			
			// cast rod
			if(player.fishEntity == null)
				rightClick();
			
			return;
		}
		
		if(bestRodSlot == -1)
		{
			ChatUtils.message("Out of fishing rods.");
			setEnabled(false);
			return;
		}
		
		if(bestRodSlot < 9)
		{
			inventory.currentItem = bestRodSlot;
			return;
		}
		
		// place rod in hotbar
		int firstEmptySlot = inventory.getFirstEmptyStack();
		if(firstEmptySlot != -1)
		{
			if(firstEmptySlot >= 9)
				PlayerControllerUtils
					.windowClick_QUICK_MOVE(36 + inventory.currentItem);
			
			PlayerControllerUtils.windowClick_QUICK_MOVE(bestRodSlot);
			
		}else
		{
			PlayerControllerUtils.windowClick_PICKUP(bestRodSlot);
			PlayerControllerUtils
				.windowClick_PICKUP(36 + inventory.currentItem);
			timer = -bestRodSlot;
		}
	}
	
	@SubscribeEvent
	public void onPacketInput(WPacketInputEvent event)
	{
		EntityPlayerSP player = WMinecraft.getPlayer();
		if(player == null || player.fishEntity == null)
			return;
		
		if(!(event.getPacket() instanceof SPacketSoundEffect))
			return;
		
		// check sound type
		SPacketSoundEffect sound = (SPacketSoundEffect)event.getPacket();
		if(!SoundEvents.ENTITY_BOBBER_SPLASH.equals(sound.getSound()))
			return;
		
		if(debugDraw.isChecked())
			lastSoundPos = new Vec3d(sound.getX(), sound.getY(), sound.getZ());
		
		// check position
		EntityFishHook bobber = player.fishEntity;
		if(Math.abs(sound.getX() - bobber.posX) > validRange.getValue()
			|| Math.abs(sound.getZ() - bobber.posZ) > validRange.getValue())
			return;
		
		// catch fish
		rightClick();
	}
	
	@SubscribeEvent
	public void onRenderWorldLast(RenderWorldLastEvent event)
	{
		if(!debugDraw.isChecked())
			return;
		
		// GL settings
		GL11.glEnable(GL11.GL_BLEND);
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
		GL11.glEnable(GL11.GL_LINE_SMOOTH);
		GL11.glLineWidth(2);
		GL11.glDisable(GL11.GL_TEXTURE_2D);
		GL11.glEnable(GL11.GL_CULL_FACE);
		GL11.glDisable(GL11.GL_DEPTH_TEST);
		
		GL11.glPushMatrix();
		GL11.glTranslated(-TileEntityRendererDispatcher.staticPlayerX,
			-TileEntityRendererDispatcher.staticPlayerY,
			-TileEntityRendererDispatcher.staticPlayerZ);
		
		EntityFishHook bobber = WMinecraft.getPlayer().fishEntity;
		if(bobber != null)
		{
			GL11.glPushMatrix();
			GL11.glTranslated(bobber.posX, bobber.posY, bobber.posZ);
			GL11.glCallList(box);
			GL11.glPopMatrix();
		}
		
		if(lastSoundPos != null)
		{
			GL11.glPushMatrix();
			GL11.glTranslated(WVec3d.getX(lastSoundPos),
				WVec3d.getY(lastSoundPos), WVec3d.getZ(lastSoundPos));
			GL11.glCallList(cross);
			GL11.glPopMatrix();
		}
		
		GL11.glPopMatrix();
		
		// GL resets
		GL11.glColor4f(1, 1, 1, 1);
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GL11.glEnable(GL11.GL_TEXTURE_2D);
		GL11.glDisable(GL11.GL_BLEND);
		GL11.glDisable(GL11.GL_LINE_SMOOTH);
	}
	
	private int getRodValue(ItemStack stack)
	{
		if(WItem.isNullOrEmpty(stack)
			|| !(stack.getItem() instanceof ItemFishingRod))
			return -1;
		
		int luckOTSLvl = EnchantmentHelper
			.getEnchantmentLevel(Enchantments.LUCK_OF_THE_SEA, stack);
		int lureLvl =
			EnchantmentHelper.getEnchantmentLevel(Enchantments.LURE, stack);
		int unbreakingLvl = EnchantmentHelper
			.getEnchantmentLevel(Enchantments.UNBREAKING, stack);
		int mendingBonus =
			EnchantmentHelper.getEnchantmentLevel(Enchantments.MENDING, stack);
		int noVanishBonus = WEnchantments.hasVanishingCurse(stack) ? 0 : 1;
		
		return luckOTSLvl * 9 + lureLvl * 9 + unbreakingLvl * 2 + mendingBonus
			+ noVanishBonus;
	}
	
	private void rightClick()
	{
		// check held item
		ItemStack stack = WMinecraft.getPlayer().inventory.getCurrentItem();
		if(WItem.isNullOrEmpty(stack)
			|| !(stack.getItem() instanceof ItemFishingRod))
			return;
		
		// right click
		try
		{
			Method rightClickMouse = mc.getClass().getDeclaredMethod(
				wurst.isObfuscated() ? "func_147121_ag" : "rightClickMouse");
			rightClickMouse.setAccessible(true);
			rightClickMouse.invoke(mc);
			
		}catch(ReflectiveOperationException e)
		{
			setEnabled(false);
			throw new RuntimeException(e);
		}
		
		// reset timer
		timer = 15;
	}
}