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

    Copyright (C) 2020  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.fight;

import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.transform.AffineTransform;
import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.record.GlobalRecorder;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependent;
import de.steamwar.fightsystem.utils.ColorConverter;
import de.steamwar.fightsystem.utils.Region;
import de.steamwar.fightsystem.utils.WorldeditWrapper;
import de.steamwar.sql.SchematicData;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SchematicType;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.DyeColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.util.Vector;

import java.io.IOException;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;

public class FightSchematic extends StateDependent {

    private final FightTeam team;
    private final Region region;
    private final boolean rotate;

    @Getter
    private Clipboard clipboard = null;
    private int schematic = 0;

    public FightSchematic(FightTeam team, boolean rotate) {
        super(ArenaMode.All, FightState.PostSchemSetup);
        this.team = team;
        this.region = team.getSchemRegion();
        this.rotate = rotate;
        register();
    }

    public boolean hasSchematic() {
        return clipboard != null;
    }

    public int getId(){
        return schematic;
    }

    public void setSchematic(SchematicNode schem) {
        schematic = schem.getId();
        try {
            clipboard = new SchematicData(schem).load();

            if(schem.replaceColor())
                replaceTeamColor(clipboard);
        } catch (IOException e) {
            team.broadcastSystem("SCHEMATIC_UNLOADABLE");
            Bukkit.getLogger().log(Level.SEVERE, e, () -> "Couldn't load Schematic " + schem.getName());
        }
    }

    public void setSchematic(int schemId, Clipboard clipboard) {
        this.schematic = schemId;
        this.clipboard = clipboard;
    }

    public void reset(){
        schematic = 0;
        clipboard = null;
    }

    @Override
    public void enable() {
        if(FightState.getFightState() == FightState.SPECTATE)
            return;

        if(clipboard == null){
            List<SchematicNode> publics = SchematicNode.getAllSchematicsOfType(0, Config.SchematicType.toDB());
            if(publics.isEmpty()) {
                for (SchematicType type : Config.SubTypes) {
                    publics = SchematicNode.getAllSchematicsOfType(0, type.toDB());
                    if (!publics.isEmpty()) {
                        break;
                    }
                }
                if (publics.isEmpty()) {
                    return;
                }
            }

            setSchematic(publics.get(new Random().nextInt(publics.size())));
        }

        if(ArenaMode.AntiReplay.contains(Config.mode)) {
            if(team.isBlue())
                GlobalRecorder.getInstance().blueSchem(schematic);
            else
                GlobalRecorder.getInstance().redSchem(schematic);
        }

        Bukkit.getScheduler().runTask(FightSystem.getPlugin(), this::paste);
    }

    private void replaceTeamColor(Clipboard clipboard) {
        try {
            WorldeditWrapper.impl.replaceTeamColor(clipboard, ArenaMode.AntiPrepare.contains(Config.mode) ? ColorConverter.chat2dye(team.getColor()) : DyeColor.PINK);
        } catch (WorldEditException e) {
            Bukkit.getLogger().log(Level.SEVERE, "Could not recolor schematic", e);
        }
    }

    private void paste(){
        FreezeWorld freezer = new FreezeWorld();

        team.teleportToSpawn();
        Vector dims = WorldeditWrapper.impl.getDimensions(clipboard);
        WorldeditWrapper.impl.pasteClipboard(
                clipboard,
                new Location(Config.world, region.centerX(), region.getMinY(), region.centerZ()),
                new Vector(
                        Config.PasteAligned && Config.BlueToRedX != 0 ? region.getSizeX()/2.0 - dims.getBlockX() : -dims.getBlockX()/2.0,
                        Config.WaterDepth != 0 ? Config.WaterDepth - WorldeditWrapper.impl.getWaterDepth(clipboard) : 0,
                        Config.PasteAligned && Config.BlueToRedZ != 0 ? region.getSizeZ()/2.0 - dims.getBlockZ() : -dims.getBlockZ()/2.0
                ).add(new Vector(rotate ? 1 : 0, 0, rotate ? 1 : 0)),
                new AffineTransform().rotateY(rotate ? 180 : 0)
        );
        FightSystem.getHullHider().initialize(team);
        team.getPlayers().forEach(fightPlayer -> fightPlayer.ifAI(ai -> ai.schematic(clipboard)));
        if(ArenaMode.Check.contains(Config.mode) && !team.isBlue())
            replaceSync(Material.TNT, Material.RED_WOOL);

        Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), freezer::disable, 3);
    }

    @Override
    public void disable() {
        if(!Config.ReplaceObsidianBedrock || Config.mode == ArenaMode.PREPARE)
            return;

        FreezeWorld freezer = null;
        if(!Config.ReplaceWithBlockupdates)
            freezer = new FreezeWorld();

        replaceSync(Material.OBSIDIAN, Material.TNT);
        replaceSync(Material.BEDROCK, Material.SLIME_BLOCK);

        if(!Config.ReplaceWithBlockupdates)
            freezer.disable();
    }

    public void pasteTeamName(){
        char[] chars = team.getName().toCharArray();
        Clipboard[] characters = new Clipboard[chars.length];

        int length = 0;
        int[] offsets = new int[chars.length];

        for(int i = 0; i < chars.length; i++){
            Clipboard character;
            try {
                character = WorldeditWrapper.impl.loadChar(chars[i] == '/' ? "slash" : String.valueOf(chars[i]));
            } catch (IOException e) {
                Bukkit.getLogger().log(Level.WARNING, "Could not display character {} due to missing file!", chars[i]);
                try {
                    character = WorldeditWrapper.impl.loadChar("");
                }catch (IOException ex) {
                    throw new SecurityException("Could not load text", ex);
                }
            }

            replaceTeamColor(character);

            characters[i] = character;
            offsets[i] = length;
            length += WorldeditWrapper.impl.getDimensions(character).getBlockX() + 1; // 1 is the distance between characters
        }

        length -= 1;
        AffineTransform aT = new AffineTransform().rotateY(((team == Fight.getRedTeam()) == (Config.BlueToRedZ > 0)) ? 180 : 0);
        Location base = new Location(Config.world, region.centerX(), team.getExtendRegion().getMaxY(), region.centerZ());
        for(int i = 0; i < characters.length; i++){
            WorldeditWrapper.impl.pasteClipboard(characters[i], base, new Vector(offsets[i] - length/2, 0, -region.getSizeZ()/2), aT);
        }
    }

    private void replaceSync(Material target, Material replacement){
        region.forEach((x, y, z) -> {
            Block block = Config.world.getBlockAt(x, y, z);
            if(block.getType() == target)
                block.setType(replacement);
        });
    }
}
