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

import org.bukkit.configuration.file.YamlConfiguration;

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

public class IDConverter8 {

    private final Map<String, Set<String>> availibleAttributes;
    private final Map<String, Map<Set<String>, BlockTypeID>> map;

    public IDConverter8() {
        Map<String, Map<Set<String>, BlockTypeID>> map = new HashMap<>();

        YamlConfiguration legacy = YamlConfiguration.loadConfiguration(new InputStreamReader(Objects.requireNonNull(IDConverter8.class.getClassLoader().getResourceAsStream("legacy.yml"))));
        for(String blockString : legacy.getKeys(false)){
            String[] legacyBlockId = legacy.getString(blockString).split(":");

            map.computeIfAbsent(getBlockId(blockString), bId -> {
                Map<Set<String>, BlockTypeID> attributeMap = new HashMap<>();
                attributeMap.put(new HashSet<>(), new BlockTypeID(legacyBlockId[0], "0"));
                return attributeMap;
            }).put(getAttributes(blockString), new BlockTypeID(legacyBlockId[0], legacyBlockId[1]));
        }
        this.map = map;

        Map<String, Set<String>> availableAttributes = new HashMap<>();
        for (Map.Entry<String, Map<Set<String>, BlockTypeID>> entry : map.entrySet()) {
            availableAttributes.put(entry.getKey(), entry.getValue().keySet().stream().flatMap(Collection::stream).collect(Collectors.toSet()));
        }
        this.availibleAttributes = availableAttributes;
    }

    public BlockTypeID getId(String blockString) {
        String blockId = getBlockId(blockString);
        Map<Set<String>, IDConverter8.BlockTypeID> attributeMap = map.get(blockId);
        if(attributeMap == null) { // Block nonexistent pre-flattening
            return new BlockTypeID("0", "0");
        }

        Set<String> attributes = getAttributes(blockString);
        Set<String> knownAttributes = this.availibleAttributes.get(blockId);
        attributes.removeIf(attribute -> !knownAttributes.contains(attribute));

        long bestMatch = -1;
        BlockTypeID blockID = null;
        for(Map.Entry<Set<String>, BlockTypeID> entry : attributeMap.entrySet()) {
            Set<String> attrs = entry.getKey();
            if(attrs.size() <= bestMatch)
                continue;

            long matching = attributes.stream().filter(attrs::contains).count();
            if(matching > bestMatch) {
                blockID = entry.getValue();
                bestMatch = matching;

                if(bestMatch == attributes.size())
                    break;
            }
        }
        return blockID;
    }

    private String getBlockId(String blockString) {
        return blockString.split("\\[", 2)[0];
    }

    private Set<String> getAttributes(String blockString) {
        Set<String> attributes = new HashSet<>();
        if(blockString.contains("["))
            Collections.addAll(attributes, blockString.split("\\[")[1].replace("]", "").split(","));
        return attributes;
    }

    static class BlockTypeID{
        private final int blockId;
        private final byte dataId;

        private BlockTypeID(String blockId, String dataId) {
            this.blockId = Integer.parseInt(blockId);
            this.dataId = Byte.parseByte(dataId);
        }

        int getBlockId() {
            return blockId;
        }

        byte getDataId() {
            return dataId;
        }
    }
}
