/*
    This file is a part of the SteamWar software.

    Copyright (C) 2021  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.core.CraftbukkitWrapper;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.events.BoardingEvent;
import de.steamwar.fightsystem.events.TeamLeaveEvent;
import de.steamwar.fightsystem.events.TeamSpawnEvent;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.techhider.TechHider;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;

import java.util.concurrent.ConcurrentHashMap;

public class TechHiderWrapper extends StateDependent implements TechHider.LocationEvaluator, Listener {

    public static final boolean ENABLED = !Config.OnlyPublicSchematics && !Config.test() && Config.TechhiderActive;

    private final ConcurrentHashMap<Player, Region> hiddenRegion = new ConcurrentHashMap<>();
    private final TechHider techHider;

    public TechHiderWrapper() {
        super(ENABLED, FightState.Schem);
        techHider = new TechHider(this, Config.ObfuscateWith, Config.HiddenBlocks, Config.HiddenBlockEntities);

        new StateDependentListener(ENABLED, FightState.Schem, this);
        register();
    }

    @Override
    public void enable() {
        techHider.enable();
    }

    @Override
    public void disable() {
        techHider.disable();
        hiddenRegion.clear();
    }

    @EventHandler
    public void teamJoin(TeamSpawnEvent e) {
        e.getFightPlayer().ifPlayer(player -> hiddenRegion.put(player, getHiddenRegion(player)));
    }

    @EventHandler
    public void boarding(BoardingEvent e) {
        e.getFightPlayer().ifPlayer(player -> hiddenRegion.put(player, getHiddenRegion(player)));
    }

    @EventHandler
    public void teamLeave(TeamLeaveEvent e) {
        e.getFightPlayer().ifPlayer(player -> {
            if(player.isOnline())
                hiddenRegion.put(player, getHiddenRegion(player));
        });
    }

    @EventHandler
    public void playerQuit(PlayerQuitEvent e) {
        Player player = e.getPlayer();
        hiddenRegion.remove(player);
    }

    public void reloadChunks(Player player, Region region, Region exclusion) {
        if(!ENABLED || !FightState.Schem.contains(FightState.getFightState()) || !player.isOnline())
            return;

        region.forEachChunk((chunkX, chunkZ) -> {
            if(exclusion.chunkOutside(chunkX, chunkZ))
                CraftbukkitWrapper.impl.sendChunk(player, chunkX, chunkZ);
        });
    }

    @Override
    public boolean skipChunk(Player player, int chunkX, int chunkZ) {
        return getHiddenRegion(player).chunkOutside(chunkX, chunkZ);
    }

    @Override
    public boolean skipChunkSection(Player player, int chunkX, int chunkY, int chunkZ) {
        return getHiddenRegion(player).chunkSectionOutside(chunkX, chunkY, chunkZ);
    }

    @Override
    public TechHider.State check(Player player, int x, int y, int z) {
        if(hiddenRegion.computeIfAbsent(player, this::getHiddenRegion).inRegion(x, y, z)) {
            if(FightSystem.getHullHider().isBlockHidden(player, x, y, z)) {
                return TechHider.State.HIDE;
            } else {
                return TechHider.State.CHECK;
            }
        } else {
            return TechHider.State.SKIP;
        }
    }

    @Override
    public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) {
        return FightSystem.getHullHider().blockPrecise(player, chunkX, chunkY, chunkZ);
    }

    private Region getHiddenRegion(Player player) {
        if(Config.isReferee(player))
            return Region.EMPTY;

        FightTeam team = Fight.getPlayerTeam(player);
        if(team == null)
            return Config.ArenaRegion;

        if(team.canPlayerEntern(player))
            return Region.EMPTY;

        return Fight.getOpposite(team).getExtendRegion();
    }
}
