package calclavia.lib;

import java.util.Iterator;
import java.util.List;
import net.minecraft.entity.Entity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import universalelectricity.core.vector.Vector3;

public class CalculationHelper {

   public static void rotateByAngle(Vector3 vector, double yaw) {
      double yawRadians = Math.toRadians(yaw);
      double x = vector.x;
      double z = vector.z;
      if(yaw != 0.0D) {
         vector.x = x * Math.cos(yawRadians) - z * Math.sin(yawRadians);
         vector.z = x * Math.sin(yawRadians) + z * Math.cos(yawRadians);
      }

   }

   public static void rotateByAngle(Vector3 vector, double yaw, double pitch) {
      rotateByAngle(vector, yaw, pitch, 0.0D);
   }

   public static void rotateByAngle(Vector3 vector, double yaw, double pitch, double roll) {
      double yawRadians = Math.toRadians(yaw);
      double pitchRadians = Math.toRadians(pitch);
      double rollRadians = Math.toRadians(roll);
      double x = vector.x;
      double y = vector.y;
      double z = vector.z;
      vector.x = x * Math.cos(yawRadians) * Math.cos(pitchRadians) + z * (Math.cos(yawRadians) * Math.sin(pitchRadians) * Math.sin(rollRadians) - Math.sin(yawRadians) * Math.cos(rollRadians)) + y * (Math.cos(yawRadians) * Math.sin(pitchRadians) * Math.cos(rollRadians) + Math.sin(yawRadians) * Math.sin(rollRadians));
      vector.z = x * Math.sin(yawRadians) * Math.cos(pitchRadians) + z * (Math.sin(yawRadians) * Math.sin(pitchRadians) * Math.sin(rollRadians) + Math.cos(yawRadians) * Math.cos(rollRadians)) + y * (Math.sin(yawRadians) * Math.sin(pitchRadians) * Math.cos(rollRadians) - Math.cos(yawRadians) * Math.sin(rollRadians));
      vector.y = -x * Math.sin(pitchRadians) + z * Math.cos(pitchRadians) * Math.sin(rollRadians) + y * Math.cos(pitchRadians) * Math.cos(rollRadians);
   }

   public static Vector3 getDeltaPositionFromRotation(float rotationYaw, float rotationPitch) {
      rotationYaw += 90.0F;
      rotationPitch = -rotationPitch;
      return new Vector3(Math.cos(Math.toRadians((double)rotationYaw)), Math.sin(Math.toRadians((double)rotationPitch)), Math.sin(Math.toRadians((double)rotationYaw)));
   }

   public static MovingObjectPosition raytraceEntities(World world, Vector3 startPosition, float rotationYaw, float rotationPitch, boolean collisionFlag, double reachDistance) {
      MovingObjectPosition pickedEntity = null;
      Vec3 startingPosition = startPosition.toVec3();
      Vec3 look = getDeltaPositionFromRotation(rotationYaw, rotationPitch).toVec3();
      Vec3 reachPoint = Vec3.createVectorHelper(startingPosition.xCoord + look.xCoord * reachDistance, startingPosition.yCoord + look.yCoord * reachDistance, startingPosition.zCoord + look.zCoord * reachDistance);
      double playerBorder = 1.1D * reachDistance;
      AxisAlignedBB boxToScan = AxisAlignedBB.getBoundingBox(-playerBorder, -playerBorder, -playerBorder, playerBorder, playerBorder, playerBorder);
      List entitiesHit = world.getEntitiesWithinAABBExcludingEntity((Entity)null, boxToScan);
      double closestEntity = reachDistance;
      if(entitiesHit != null && !entitiesHit.isEmpty()) {
         Iterator i$ = entitiesHit.iterator();

         while(i$.hasNext()) {
            Entity entityHit = (Entity)i$.next();
            if(entityHit != null && entityHit.canBeCollidedWith() && entityHit.boundingBox != null) {
               float border = entityHit.getCollisionBorderSize();
               AxisAlignedBB aabb = entityHit.boundingBox.expand((double)border, (double)border, (double)border);
               MovingObjectPosition hitMOP = aabb.calculateIntercept(startingPosition, reachPoint);
               if(hitMOP != null) {
                  if(aabb.isVecInside(startingPosition)) {
                     if(0.0D < closestEntity || closestEntity == 0.0D) {
                        pickedEntity = new MovingObjectPosition(entityHit);
                        if(pickedEntity != null) {
                           pickedEntity.hitVec = hitMOP.hitVec;
                           closestEntity = 0.0D;
                        }
                     }
                  } else {
                     double distance = startingPosition.distanceTo(hitMOP.hitVec);
                     if(distance < closestEntity || closestEntity == 0.0D) {
                        pickedEntity = new MovingObjectPosition(entityHit);
                        pickedEntity.hitVec = hitMOP.hitVec;
                        closestEntity = distance;
                     }
                  }
               }
            }
         }

         return pickedEntity;
      } else {
         return null;
      }
   }

   public static MovingObjectPosition raytraceBlocks(World world, Vector3 startPosition, float rotationYaw, float rotationPitch, boolean collisionFlag, double reachDistance) {
      Vector3 lookVector = getDeltaPositionFromRotation(rotationYaw, rotationPitch);
      Vector3 reachPoint = Vector3.add(startPosition, Vector3.multiply(lookVector, reachDistance));
      return world.rayTraceBlocks(startPosition.toVec3(), reachPoint.toVec3(), collisionFlag);
   }

   public static MovingObjectPosition doCustomRayTrace(World world, Vector3 startPosition, float rotationYaw, float rotationPitch, boolean collisionFlag, double reachDistance) {
      MovingObjectPosition pickedBlock = raytraceBlocks(world, startPosition, rotationYaw, rotationPitch, collisionFlag, reachDistance);
      MovingObjectPosition pickedEntity = raytraceEntities(world, startPosition, rotationYaw, rotationPitch, collisionFlag, reachDistance);
      if(pickedBlock == null) {
         return pickedEntity;
      } else if(pickedEntity == null) {
         return pickedBlock;
      } else {
         double dBlock = startPosition.distanceTo(new Vector3(pickedBlock.hitVec));
         double dEntity = startPosition.distanceTo(new Vector3(pickedEntity.hitVec));
         return dEntity < dBlock?pickedEntity:pickedBlock;
      }
   }
}
