package dev.crucifixbeta.core.impl;

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.BlockBreakingProgressEvent;
import dev.crucifixbeta.api.events.impl.ClientTickEvent;
import dev.crucifixbeta.api.events.impl.PacketEvent;
import dev.crucifixbeta.api.events.impl.ServerConnectBeginEvent;
import dev.crucifixbeta.api.utils.Wrapper;
import dev.crucifixbeta.api.utils.math.FadeUtils;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.client.AntiCheat;
import dev.crucifixbeta.mod.modules.impl.player.PacketMine;
import dev.crucifixbeta.mod.modules.impl.render.BreakESP;
import java.util.concurrent.ConcurrentHashMap;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.packet.s2c.play.BlockBreakingProgressS2CPacket;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;

@Environment(EnvType.CLIENT)
public class BreakManager implements Wrapper {
   public final ConcurrentHashMap<Integer, BreakManager.BreakData> breakMap = new ConcurrentHashMap<>();
   public final ConcurrentHashMap<Integer, BreakManager.BreakData> doubleMap = new ConcurrentHashMap<>();

   public BreakManager() {
      CrucifixBeta.EVENT_BUS.subscribe(this);
   }

   @EventListener
   public void onServerConnectBegin(ServerConnectBeginEvent event) {
      this.breakMap.clear();
      this.doubleMap.clear();
   }

   @EventListener
   public void onTick(ClientTickEvent event) {
      if (!Module.nullCheck()) {
         if (AntiCheat.INSTANCE.detectDouble.getValue()) {
            for (int var3 : CrucifixBeta.BREAK.doubleMap.keySet()) {
               BreakManager.BreakData var4 = CrucifixBeta.BREAK.doubleMap.get(var3);
               if (var4 == null
                  || var4.getEntity() == null
                  || mc.world.isAir(var4.pos)
                  || var4.timer
                     .passedMs(Math.max(AntiCheat.INSTANCE.minTimeout.getValue() * 1000.0, var4.breakTime * AntiCheat.INSTANCE.doubleMineTimeout.getValue()))) {
                  CrucifixBeta.BREAK.doubleMap.remove(var3);
               }
            }
         }

         for (BreakManager.BreakData var6 : this.breakMap.values()) {
            var6.breakTime = Math.max(BreakESP.getBreakTime(var6.pos, false), 50.0);
            if (PacketMine.unbreakable(var6.pos)) {
               var6.fade.setLength(0L);
               var6.complete = false;
               var6.failed = true;
            } else if (mc.world.isAir(var6.pos)) {
               var6.fade.setLength(0L);
               var6.complete = true;
               var6.failed = false;
            } else if (!var6.complete && var6.timer.passedMs(var6.breakTime * AntiCheat.INSTANCE.breakTimeout.getValue())) {
               var6.fade.setLength(0L);
               var6.failed = true;
            } else {
               var6.fade.setLength((long)var6.breakTime);
            }
         }
      }
   }

   @EventListener
   public void onPacket(PacketEvent.Receive event) {
      if (!Module.nullCheck()) {
         if (event.getPacket() instanceof BlockBreakingProgressS2CPacket var3) {
            if (var3.getPos() == null) {
               return;
            }

            BreakManager.BreakData var4 = new BreakManager.BreakData(var3.getPos(), var3.getEntityId(), false);
            if (var4.getEntity() == null) {
               return;
            }

            if (MathHelper.sqrt((float)var4.getEntity().getEyePos().squaredDistanceTo(var3.getPos().toCenterPos())) > 8.0F) {
               return;
            }

            if (AntiCheat.INSTANCE.detectDouble.getValue() && var3.getProgress() != 255) {
               if (var3.getProgress() != 0) {
                  BreakManager.BreakData var7 = this.doubleMap.get(var3.getEntityId());
                  if (var7 != null) {
                     var7.pos = var3.getPos();
                     var7.timer.reset();
                  } else if (!PacketMine.unbreakable(var3.getPos())) {
                     this.doubleMap.put(var3.getEntityId(), new BreakManager.BreakData(var3.getPos(), var3.getEntityId(), true));
                  }

                  return;
               }

               BreakManager.BreakData var5 = this.doubleMap.get(var3.getEntityId());
               if (var5 != null && var5.pos.equals(var3.getPos()) && !var5.timer.passedS(150.0)) {
                  return;
               }
            }

            BreakManager.BreakData var6;
            if ((var6 = this.breakMap.get(var3.getEntityId())) != null && !var6.failed && var6.pos.equals(var3.getPos())) {
               return;
            }

            this.breakMap.put(var3.getEntityId(), var4);
            CrucifixBeta.EVENT_BUS.post(BlockBreakingProgressEvent.get(var3.getPos(), var3.getEntityId(), var3.getProgress()));
            if (AntiCheat.INSTANCE.detectDouble.getValue() && !this.doubleMap.containsKey(var3.getEntityId()) && !PacketMine.unbreakable(var3.getPos())) {
               this.doubleMap.put(var3.getEntityId(), new BreakManager.BreakData(var3.getPos(), var3.getEntityId(), true));
            }
         }
      }
   }

   public boolean isMining(BlockPos pos) {
      return this.isMining(pos, true);
   }

   public boolean isMining(BlockPos pos, boolean self) {
      if (self && PacketMine.getBreakPos() != null && PacketMine.getBreakPos().equals(pos)) {
         return true;
      } else {
         for (BreakManager.BreakData var4 : this.breakMap.values()) {
            if (var4.getEntity() != null && !(var4.getEntity().getEyePos().distanceTo(pos.toCenterPos()) > 7.0) && !var4.failed && var4.pos.equals(pos)) {
               return true;
            }
         }

         return false;
      }
   }

   @Environment(EnvType.CLIENT)
   public static class BreakData {
      public BlockPos pos;
      private final int entityId;
      public final FadeUtils fade;
      public final Timer timer;
      public double breakTime;
      public boolean failed = false;
      public boolean complete = false;

      public BreakData(BlockPos pos, int entityId, boolean extraBreak) {
         this.pos = pos;
         this.entityId = entityId;
         this.breakTime = Math.max(BreakESP.getBreakTime(pos, extraBreak), 50.0);
         this.fade = new FadeUtils((long)this.breakTime);
         this.timer = new Timer();
      }

      public Entity getEntity() {
         if (Wrapper.mc.world == null) {
            return null;
         } else {
            Entity var1 = Wrapper.mc.world.getEntityById(this.entityId);
            return var1 instanceof PlayerEntity ? var1 : null;
         }
      }
   }
}
