package cz.jzitnik.game.entities.items;

import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.EntityRegistry;
import cz.jzitnik.game.annotations.ItemRegistry;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.entities.MyOptional;
import org.reflections.Reflections;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

public class ItemBlockSupplier {
    private static final HashMap<String, Constructor<Block>> registeredBlocks = new HashMap<>();
    private static final HashMap<String, Constructor<Item>> registeredItems = new HashMap<>();
    private static final HashMap<String, Constructor<Block>> registeredEntities = new HashMap<>();
    private static final HashMap<String, String> dropsList = new HashMap<>();
    private static final HashMap<String, String> blockList = new HashMap<>();

    static {
        registerItems();
        registerBlocks();
        registerEntities();
    }

    private static void registerBlocks() {
        Reflections reflections = new Reflections("cz.jzitnik.game.entities.items.registry");
        Set<Class<?>> blockClasses = reflections.getTypesAnnotatedWith(BlockRegistry.class);

        for (Class<?> clazz : blockClasses) {
            try {
                Constructor<Block> blockInstance = (Constructor<Block>) clazz.getDeclaredConstructor();
                BlockRegistry annotation = clazz.getAnnotation(BlockRegistry.class);
                registeredBlocks.put(annotation.value(), blockInstance);

                String dropKey = annotation.drops().isEmpty() ? annotation.value() : annotation.drops();
                dropsList.put(annotation.value(), dropKey);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static void registerEntities() {
        Reflections reflections = new Reflections("cz.jzitnik.game.entities.items.registry");
        Set<Class<?>> entityClasses = reflections.getTypesAnnotatedWith(EntityRegistry.class);

        for (Class<?> clazz : entityClasses) {
            try {
                Constructor<Block> entityInstance = (Constructor<Block>) clazz.getDeclaredConstructor();
                EntityRegistry annotation = clazz.getAnnotation(EntityRegistry.class);
                registeredEntities.put(annotation.value(), entityInstance);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static void registerItems() {
        Reflections reflections = new Reflections("cz.jzitnik.game.entities.items.registry");
        Set<Class<?>> itemClasses = reflections.getTypesAnnotatedWith(ItemRegistry.class);

        for (Class<?> clazz : itemClasses) {
            try {
                Constructor<Item> itemInstance = (Constructor<Item>) clazz.getDeclaredConstructor();
                ItemRegistry annotation = clazz.getAnnotation(ItemRegistry.class);
                registeredItems.put(annotation.value(), itemInstance);

                blockList.put(annotation.value(),
                        annotation.block().isEmpty() ? annotation.value() : annotation.block());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static Block getBlock(String key, Item item) {
        try {
            Block block = registeredBlocks.get(key).newInstance();

            if (dropsList.get(key).equals(item.getId())) {
                block.setDrops(List.of(item));
            } else {
                block.setDrops(List.of(getItem(dropsList.get(key), block)));
            }

            return block;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    public static Block getBlock(String key) {
        try {
            Block block = registeredBlocks.get(key).newInstance();
            if (registeredItems.containsKey(dropsList.get(key))) {
                block.setDrops(List.of(getItem(dropsList.get(key), block)));
            }

            return block;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    public static Item getItem(String key) {
        try {
            Item item = registeredItems.get(key).newInstance();
            if (registeredBlocks.containsKey(blockList.get(key))) {
                item.setBlock(MyOptional.of(getBlock(blockList.get(key), item)));
            }

            return item;
        } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public static Item getItem(String key, Block block) {
        try {
            Item item = registeredItems.get(key).newInstance();

            if (blockList.get(key).equals(block.getBlockId())) {
                item.setBlock(MyOptional.of(block));
            } else {
                item.setBlock(MyOptional.of(getBlock(blockList.get(key), item)));
            }

            return item;
        } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public static Block getEntity(String key) {
        try {
            return registeredEntities.get(key).newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}
