/*
 *   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.bausystem.features.design.endstone;

import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.Region;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;

import java.util.*;
import java.util.stream.Collectors;

public class DesignEndStone {

    private static final World WORLD = Bukkit.getWorlds().get(0);

    private int minX, minY, minZ, maxX, maxY, maxZ;
    private REntityServer entityServer = new REntityServer();
    private List<REntity> entities = new ArrayList<>();
    private Set<Location> locations = new HashSet<>();
    private Set<Material> limited;
    private boolean calculateFromBottom;

    public DesignEndStone(Region region) {
        this.minX = region.getBuildArea().getMinPoint(false).getX();
        this.minY = region.getBuildArea().getMinPoint(false).getY();
        this.minZ = region.getBuildArea().getMinPoint(false).getZ();
        this.maxX = region.getBuildArea().getMaxPoint(false).getX();
        this.maxY = region.getBuildArea().getMaxPoint(false).getY();
        this.maxZ = region.getBuildArea().getMaxPoint(false).getZ();

        limited = Arrays.stream(Material.values())
                .filter(Material::isBlock)
                .filter(material -> !material.isLegacy())
                .filter(material -> material.getBlastResistance() > region.getGameModeConfig().Schematic.MaxDesignBlastResistance)
                .collect(Collectors.toSet());
        calculateFromBottom = region.getGameModeConfig().Arena.NoFloor;

        entityServer.setCallback((player, rEntity, entityAction) -> {
            if (entityAction != REntityServer.EntityAction.ATTACK) return;
            Location location = new Location(WORLD, rEntity.getX(), rEntity.getY(), rEntity.getZ());
            Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
                location.getBlock().breakNaturally();
                calc();
            }, 1);
        });
    }

    public void calc() {
        entities.forEach(REntity::die);
        entities.clear();
        locations.clear();

        calc(minX, minY, minZ, maxX, maxY, minZ, 0, 0, 1, maxZ - minZ);
        calc(minX, minY, maxZ, maxX, maxY, maxZ, 0, 0, -1, maxZ - minZ);
        calc(minX, minY, minZ, minX, maxY, maxZ, 1, 0, 0, maxX - minX);
        calc(maxX, minY, minZ, maxX, maxY, maxZ, -1, 0, 0, maxX - minX);
        if (calculateFromBottom) {
            calc(minX, minY, minZ, maxX, minY, maxZ, 0, 1, 0, maxY - minY + 1);
        } else {
            int airBlocks = 0;
            double minAirBlocks = (maxX - minX) * (maxZ - minZ) * 0.1;
            for (int x = minX; x < maxX; x++) {
                for (int z = minZ; z < maxZ; z++) {
                    if (WORLD.getBlockAt(x, minY, z).getType().isAir()) {
                        airBlocks++;
                        if (airBlocks > minAirBlocks) break;
                    }
                }
            }
            if (airBlocks > minAirBlocks) {
                calc(minX, minY, minZ, maxX, minY, maxZ, 0, 1, 0, maxY - minY + 1);
            }
        }
        calc(minX, maxY, minZ, maxX, maxY, maxZ, 0, -1, 0, maxY - minY + 1);
    }

    private void calc(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, int dirX, int dirY, int dirZ, int steps) {
        for (int x = minX; x <= maxX; x++) {
            for (int y = minY; y <= maxY; y++) {
                for (int z = minZ; z <= maxZ; z++) {
                    for (int step = 0; step < steps; step++) {
                        int cx = x + step * dirX;
                        int cy = y + step * dirY;
                        int cz = z + step * dirZ;
                        Material material = WORLD.getBlockAt(cx, cy, cz).getType();

                        if (material != Material.WATER && material != Material.LAVA && limited.contains(material)) {
                            Location location = new Location(WORLD, cx + 0.5, cy, cz + 0.5);
                            if (!locations.add(location)) break;
                            RFallingBlockEntity entity = new RFallingBlockEntity(entityServer, location, Material.RED_STAINED_GLASS);
                            entity.setNoGravity(true);
                            entity.setGlowing(true);
                            entities.add(entity);
                            break;
                        } else if (!material.isAir() && material != Material.WATER && material != Material.LAVA) {
                            break;
                        }
                    }
                }
            }
        }
    }

    public void toggle(Player player) {
        if (entityServer.getPlayers().contains(player)) {
            entityServer.removePlayer(player);
            BauSystem.MESSAGE.sendPrefixless("DESIGN_ENDSTONE_DISABLE", player, ChatMessageType.ACTION_BAR);
        } else {
            entityServer.addPlayer(player);
            calc();
            BauSystem.MESSAGE.sendPrefixless("DESIGN_ENDSTONE_ENABLE", player, ChatMessageType.ACTION_BAR);
        }
    }

    public boolean removePlayer(Player player) {
        entityServer.removePlayer(player);
        return entityServer.getPlayers().isEmpty();
    }
}
