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

   Copyright (C) 2023  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.schematicsystem.commands.schematiccommand;

import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.CompoundTagBuilder;
import com.sk89q.jnbt.ListTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import de.steamwar.schematicsystem.CheckSchemType;
import de.steamwar.schematicsystem.autocheck.AutoCheckerResult;
import de.steamwar.schematicsystem.autocheck.BlockPos;
import de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommand;
import org.bukkit.Material;

import java.util.*;
import java.util.stream.Collectors;

public class SchematicCommand15 implements SchematicCommand.ISchematicCommand {
    @Override
    public Clipboard fixClipboard(Clipboard clipboard, AutoCheckerResult result, CheckSchemType type) throws Exception {
        for (BlockPos blockPos : result.getBlockScanResult().getRecords()) {
            BlockVector3 vector = BlockVector3.at(blockPos.getX(), blockPos.getY(), blockPos.getZ());
            clipboard.setBlock(vector, clipboard.getFullBlock(vector).toBaseBlock(new CompoundTag(Collections.emptyMap())));
        }

        Map<BlockPos, Set<Material>> toBeCheckedInvs = new HashMap<>();

        toBeCheckedInvs.putAll(result.getBlockScanResult().getForbiddenItems());
        toBeCheckedInvs.putAll(result.getBlockScanResult().getForbiddenNbt());

        for (Map.Entry<BlockPos, Set<Material>> entry: toBeCheckedInvs.entrySet()) {
            BlockPos pos = entry.getKey();
            Set<Material> materials = entry.getValue();
            BlockVector3 vector = BlockVector3.at(pos.getX(), pos.getY(), pos.getZ());
            BaseBlock block = clipboard.getFullBlock(vector);
            CompoundTag tag = block.getNbtData();
            CompoundTagBuilder builder = CompoundTagBuilder.create();
            List<CompoundTag> list = new ArrayList<>();
            for (CompoundTag items : tag.getList("Items", CompoundTag.class)) {
                if(materials.contains(Material.matchMaterial(items.getString("id")))) {
                    continue;
                }

                if(items.containsKey("tag")) {
                    continue;
                }

                list.add(items);
            }
            builder.put("Items", new ListTag(CompoundTag.class, list));
            clipboard.setBlock(vector, block.toBaseBlock(builder.build()));
        }

        if(type.getMaxDispenserItems() > 0 ) {
            for (Map.Entry<BlockPos, Integer> entry : result.getBlockScanResult().getDispenserItems().entrySet()) {
                if(entry.getValue() <= type.getMaxDispenserItems()) {
                    continue;
                }

                BlockPos pos = entry.getKey();
                BlockVector3 vector = BlockVector3.at(pos.getX(), pos.getY(), pos.getZ());
                BaseBlock block = clipboard.getFullBlock(vector);
                CompoundTag tag = block.getNbtData();
                CompoundTagBuilder builder = tag.createBuilder();
                List<CompoundTag> items = tag.getList("Items", CompoundTag.class);
                Collections.reverse(items); // To let the first item be in the Dispenser
                List<CompoundTag> list = new ArrayList<>();
                int diff = entry.getValue() - type.getMaxDispenserItems();
                for (CompoundTag item : items) {
                    if(item == null) {
                        continue;
                    }

                    if(diff == 0) {
                        list.add(item);
                        continue;
                    }

                    if(diff > item.getByte("Count")) {
                        diff -= item.getByte("Count");
                        continue;
                    }

                    item = item.createBuilder().putByte("Count", (byte) (item.getByte("Count") - diff)).build();
                    diff = 0;
                    list.add(item);
                }

                builder.put("Items", new ListTag(CompoundTag.class, list));
                clipboard.setBlock(vector, block.toBaseBlock(builder.build()));
            }
        }

        if(!result.isLimitedBlocksOK()) {
            Set<Material> toReplace = type.getLimits().entrySet().stream()
                    .filter(setIntegerEntry -> setIntegerEntry.getValue() == 0)
                    .flatMap(setIntegerEntry -> setIntegerEntry.getKey().stream())
                    .map(Material::matchMaterial)
                    .collect(Collectors.toSet());
            BlockState replaceType = Objects.requireNonNull(toReplace.contains(Material.END_STONE) ? BlockTypes.IRON_BLOCK : BlockTypes.END_STONE).getDefaultState();
            BlockVector3 min = clipboard.getMinimumPoint();
            BlockVector3 max = clipboard.getMaximumPoint();
            for (int i = min.getBlockX(); i <= max.getBlockX(); i++) {
                for (int j = min.getBlockY(); j <= max.getBlockY(); j++) {
                    for (int k = min.getBlockZ(); k <= max.getBlockZ(); k++) {
                        BlockVector3 vector = BlockVector3.at(i, j, k);
                        BaseBlock block = clipboard.getFullBlock(vector);
                        if(toReplace.contains(Material.matchMaterial(block.getBlockType().getId()))) {
                            clipboard.setBlock(vector, replaceType.toBaseBlock());
                        }
                    }
                }
            }
        }

        return clipboard;
    }

    @Override
    public void createCopy(EditSession editSession, Clipboard clipboard) throws WorldEditException {
        Operations.complete(new ForwardExtentCopy(editSession, clipboard.getRegion(), clipboard, clipboard.getMinimumPoint()));
    }
}
