/*
 *   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 com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.entity.REntity;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightPlayer;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.listener.Recording;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.states.StateDependentTask;
import lombok.Getter;
import net.minecraft.core.Vec3i;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;

public class HullHider implements Listener {

    @Getter
    private final Map<FightTeam, Hull> hullMap = new HashMap<>();
    private final Hull[] hulls;

    public HullHider() {
        if (!TechHiderWrapper.ENABLED) {
            hulls = new Hull[0];
            return;
        }

        Fight.teams().forEach(team -> hullMap.put(team, new Hull(team)));
        hulls = hullMap.values().toArray(new Hull[0]);


        new StateDependentListener(TechHiderWrapper.ENABLED, FightState.Schem, this);
        new StateDependentTask(TechHiderWrapper.ENABLED, FightState.Schem, this::onTick, 0, 1);
    }

    public void initialize(FightTeam team) {
        if (!TechHiderWrapper.ENABLED) return;

        hullMap.get(team).initialize();
    }


    @EventHandler(priority = EventPriority.HIGH)
    public void onJoin(PlayerJoinEvent e) {
        updatePlayer(e.getPlayer());
    }

    @EventHandler
    public void onLeave(PlayerQuitEvent e) {
        removePlayer(e.getPlayer(), false);
    }

    public void updatePlayer(Player player) {
        if (!TechHiderWrapper.ENABLED) return;

        FightTeam team = Fight.getPlayerTeam(player);
        FightPlayer fp = Fight.getFightPlayer(player);
        for (Map.Entry<FightTeam, Hull> hull : hullMap.entrySet()) {
            if (Config.isReferee(player) || hull.getKey() == team || (fp != null && fp.canEntern())) {
                hull.getValue().removePlayer(player, true);
            } else {
                hull.getValue().addPlayer(player);
            }
        }
    }

    private void removePlayer(Player player, boolean activeRemoval) {
        for (Hull hull : hulls) {
            hull.removePlayer(player, activeRemoval);
        }
    }


    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onBlockPhysic(BlockPhysicsEvent e) {
        if (FlatteningWrapper.impl.doRecord(e)) {
            blockUpdate(e.getBlock(), e.getChangedType());
        }
    }

    public void blockUpdate(Block block, Material changedType) {
        for (Hull hull : hulls) {
            hull.updateBlockVisibility(block, changedType);
        }
    }

    public boolean isBlockHidden(Player player, int x, int y, int z) {
        if (!TechHiderWrapper.ENABLED) return false;

        for (Hull hull : hulls) {
            if (hull.isBlockHidden(player, x, y, z)) {
                return true;
            }
        }

        return false;
    }

    public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) {
        if (!TechHiderWrapper.ENABLED) return false;

        for (Hull hull : hulls) {
            if (hull.blockPrecise(player, chunkX, chunkY, chunkZ)) {
                return true;
            }
        }

        return false;
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onSpawn(EntitySpawnEvent e) {
        for (Hull hull : hulls) {
            hull.checkEntity(e.getEntity());
        }
    }

    private void onTick() {
        Recording.iterateOverEntities(Objects::nonNull, entity -> {
            for (Hull hull : hulls) {
                hull.checkEntity(entity);
            }
        });

        for (Hull hull : hulls) {
            hull.sendUncoveredBlocks();
        }
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onDeath(EntityDeathEvent e) {
        for (Hull hull : hulls) {
            hull.removeEntity(e.getEntity());
        }
    }

    public void updateREntity(REntity e) {
        for (Hull hull : hulls) {
            hull.checkREntity(e);
        }
    }

    public void despawnREntity(REntity e) {
        for (Hull hull : hulls) {
            hull.removeREntity(e);
        }
    }
}
