package nivia.modules.exploits;

import net.minecraft.network.play.client.C03PacketPlayer;
import net.minecraft.network.play.client.C0BPacketEntityAction;
import net.minecraft.network.play.server.S08PacketPlayerPosLook;
import net.minecraft.util.BlockPos;
import org.lwjgl.input.Keyboard;
import nivia.Pandora;
import nivia.commands.Command;
import nivia.events.EventTarget;
import nivia.events.events.*;
import nivia.managers.PropertyManager.Property;
import nivia.modules.Module;
import nivia.modules.movement.NoVelocity;
import nivia.utils.Helper;
import nivia.utils.Logger;
import nivia.utils.utils.Timer;

public class Phase extends Module {

	Timer timer = new Timer();
	private BlockPos endPos;
	public Property<PhaseMode> mode = new Property<PhaseMode>(this, "Mode", PhaseMode.Normal);
	int delay;

	public Phase() {
		super("Phase", Keyboard.KEY_V, 0xE6B800, Category.EXPLOITS, "Exploits NCP to glitch through thin blocks",
				new String[] { "phse", "pase", "faze", "noclip" }, true);
	}

	public enum PhaseMode {
		Spider, Skip, vPhase, Normal, Weird, Silent, Vanilla, Motion, NoClip, NewClip, Teleport, Spartan, Bypass, NCP
	}

	@EventTarget
	public void onSend(EventPacketSend e) {
		if (mode.value != PhaseMode.Motion)
			return;
		double multiplier = 0.2;
		double mx = Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F));
		double mz = Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F));
		double x = (mc.thePlayer.movementInput.moveForward * multiplier * mx
				+ mc.thePlayer.movementInput.moveStrafe * multiplier * mz);
		double z = (mc.thePlayer.movementInput.moveForward * multiplier * mz
				- mc.thePlayer.movementInput.moveStrafe * multiplier * mx);

		if (e.getPacket() instanceof C03PacketPlayer) {
			if (mc.thePlayer.isCollidedHorizontally)
				delay++;
			C03PacketPlayer player = (C03PacketPlayer) e.getPacket();
			if (Helper.blockUtils().isInsideBlock() && delay >= 2) {
				player.x += x;
				player.z += z;
				player.y--;
				delay = 0;
			}
		}
	}

	@EventTarget
	public void onPre(EventPreMotionUpdates pre) {
	setSuffix(mode.value.toString());
		double multiplier = 0.05;

		if (Helper.blockUtils().isInsideBlock())
			multiplier = 1.2;

		double mx = Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F));
		double mz = Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F));
		double x = (mc.thePlayer.movementInput.moveForward * multiplier * mx + mc.thePlayer.movementInput.moveStrafe * multiplier * mz);
		double z = (mc.thePlayer.movementInput.moveForward * multiplier * mz - mc.thePlayer.movementInput.moveStrafe * multiplier * mx);

		switch (mode.value) {
		case Teleport:
			while(Helper.blockUtils().isInsideBlock() && mc.thePlayer.isSneaking())
				mc.thePlayer.boundingBox.offsetAndUpdate(0.01D * Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)), 0.0D, 0.01D * Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)));
			break;
		case Spartan:
			if (Helper.blockUtils().isInsideBlock() && Helper.playerUtils().MovementInput() && mc.thePlayer.isSneaking())
				mc.thePlayer.boundingBox.offsetAndUpdate(2.0D * Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)), 0.0D, 2.0D * Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)));
			break;
		case NewClip:
			if (Helper.blockUtils().isInsideBlock() && Helper.playerUtils().MovementInput() && mc.thePlayer.isSneaking())
				mc.thePlayer.boundingBox.offsetAndUpdate(0.82D * Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)), 0.0D, 0.82D * Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)));
			break;
		case NoClip:
			if (Helper.blockUtils().isInsideBlock() && Helper.playerUtils().MovementInput() && mc.thePlayer.isSneaking())
				mc.thePlayer.boundingBox.offsetAndUpdate(x, 0.0, z);
			break;
		default:
			break;
		}
	}

	@EventTarget
	public void onPost(EventPostMotionUpdates event) {
		if (mode.value.equals(PhaseMode.Vanilla) && delay == 1) {
			Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY - 2, mc.thePlayer.posZ, true));
			Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, true));

			delay = 0;
		}
		double multiplier = 0.163;
		double mx = Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F));
		double mz = Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F));
		if (mode.value == PhaseMode.Weird)
			multiplier = 0.31;
		if (mode.value == PhaseMode.Silent)
			multiplier = 0.2;
		if (mode.value == PhaseMode.vPhase)
			multiplier = 0.0624;
		double x = (mc.thePlayer.movementInput.moveForward * multiplier * mx + mc.thePlayer.movementInput.moveStrafe * multiplier * mz);
		double z = (mc.thePlayer.movementInput.moveForward * multiplier * mz - mc.thePlayer.movementInput.moveStrafe * multiplier * mx);
		
		

		switch (mode.value) {
			case vPhase:
				if(!mc.thePlayer.isCollidedHorizontally)
					break;

				Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(this.mc.thePlayer.posX + x, -8E307, this.mc.thePlayer.posZ + z, true));
				Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(this.mc.thePlayer.posX + x, mc.thePlayer.posY, this.mc.thePlayer.posZ + z, true));
				mc.thePlayer.setPosition(mc.thePlayer.posX + x * 4, mc.thePlayer.posY, mc.thePlayer.posZ + z * 4);
				Helper.sendPacket(new C0BPacketEntityAction(mc.thePlayer, C0BPacketEntityAction.Action.STOP_SNEAKING));
				break;
			case Weird:
				if ((mc.thePlayer.isCollidedHorizontally && !mc.thePlayer.isOnLadder()
						&& !Helper.blockUtils().isInsideBlock())) {
					Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + (x),
							mc.thePlayer.posY, mc.thePlayer.posZ + (z), false));
					for (int i = 1; i < 11; i++)
						Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY + Double.MAX_VALUE,
								mc.thePlayer.posZ, false));
					Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY,
							mc.thePlayer.posZ, true));
				}
				break;
			case NCP:
				break;
			case Normal:
				if ((mc.thePlayer.isCollidedHorizontally && !mc.thePlayer.isOnLadder()
						&& !Helper.blockUtils().isInsideBlock())) {
					Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + (x),
							mc.thePlayer.posY, mc.thePlayer.posZ + (z), false));
					Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX,
							mc.thePlayer.posY - (Helper.blockUtils().isOnLiquid() ? 9000 : (0.5)), mc.thePlayer.posZ,
							false));
					// Helper.sendPacket(new
					// C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX +
					// x, mc.thePlayer.posY - (Helper.blockUtils().isOnLiquid() ?
					// 9000 : (0.5)), mc.thePlayer.posZ + z, false));
					// mc.thePlayer.setPosition(mc.thePlayer.posX + x,
					// mc.thePlayer.posY, mc.thePlayer.posZ + z);
				}
				break;
			case Silent:
				if ((mc.thePlayer.isCollidedHorizontally && !mc.thePlayer.isOnLadder()
						&& !Helper.blockUtils().isInsideBlock())) {
					Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(this.mc.thePlayer.posX + x,
							this.mc.thePlayer.posY, this.mc.thePlayer.posZ + z, false));
					for (int i = 0; i < 5; i++) {
						Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(this.mc.thePlayer.posX, -8E307,
								this.mc.thePlayer.posZ, false));
						mc.thePlayer.setPosition(mc.thePlayer.posX + (x * 0.75), mc.thePlayer.posY,
								mc.thePlayer.posZ + (z * 0.75));
					}
					// mc.thePlayer.setPosition(mc.thePlayer.posX + x,
					// mc.thePlayer.posY, mc.thePlayer.posZ + z);
				}
				break;
			case Skip:
				if (!mc.thePlayer.isCollidedHorizontally)
					return;
				this.mc.thePlayer.motionX *= 0.5D;
				this.mc.thePlayer.motionZ *= 0.5D;
				double[] OPOP = new double[]{-0.02500000037252903D, -0.028571428997176036D, -0.033333333830038704D,
						-0.04000000059604645D, -0.05000000074505806D, -0.06666666766007741D, -0.10000000149011612D, 0.0,
						-0.20000000298023224D, -0.04000000059604645D, -0.033333333830038704D, -0.028571428997176036D,
						-0.02500000037252903D};
				for (int i = 0; i < OPOP.length; ++i) {
					Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(this.mc.thePlayer.posX,
							this.mc.thePlayer.posY + OPOP[i], this.mc.thePlayer.posZ, false));
					Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(this.mc.thePlayer.posX + ((x) * i),
							this.mc.thePlayer.boundingBox.minY, this.mc.thePlayer.posZ + ((z) * i), false));
				}
				Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(this.mc.thePlayer.posX, mc.thePlayer.posY,
						this.mc.thePlayer.posZ, true));
				this.mc.thePlayer.setPosition(this.mc.thePlayer.posX + (x), this.mc.thePlayer.posY,
						this.mc.thePlayer.posZ + (z));
				Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(this.mc.thePlayer.posX,
						mc.thePlayer.boundingBox.minY, this.mc.thePlayer.posZ, false));
				break;
			case Bypass:
				   if ((mc.thePlayer.isCollidedHorizontally && !mc.thePlayer.isOnLadder())) {
				    Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(Helper.player().posX + x,
				      Helper.player().posY, Helper.player().posZ + z, false));
				    for (int i = 1; i < 10; i++) {
				     Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(Helper.player().posX,
				       8.988465674311579E307D, Helper.player().posZ, false));
				    }
				    Helper.player().setPosition(Helper.player().posX + x, Helper.player().posY, Helper.player().posZ + z);
				   }
				   break;
			case Spider:
				if (!Helper.blockUtils().isInsideBlock())
					break;
				mc.thePlayer.posY += 0.1;
				mc.thePlayer.motionY = 0.065;
				mc.thePlayer.resetHeight();
				break;
		
			default:
				break;
		}
	}
   
	@EventTarget
	public void onPress(EventKeyPress key) {
		if (key.getEventKey() == 37 && mode.value.equals(PhaseMode.Vanilla)) {
			Pandora.getModManager().getModule(NoVelocity.class).setState(true);
			Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY - 2,
					mc.thePlayer.posZ, true));
			Helper.sendPacket(new C03PacketPlayer.C04PacketPlayerPosition(Double.POSITIVE_INFINITY,
					Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, true));
			if (Helper.blockUtils().isInsideBlock())
				mc.thePlayer.capabilities.isFlying = true;
			delay = 2;
		}
	}


	protected void onDisable() {
		super.onDisable();
		mc.thePlayer.capabilities.isFlying = false;
	}

	@EventTarget
	public void onReceive(EventPacketReceive e) {
		if (e.getPacket() instanceof S08PacketPlayerPosLook && this.delay == 2)
			this.delay = 1;
	}
	
	@EventTarget
	public void onBB(EventBoundingBox bb) {
		if (mc.thePlayer == null)return;
		
		if (mode.value.equals(PhaseMode.Vanilla)) {
			if (Helper.blockUtils().isInsideBlock()) {
				bb.setBoundingBox(null);
				mc.thePlayer.noClip = true;
				return;
			}
		}
		
		if (mode.value.equals(PhaseMode.NoClip) || mode.value.equals(PhaseMode.Spartan) || mode.value.equals(PhaseMode.NewClip) || mode.value.equals(PhaseMode.Teleport)) {
			if (mc.thePlayer.isSneaking()) {
				mc.thePlayer.noClip = true;
				if (bb.getY() > mc.thePlayer.posY + (Helper.blockUtils().isInsideBlock() ? 0 : 1))
					bb.setBoundingBox(null);
				if (mc.thePlayer.isCollidedHorizontally && bb.getY() > mc.thePlayer.boundingBox.minY - 0.4D)
					bb.setBoundingBox(null);
			}
		} else if (mode.value.equals(PhaseMode.NCP) && mc.thePlayer.isSneaking()) {
		    if (bb.getBoundingBox().minY >= mc.thePlayer.posY) {
                        bb.setBoundingBox(null);
                        mc.thePlayer.jump();
                    }
		} else {
			mc.thePlayer.noClip = true;
			if (bb.getY() > mc.thePlayer.posY + (Helper.blockUtils().isInsideBlock() ? 0 : 1))
				bb.setBoundingBox(null);
			if (mc.thePlayer.isCollidedHorizontally && bb.getY() > mc.thePlayer.boundingBox.minY - 0.4D)
				bb.setBoundingBox(null);
		}
	}

	protected void addCommand() {
		Pandora.getCommandManager().cmds
				.add(new Command("Phase", "Manages Phase modes",
						Logger.LogExecutionFail("Option, Options:", new String[] { "Spider", "Skip", "vPhase", "Normal","Weird", "Silent", "Motion", "NoClip", "Vanilla", "Teleport", "NCP",  "Spartan", "NewClip", "Bypass", "Values" }),"noclip") {
					@Override
					public void execute(String commandName, String[] arguments) {

						String message = arguments[1];
						switch (message.toLowerCase()) {
						case "spider":
						case "sp":
							mode.value = PhaseMode.Spider;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "teleport":
						case "tele":
							mode.value = PhaseMode.Teleport;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "skip":
						case "hcf":
							mode.value = PhaseMode.Skip;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "ncp":
						case "nocheatplus":
							mode.value = PhaseMode.NCP;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "motion":
						case "m":
							mode.value = PhaseMode.Motion;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "vphase":
							mode.value = PhaseMode.vPhase;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "normal":
						case "norm":
							mode.value = PhaseMode.Normal;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "weird":
						case "w":
							mode.value = PhaseMode.Weird;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "vanilla":
						case "v":
							mode.value = PhaseMode.Vanilla;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "silent":
						case "s":
							mode.value = PhaseMode.Silent;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "spartan":
						case "spa":
							mode.value = PhaseMode.Spartan;
							Logger.logChat("Only works when sneaking");
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "bypass":
						case "byp":
							mode.value = PhaseMode.Bypass;
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "newclip":
						case "nclip":
							mode.value = PhaseMode.NewClip;
							Logger.logChat("Only works when sneaking");
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "noclip":
						case "nc":
						case "noc":
							mode.value = PhaseMode.NoClip;
							Logger.logChat("Only works when sneaking");
							Logger.logSetMessage("Phase", "Mode", mode);
							break;
						case "values":
						case "actual":
							logValues();
							break;
						default:
							Logger.logChat(this.getError());
							break;
						}

					}
				});
	}

}