package electroblob.wizardry.client.animation;

import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.spell.Grapple;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.InventoryUtils;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.util.EnumHandSide;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;

/**
 * An animation that is associated with a particular {@link net.minecraft.item.EnumAction}. This animation will display
 * when a player is using an item with that action.
 * @author Electroblob
 * @since Wizardry 4.3
 * @see electroblob.wizardry.item.SpellActions
 */
public abstract class ActionAnimation extends Animation {

	private final EnumAction action;

	public ActionAnimation(EnumAction action){
		super(action.name());
		this.action = action;
	}

	@Override
	public boolean shouldDisplay(EntityPlayer player, boolean firstPerson){
		return !firstPerson && player.getItemInUseMaxCount() > 0 && player.getActiveItemStack().getItemUseAction() == action;
	}

	/** Called from the client proxy to register all of wizardry's action animations. */
	public static void register(){

		PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT){
			@Override
			public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
				// Something (probably some sort of race condition) causes getActiveItemStack to sometimes be wrong
				if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;

				EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());

				float pitch = (float)Math.toRadians(player.prevRotationPitch + (player.rotationPitch - player.prevRotationPitch) * partialTicks);
				float yaw = (float)Math.toRadians(player.prevRotationYawHead + (player.rotationYawHead - player.prevRotationYawHead) * partialTicks)
						- (float)Math.toRadians(player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks);

				float x = -((float)Math.PI / 2) + pitch + 0.2f;
				float y = (side == EnumHandSide.RIGHT ? -0.25f : 0.25f) + yaw;

				ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
				arm.setRotation(x, y, 0);
			}
		});

		PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_UP){
			@Override
			public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
				if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
				EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
				ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
				arm.addRotation(-2.2f, side == EnumHandSide.RIGHT ? 0.2f : -0.2f, 0);
			}
		});

		PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_DOWN){
			@Override
			public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
				float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
				float y = Math.min(0.4f + tick * 0.05f, 0.7f);
				EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
				ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
				arm.addRotation(-0.2f, side == EnumHandSide.RIGHT ? y : -y, 0);
			}
		});

		PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.SUMMON){
			@Override
			public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
				float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
				float x = -Math.min(0.4f + tick * 0.2f, 2f);
				((ModelRendererExtended)getArmForSide(model, EnumHandSide.RIGHT)).addRotation(x, 1.2f, 0);
				((ModelRendererExtended)getArmForSide(model, EnumHandSide.LEFT)).addRotation(x, -1.2f, 0);
			}
		});

		PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.GRAPPLE){
			@Override
			public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){

				WizardData data = WizardData.get(player);
				RayTraceResult hit = data.getVariable(Grapple.TARGET_KEY);
				if(hit == null || hit.typeOfHit == RayTraceResult.Type.MISS) return;
				Vec3d target = hit.hitVec;

				if(hit.entityHit instanceof EntityLivingBase){
					// If the target is an entity, we need to use the entity's centre rather than the original hit position
					// because the entity will have moved!
					target = GeometryUtils.getCentre(hit.entityHit);
				}

				EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());

				ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);

				Vec3d direction = target.subtract(player.getPositionEyes(partialTicks));
				float yaw = player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks;

				float pitch = (float)MathHelper.atan2(MathHelper.sqrt(direction.x*direction.x + direction.z*direction.z), direction.y);
				float x = pitch - (float)Math.PI * 0.9f;
				float y = -(float)Math.toRadians(yaw) - (float)MathHelper.atan2(direction.x, direction.z);
				y += (side == EnumHandSide.RIGHT ? -0.25f : 0.25f);
				if(Math.abs(pitch) < 0.2f) y = arm.rotateAngleY;

				arm.setRotation(x, y, 0);
			}
		});

		PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.IMBUE){
			@Override
			public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
				float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
				float z = Math.max(1.5f - tick * 0.1f, 0.8f);
				EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
				ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
				arm.addRotation(-1.2f, side == EnumHandSide.RIGHT ? -0.2f : 0.2f, side == EnumHandSide.RIGHT ? z : -z);
				if(!player.getHeldItem(InventoryUtils.getHandForSide(player, side.opposite())).isEmpty()){
					((ModelRendererExtended)getArmForSide(model, side.opposite())).addRotation(-0.8f, side == EnumHandSide.LEFT ? 0.3f : -0.3f, 0);
				}
			}
		});

		PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.THRUST){
			@Override
			public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
				if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
				EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
				ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
				float y = side == EnumHandSide.RIGHT ? -0.6f : 0.6f;
				arm.addRotation(-1.2f, y, 0);
				if(player.getHeldItem(InventoryUtils.getHandForSide(player, side.opposite())).isEmpty()){
					ModelRendererExtended otherArm = (ModelRendererExtended)getArmForSide(model, side.opposite());
					otherArm.setRotation(arm.rotateAngleX - 1.2f, -arm.rotateAngleY - y, otherArm.rotateAngleZ);
				}
			}
		});

	}

}
