package org.allaymc.server.block.hash;

import lombok.extern.slf4j.Slf4j;
import org.allaymc.testutils.AllayTestExtension;
import org.junit.jupiter.api.extension.ExtendWith;

import java.nio.charset.StandardCharsets;

/**
 * @author daoge_cmd
 */
@ExtendWith(AllayTestExtension.class)
@Slf4j
public class MinecraftNamespaceComparator {
    private static final long FNV1_64_INIT = 0xcbf29ce484222325L;
    private static final long FNV1_PRIME_64 = 1099511628211L;

//    @Test
//    void test() {
//        var example = VanillaBlockPaletteRegistry.getRegistry()
//                .getContent()
//                .entrySet()
//                .stream()
//                .sorted(Comparator.comparingInt(e -> e.getValue().index()))
//                .map(e -> e.getKey().getIdentifier().toString())
//                .toList();
//        var blocks = Arrays.stream(VanillaBlockId.values()).map(vanillaBlockId -> vanillaBlockId.getIdentifier().toString()).sorted(MinecraftNamespaceComparator::compareFNV).toList();
//        assertEquals(example, blocks);
//    }

    public static int compare(String idA, String idB) {
        String childIdA = idA.substring(idA.indexOf(":") + 1);
        String childIdB = idB.substring(idB.indexOf(":") + 1);

        // Compare by child first
        int childIdComparsion = childIdB.compareTo(childIdA);
        if (childIdComparsion != 0) {
            return childIdComparsion;
        }

        // Compare by namespace if childs are equal
        String namespaceA = idA.substring(0, idA.indexOf(":"));
        String namespaceB = idB.substring(0, idB.indexOf(":"));
        return namespaceB.compareTo(namespaceA);
    }

    // https://gist.github.com/SupremeMortal/5e09c8b0eb6b3a30439b317b875bc29c
    // Thank you Supreme
    public static int compareFNV(String idA, String idB) {
        byte[] bytes1 = idA.getBytes(StandardCharsets.UTF_8);
        byte[] bytes2 = idB.getBytes(StandardCharsets.UTF_8);
        long hash1 = fnv164(bytes1);
        long hash2 = fnv164(bytes2);
        return Long.compareUnsigned(hash1, hash2);
    }

    private static long fnv164(byte[] data) {
        long hash = FNV1_64_INIT;
        for (byte datum : data) {
            hash *= FNV1_PRIME_64;
            hash ^= (datum & 0xff);
        }

        return hash;
    }
}
