/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.fightsystem.listener;

import de.steamwar.Reflection;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentTask;
import de.steamwar.fightsystem.utils.WorldOfColorWrapper;
import de.steamwar.linkage.Linked;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.projectiles.ProjectileSource;
import org.bukkit.util.Vector;

@Linked
public class ArrowStopper {

    private static final Vector NULL_VECTOR = new Vector(0, 0, 0);
    private static final BlockFace[] BLOCK_FACES = {BlockFace.UP, BlockFace.DOWN, BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH};

    public ArrowStopper() {
        new StateDependentTask(Config.TechhiderActive, FightState.Running, this::run, 1, 1);
    }

    private static final Class<?> entityArrow = Reflection.getClass("net.minecraft.world.entity.projectile.AbstractArrow");
    private void run() {
        Recording.iterateOverEntities(entityArrow::isInstance, entity -> {
            Projectile arrow = (Projectile) entity;
            if (invalidEntity(arrow))
                return;

            Location prevLocation = arrow.getLocation().toVector().subtract(arrow.getVelocity()).toLocation(arrow.getWorld());
            if (arrow.getTicksLived() == 0){
                ProjectileSource projSource = arrow.getShooter();
                if(projSource instanceof Player)
                    prevLocation = ((Player) arrow.getShooter()).getEyeLocation();
                else
                    return;
            }
            if (checkBlocks(arrow.getLocation().getBlock(), prevLocation.getBlock())) {
                arrow.remove();
            }
        });
    }

    private boolean checkBlocks(Block start, Block end) {
        Block cursor = start;

        while (!cursor.getLocation().equals(end.getLocation())) {
            BlockFace nearest = BlockFace.SELF;
            double nearestDistance = cursor.getLocation().distance(end.getLocation());
            for (BlockFace face : BLOCK_FACES) {
                Block relative = cursor.getRelative(face);
                double distance = relative.getLocation().distance(end.getLocation());
                if(distance < nearestDistance) {
                    nearestDistance = distance;
                    nearest = face;
                }
            }
            cursor = cursor.getRelative(nearest);
            if(checkBlock(cursor))
                return true;
        }

        return false;
    }

    private boolean checkBlock(Block block) {
        return Config.HiddenBlocks.contains(block.getType());
    }

    private boolean invalidEntity(Projectile entity) {
        Location location = entity.getLocation();
        boolean teamFrom = entity.getVelocity().getZ() > 0;
        boolean overMid = location.getZ() > Config.SpecSpawn.getZ();
        boolean otherSide = teamFrom == overMid;
        return  otherSide || !Config.ArenaRegion.inRegion(location) ||
                WorldOfColorWrapper.impl.isInBlock(entity) ||
                entity.getVelocity().equals(NULL_VECTOR);
    }
}
