/*
 *   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.slaves.laufbau;

import com.sk89q.worldedit.EditSession;
import de.steamwar.bausystem.features.slaves.laufbau.states.LaufbauState;
import de.steamwar.bausystem.features.slaves.laufbau.states.ProcessingTracesState;
import de.steamwar.bausystem.utils.WorldEditUtils;
import lombok.Getter;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;

import java.util.List;
import java.util.stream.Collectors;

public class Laufbau {

    private World world;
    private Location pos1;
    private Location pos2;

    private LaufbauState active;

    private List<BlockBoundingBox> elements;

    @Getter
    private EditSession editSession;

    private int factor;

    public Laufbau(Player player, Location pos1, Location pos2, boolean preferingBlastResistance) {
        this.world = pos1.getWorld();
        this.pos1 = new Location(world, Math.min(pos1.getBlockX(), pos2.getBlockX()), Math.min(pos1.getBlockY(), pos2.getBlockY()), Math.min(pos1.getBlockZ(), pos2.getBlockZ()));
        this.pos2 = new Location(world, Math.max(pos1.getBlockX(), pos2.getBlockX()), Math.max(pos1.getBlockY(), pos2.getBlockY()), Math.max(pos1.getBlockZ(), pos2.getBlockZ()));

        Vector selectionSize = pos2.toVector().subtract(pos1.toVector());
        int xFactor = (int) (Math.abs(selectionSize.getX()) / 12.375);
        int yFactor = (int) (Math.abs(selectionSize.getY()) / 7.125);
        int zFactor = (int) (Math.abs(selectionSize.getZ()) / 9.875);
        factor = Math.max(Math.max(xFactor, Math.max(yFactor, zFactor)), 8);

        editSession = WorldEditUtils.getEditSession(player);

        elements = BlockBoundingBox.elements.stream().filter(blockBoundingBox -> {
            return !LaufbauUtils.isDeactivated(player, blockBoundingBox);
        }).collect(Collectors.toList());
        if (preferingBlastResistance) {
            elements.sort((o1, o2) -> {
                int compared = Double.compare(o1.blockData.getMaterial().getBlastResistance(), o2.blockData.getMaterial().getBlastResistance());
                if (compared != 0) {
                    return -compared;
                }
                return -Double.compare(o1.volume(), o2.volume());
            });
        } else {
            elements.sort((o1, o2) -> {
                int compared = Double.compare(o1.volume(), o2.volume());
                if (compared != 0) {
                    return -compared;
                }
                return -Double.compare(o1.blockData.getMaterial().getBlastResistance(), o2.blockData.getMaterial().getBlastResistance());
            });
        }

        active = new ProcessingTracesState(world, this::inRegion, editSession, elements, factor);
    }

    private void createNextState() {
        if (active == null) return;
        active = active.getNextState();
    }

    public String actionBarMessage(Player p) {
        return active.actionBarMessage(p);
    }

    public boolean hasNext() {
        if (active == null) return false;
        return active.hasNext();
    }

    public void next() {
        LaufbauState state = active;
        state.next();
        if (!state.hasNext()) {
            createNextState();
        }
    }

    private boolean inRegion(Vector location, int expansion) {
        if (location.getBlockX() >= pos1.getBlockX() - expansion) {
            if (location.getBlockY() >= pos1.getBlockY() - expansion) {
                if (location.getBlockZ() >= pos1.getBlockZ() - expansion) {
                    if (location.getBlockX() <= pos2.getBlockX() + expansion) {
                        if (location.getBlockY() <= pos2.getBlockY() + expansion) {
                            if (location.getBlockZ() <= pos2.getBlockZ() + expansion) {
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }
}
