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

import de.steamwar.techhider.ProtocolUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;

import java.util.function.ObjIntConsumer;

@Getter
@AllArgsConstructor
public class Region {

    public static final Region EMPTY = Region.fromSize(-10000, -10000, -10000, 0, 0, 0);

    public static Region withExtension(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ, int extendX, int extendY, int extendZ) {
        return Region.fromSize(minX - extendX, minY - extendY, minZ - extendZ,
                sizeX + extendX * 2, sizeY + extendY * 2, sizeZ + extendZ * 2);
    }

    public static Region fromSize(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
        return new Region(minX, minY, minZ, minX+sizeX, minY+sizeY, minZ+sizeZ);
    }

    private final int minX;
    private final int minY;
    private final int minZ;
    private final int maxX;
    private final int maxY;
    private final int maxZ;

    public int getSizeX() {
        return maxX - minX;
    }

    public int getSizeY() {
        return maxY - minY;
    }

    public int getSizeZ() {
        return maxZ - minZ;
    }

    private int getMinChunkX(){
        return ProtocolUtils.posToChunk(minX);
    }

    private int getMaxChunkX(){
        return ProtocolUtils.posToChunk(maxX);
    }

    private int getMinChunkZ(){
        return ProtocolUtils.posToChunk(minZ);
    }

    private int getMaxChunkZ(){
        return ProtocolUtils.posToChunk(maxZ);
    }

    public Location getMinLocation(World world) {
        return new Location(world, minX, minY, minZ);
    }

    public Location getMaxLocation(World world) {
        return new Location(world, maxX, maxY, maxZ);
    }

    public boolean chunkOutside(int cX, int cZ) {
        return getMinChunkX() > cX || cX > getMaxChunkX() ||
               getMinChunkZ() > cZ || cZ > getMaxChunkZ();
    }

    public boolean chunkSectionOutside(int cX, int cY, int cZ) {
        return getMinChunkX() > cX || cX > getMaxChunkX() ||
                ProtocolUtils.posToChunk(minY) > cY || cY > ProtocolUtils.posToChunk(maxY) ||
                getMinChunkZ() > cZ || cZ > getMaxChunkZ();
    }

    public void forEachChunk(ObjIntConsumer<Integer> executor) {
        for(int x = getMinChunkX(); x <= getMaxChunkX(); x++)
            for(int z = getMinChunkZ(); z <= getMaxChunkZ(); z++)
                executor.accept(x, z);
    }

    public void forEach(TriConsumer<Integer, Integer, Integer> executor) {
        for(int x = minX; x < maxX; x++) {
            for(int y = minY; y < maxY; y++) {
                for (int z = minZ; z < maxZ; z++) {
                    executor.accept(x, y, z);
                }
            }
        }
    }

    public int volume(){
        return (maxX - minX) * (maxY - minY) * (maxZ - minZ);
    }

    public double centerX() {
        return (maxX - minX) / 2.0 + minX;
    }

    public double centerZ() {
        return (maxZ - minZ) / 2.0 + minZ;
    }

    public boolean in2dRegion(Location location){
        return minX <= location.getX() && location.getX() < maxX && minZ <= location.getZ() && location.getZ() <= maxZ;
    }

    public boolean inRegion(Location location){
        return in2dRegion(location) && minY < location.getY() && location.getY() < maxY;
    }

    public boolean playerInRegion(Location location){
        return in2dRegion(location) && minY <= location.getY() && location.getY() < maxY;
    }

    public boolean in2dRegion(Block block){
        return in2dRegion(block.getX(), block.getZ());
    }

    public boolean in2dRegion(int x, int z) {
        return minX <= x && x < maxX && minZ <= z && z < maxZ;
    }

    public boolean inRegion(Block block){
        return in2dRegion(block) && minY <= block.getY() && block.getY() < maxY;
    }

    public boolean inRegion(int x, int y, int z) {
        return in2dRegion(x, z) && minY <= y && y < maxY;
    }

    public Region intersection(Region other) {
        int x = Math.max(minX, other.minX);
        int y = Math.max(minY, other.minY);
        int z = Math.max(minZ, other.minZ);

        return new Region(x, y, z, Math.min(maxX, other.maxX), Math.min(maxY, other.maxY), Math.min(maxZ, other.maxZ));
    }

    public Region to2d() {
        return new Region(minX, Integer.MIN_VALUE/2, minZ, maxX, Integer.MAX_VALUE/2, maxZ);
    }

    public interface TriConsumer<T, V, U>{
        void accept(T x, V y, U z);
    }

    @Override
    public String toString() {
        return minX + "," + minY + "," + minZ + "->" + maxX + "," + maxY + "," + maxZ;
    }
}
