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

import de.steamwar.Reflection;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class FlatteningWrapper14 implements FlatteningWrapper.IFlatteningWrapper {

    private static final Map<String, Material> renamedLegacy = new HashMap<>();

    static{
        renamedLegacy.put("WOOD", Material.OAK_WOOD);
        renamedLegacy.put("SAPLING", Material.OAK_SAPLING);
        renamedLegacy.put("STATIONARY_WATER", Material.WATER);
        renamedLegacy.put("STATIONARY_LAVA", Material.LAVA);
        renamedLegacy.put("LOG", Material.OAK_LOG);
        renamedLegacy.put("LEAVES", Material.OAK_LEAVES);
        renamedLegacy.put("BED_BLOCK", Material.RED_BED);
        renamedLegacy.put("BED", Material.RED_BED);
        renamedLegacy.put("PISTON_STICKY_BASE", Material.STICKY_PISTON);
        renamedLegacy.put("WEB", Material.COBWEB);
        renamedLegacy.put("LONG_GRASS", Material.TALL_GRASS);
        renamedLegacy.put("PISTON_BASE", Material.PISTON);
        renamedLegacy.put("PISTON_EXTENSION", Material.PISTON_HEAD);
        renamedLegacy.put("WOOL", Material.WHITE_WOOL);
        renamedLegacy.put("PISTON_MOVING_PIECE", Material.MOVING_PISTON);
        renamedLegacy.put("YELLOW_FLOWER", Material.DANDELION);
        renamedLegacy.put("RED_ROSE", Material.POPPY);
        renamedLegacy.put("DOUBLE_STEP", Material.SMOOTH_STONE);
        renamedLegacy.put("STEP", Material.SMOOTH_STONE_SLAB);
        renamedLegacy.put("MOB_SPAWNER", Material.SPAWNER);
        renamedLegacy.put("WOOD_STAIRS", Material.OAK_STAIRS);
        renamedLegacy.put("WORKBENCH", Material.CRAFTING_TABLE);
        renamedLegacy.put("CROPS", Material.WHEAT_SEEDS);
        renamedLegacy.put("SEEDS", Material.WHEAT_SEEDS);
        renamedLegacy.put("SOIL", Material.FARMLAND);
        renamedLegacy.put("BURNING_FURNACE", Material.FURNACE);
        renamedLegacy.put("SIGN_POST", Material.OAK_SIGN);
        renamedLegacy.put("SIGN", Material.OAK_SIGN);
        renamedLegacy.put("WOODEN_DOOR", Material.OAK_DOOR);
        renamedLegacy.put("WOOD_DOOR", Material.OAK_DOOR);
        renamedLegacy.put("RAILS", Material.RAIL);
        renamedLegacy.put("WALL_SIGN", Material.OAK_WALL_SIGN);
        renamedLegacy.put("STONE_PLATE", Material.STONE_PRESSURE_PLATE);
        renamedLegacy.put("WOOD_PLATE", Material.OAK_PRESSURE_PLATE);
        renamedLegacy.put("GLOWING_REDSTONE_ORE", Material.REDSTONE_ORE);
        renamedLegacy.put("REDSTONE_TORCH_OFF", Material.REDSTONE_TORCH);
        renamedLegacy.put("REDSTONE_TORCH_ON", Material.REDSTONE_TORCH);
        renamedLegacy.put("IRON_DOOR_BLOCK", Material.IRON_DOOR);
        renamedLegacy.put("SUGAR_CANE_BLOCK", Material.SUGAR_CANE);
        renamedLegacy.put("CAKE_BLOCK", Material.CAKE);
        renamedLegacy.put("MELON_BLOCK", Material.MELON);
        renamedLegacy.put("BEETROOT_BLOCK", Material.BEETROOT);
        renamedLegacy.put("FENCE", Material.OAK_FENCE);
        renamedLegacy.put("PORTAL", Material.NETHER_PORTAL);
        renamedLegacy.put("DIODE_BLOCK_OFF", Material.REPEATER);
        renamedLegacy.put("DIODE_BLOCK_ON", Material.REPEATER);
        renamedLegacy.put("DIODE", Material.REPEATER);
        renamedLegacy.put("STAINED_GLASS", Material.WHITE_STAINED_GLASS);
        renamedLegacy.put("TRAP_DOOR", Material.OAK_TRAPDOOR);
        renamedLegacy.put("MONSTER_EGGS", Material.SKELETON_SPAWN_EGG);
        renamedLegacy.put("MONSTER_EGG", Material.SKELETON_SPAWN_EGG);
        renamedLegacy.put("SMOOTH_BRICK", Material.STONE_BRICKS);
        renamedLegacy.put("HUGE_MUSHROOM_1", Material.MUSHROOM_STEM);
        renamedLegacy.put("HUGE_MUSHROOM_2", Material.RED_MUSHROOM);
        renamedLegacy.put("IRON_FENCE", Material.IRON_BARS);
        renamedLegacy.put("THIN_GLASS", Material.GLASS_PANE);
        renamedLegacy.put("FENCE_GATE", Material.OAK_FENCE_GATE);
        renamedLegacy.put("SMOOTH_STAIRS", Material.STONE_BRICK_STAIRS);
        renamedLegacy.put("MYCEL", Material.MYCELIUM);
        renamedLegacy.put("WATER_LILY", Material.LILY_PAD);
        renamedLegacy.put("NETHER_FENCE", Material.NETHER_BRICK_FENCE);
        renamedLegacy.put("NETHER_WARTS", Material.NETHER_WART);
        renamedLegacy.put("NETHER_STALK", Material.NETHER_WART);
        renamedLegacy.put("ENCHANTMENT_TABLE", Material.ENCHANTING_TABLE);
        renamedLegacy.put("ENDER_PORTAL", Material.END_PORTAL);
        renamedLegacy.put("ENDER_PORTAL_FRAME", Material.END_PORTAL_FRAME);
        renamedLegacy.put("ENDER_STONE", Material.END_STONE);
        renamedLegacy.put("REDSTONE_LAMP_OFF", Material.REDSTONE_LAMP);
        renamedLegacy.put("REDSTONE_LAMP_ON", Material.REDSTONE_LAMP);
        renamedLegacy.put("WOOD_DOUBLE_STEP", Material.OAK_SLAB);
        renamedLegacy.put("WOOD_STEP", Material.OAK_SLAB);
        renamedLegacy.put("SPRUCE_WOOD_STAIRS", Material.SPRUCE_STAIRS);
        renamedLegacy.put("BIRCH_WOOD_STAIRS", Material.BIRCH_STAIRS);
        renamedLegacy.put("JUNGLE_WOOD_STAIRS", Material.JUNGLE_STAIRS);
        renamedLegacy.put("COMMAND", Material.COMMAND_BLOCK);
        renamedLegacy.put("COBBLE_WALL", Material.COBBLESTONE_WALL);
        renamedLegacy.put("WOOD_BUTTON", Material.OAK_BUTTON);
        renamedLegacy.put("SKULL", Material.SKELETON_SKULL);
        renamedLegacy.put("SKULL_ITEM", Material.SKELETON_SKULL);
        renamedLegacy.put("GOLD_PLATE", Material.LIGHT_WEIGHTED_PRESSURE_PLATE);
        renamedLegacy.put("IRON_PLATE", Material.HEAVY_WEIGHTED_PRESSURE_PLATE);
        renamedLegacy.put("REDSTONE_COMPARATOR_OFF", Material.COMPARATOR);
        renamedLegacy.put("REDSTONE_COMPARATOR_ON", Material.COMPARATOR);
        renamedLegacy.put("REDSTONE_COMPARATOR", Material.COMPARATOR);
        renamedLegacy.put("QUARTZ_ORE", Material.QUARTZ);
        renamedLegacy.put("STAINED_CLAY", Material.WHITE_TERRACOTTA);
        renamedLegacy.put("STAINED_GLASS_PANE", Material.WHITE_STAINED_GLASS_PANE);
        renamedLegacy.put("LEAVES_2", Material.ACACIA_LEAVES);
        renamedLegacy.put("LOG_2", Material.ACACIA_LOG);
        renamedLegacy.put("CARPET", Material.WHITE_CARPET);
        renamedLegacy.put("HARD_CLAY", Material.TERRACOTTA);
        renamedLegacy.put("DOUBLE_PLANT", Material.SUNFLOWER);
        renamedLegacy.put("STANDING_BANNER", Material.WHITE_BANNER);
        renamedLegacy.put("BANNER", Material.WHITE_BANNER);
        renamedLegacy.put("WALL_BANNER", Material.WHITE_WALL_BANNER);
        renamedLegacy.put("DAYLIGHT_DETECTOR_INVERTED", Material.DAYLIGHT_DETECTOR);
        renamedLegacy.put("DOUBLE_STONE_SLAB2", Material.RED_SANDSTONE_SLAB);
        renamedLegacy.put("STONE_SLAB2", Material.RED_SANDSTONE_SLAB);
        renamedLegacy.put("PURPUR_DOUBLE_SLAB", Material.PURPUR_SLAB);
        renamedLegacy.put("END_BRICKS", Material.END_STONE_BRICKS);
        renamedLegacy.put("COMMAND_REPEATING", Material.REPEATING_COMMAND_BLOCK);
        renamedLegacy.put("COMMAND_CHAIN", Material.CHAIN_COMMAND_BLOCK);
        renamedLegacy.put("MAGMA", Material.MAGMA_BLOCK);
        renamedLegacy.put("RED_NETHER_BRICK", Material.RED_NETHER_BRICKS);
        renamedLegacy.put("SILVER_SHULKER_BOX", Material.LIGHT_GRAY_SHULKER_BOX);
        renamedLegacy.put("SILVER_GLAZED_TERRACOTTA", Material.LIGHT_GRAY_TERRACOTTA);
        renamedLegacy.put("CONCRETE", Material.WHITE_CONCRETE);
        renamedLegacy.put("CONCRETE_POWDER", Material.WHITE_CONCRETE_POWDER);
        renamedLegacy.put("IRON_SPADE", Material.IRON_SHOVEL);
        renamedLegacy.put("WOOD_SWORD", Material.WOODEN_SWORD);
        renamedLegacy.put("WOOD_SPADE", Material.WOODEN_SHOVEL);
        renamedLegacy.put("WOOD_PICKAXE", Material.WOODEN_PICKAXE);
        renamedLegacy.put("WOOD_AXE", Material.WOODEN_AXE);
        renamedLegacy.put("STONE_SPADE", Material.STONE_SHOVEL);
        renamedLegacy.put("DIAMOND_SPADE", Material.DIAMOND_SHOVEL);
        renamedLegacy.put("MUSHROOM_SOUP", Material.MUSHROOM_STEW);
        renamedLegacy.put("GOLD_SWORD", Material.GOLDEN_SWORD);
        renamedLegacy.put("GOLD_SPADE", Material.GOLDEN_SHOVEL);
        renamedLegacy.put("GOLD_PICKAXE", Material.GOLDEN_PICKAXE);
        renamedLegacy.put("GOLD_AXE", Material.GOLDEN_AXE);
        renamedLegacy.put("SULPHUR", Material.GUNPOWDER);
        renamedLegacy.put("WOOD_HOE", Material.WOODEN_HOE);
        renamedLegacy.put("GOLD_HOE", Material.GOLDEN_HOE);
        renamedLegacy.put("GOLD_HELMET", Material.GOLDEN_HELMET);
        renamedLegacy.put("GOLD_CHESTPLATE", Material.GOLDEN_CHESTPLATE);
        renamedLegacy.put("GOLD_LEGGINGS", Material.GOLDEN_LEGGINGS);
        renamedLegacy.put("GOLD_BOOTS", Material.GOLDEN_BOOTS);
        renamedLegacy.put("PORK", Material.PORKCHOP);
        renamedLegacy.put("GRILLED_PORK", Material.COOKED_PORKCHOP);
        renamedLegacy.put("SNOW_BALL", Material.SNOWBALL);
        renamedLegacy.put("BOAT", Material.OAK_BOAT);
        renamedLegacy.put("CLAY_BRICK", Material.BRICKS);
        renamedLegacy.put("STORAGE_MINECART", Material.CHEST_MINECART);
        renamedLegacy.put("POWERED_MINECART", Material.FURNACE_MINECART);
        renamedLegacy.put("WATCH", Material.CLOCK);
        renamedLegacy.put("RAW_FISH", Material.SALMON);
        renamedLegacy.put("COOKED_FISH", Material.COOKED_SALMON);
        renamedLegacy.put("INK_SACK", Material.INK_SAC);
        renamedLegacy.put("RAW_BEEF", Material.BEEF);
        renamedLegacy.put("RAW_CHICKEN", Material.CHICKEN);
        renamedLegacy.put("EYE_OF_ENDER", Material.ENDER_EYE);
        renamedLegacy.put("SPECKLED_MELON", Material.GLISTERING_MELON_SLICE);
        renamedLegacy.put("EXP_BOTTLE", Material.EXPERIENCE_BOTTLE);
        renamedLegacy.put("FIREBALL", Material.FIRE_CHARGE);
        renamedLegacy.put("BOOK_AND_QUILL", Material.WRITABLE_BOOK);
        renamedLegacy.put("FLOWER_POT_ITEM", Material.FLOWER_POT);
        renamedLegacy.put("EMPTY_MAP", Material.MAP);
        renamedLegacy.put("BREWING_STAND_ITEM", Material.BREWING_STAND);
        renamedLegacy.put("CAULDRON_ITEM", Material.CAULDRON);
        renamedLegacy.put("CARROT_ITEM", Material.CARROT);
        renamedLegacy.put("POTATO_ITEM", Material.POTATO);
        renamedLegacy.put("SPRUCE_DOOR_ITEM", Material.SPRUCE_DOOR);
        renamedLegacy.put("BIRCH_DOOR_ITEM", Material.BIRCH_DOOR);
        renamedLegacy.put("JUNGLE_DOOR_ITEM", Material.JUNGLE_DOOR);
        renamedLegacy.put("ACACIA_DOOR_ITEM", Material.ACACIA_DOOR);
        renamedLegacy.put("DARK_OAK_DOOR_ITEM", Material.DARK_OAK_DOOR);
        renamedLegacy.put("CARROT_STICK", Material.CARROT_ON_A_STICK);
        renamedLegacy.put("FIREWORK", Material.FIREWORK_ROCKET);
        renamedLegacy.put("FIREWORK_CHARGE", Material.FIREWORK_STAR);
        renamedLegacy.put("NETHER_BRICK_ITEM", Material.NETHER_BRICKS);
        renamedLegacy.put("EXPLOSIVE_MINECART", Material.TNT_MINECART);
        renamedLegacy.put("IRON_BARDING", Material.IRON_HORSE_ARMOR);
        renamedLegacy.put("GOLD_BARDING", Material.GOLDEN_HORSE_ARMOR);
        renamedLegacy.put("DIAMOND_BARDING", Material.DIAMOND_HORSE_ARMOR);
        renamedLegacy.put("LEASH", Material.LEAD);
        renamedLegacy.put("COMMAND_MINECART", Material.COMMAND_BLOCK_MINECART);
        renamedLegacy.put("CHORUS_FRUIT_POPPED", Material.POPPED_CHORUS_FRUIT);
        renamedLegacy.put("DRAGONS_BREATH", Material.DRAGON_BREATH);
        renamedLegacy.put("BOAT_SPRUCE", Material.SPRUCE_BOAT);
        renamedLegacy.put("BOAT_BIRCH", Material.BIRCH_BOAT);
        renamedLegacy.put("BOAT_JUNGLE", Material.JUNGLE_BOAT);
        renamedLegacy.put("BOAT_ACACIA", Material.ACACIA_BOAT);
        renamedLegacy.put("BOAT_DARK_OAK", Material.DARK_OAK_BOAT);
        renamedLegacy.put("TOTEM", Material.TOTEM_OF_UNDYING);
        renamedLegacy.put("GOLD_RECORD", Material.MUSIC_DISC_13);
        renamedLegacy.put("GREEN_RECORD", Material.MUSIC_DISC_CAT);
        renamedLegacy.put("RECORD_3", Material.MUSIC_DISC_BLOCKS);
        renamedLegacy.put("RECORD_4", Material.MUSIC_DISC_CHIRP);
        renamedLegacy.put("RECORD_5", Material.MUSIC_DISC_FAR);
        renamedLegacy.put("RECORD_6", Material.MUSIC_DISC_MALL);
        renamedLegacy.put("RECORD_7", Material.MUSIC_DISC_MELLOHI);
        renamedLegacy.put("RECORD_8", Material.MUSIC_DISC_STAL);
        renamedLegacy.put("RECORD_9", Material.MUSIC_DISC_STRAD);
        renamedLegacy.put("RECORD_10", Material.MUSIC_DISC_WARD);
        renamedLegacy.put("RECORD_11", Material.MUSIC_DISC_11);
        renamedLegacy.put("RECORD_12", Material.MUSIC_DISC_WAIT);
    }

    private static final Reflection.Field<?> scoreboardName = Reflection.getField(FlatteningWrapper.scoreboardObjective, Reflection.getClass("net.minecraft.network.chat.Component"), 0);
    @Override
    public void setScoreboardTitle(Object packet, String title) {
        scoreboardName.set(packet, ChatWrapper.impl.stringToChatComponent(title));
    }

    private static final Class<?> scoreActionEnum = Core.getVersion() < 21 ? Reflection.getClass("net.minecraft.server.ServerScoreboard$Method") : null;
    private static final Reflection.Field<?> scoreAction = Core.getVersion() < 21 ? Reflection.getField(FlatteningWrapper.scoreboardScore, scoreActionEnum, 0) : null;
    private static final Object scoreActionChange = Core.getVersion() < 21 ? scoreActionEnum.getEnumConstants()[0] : null;

    @Override
    public void setScoreAction(Object packet) {
        scoreAction.set(packet, scoreActionChange);
    }

    @Override
    public Material getMaterial(String material) {
        try{
            return Material.valueOf(material);
        }catch(IllegalArgumentException e){
            return renamedLegacy.get(material);
        }
    }

    @Override
    public Material getDye(int colorCode) {
        switch(colorCode){
            case 1:
                return Material.RED_DYE;
            case 2:
                return Material.GREEN_DYE;
            case 3:
                return Material.BROWN_DYE;
            case 4:
                return Material.LAPIS_LAZULI;
            case 5:
                return Material.PURPLE_DYE;
            case 6:
                return Material.CYAN_DYE;
            case 7:
                return Material.LIGHT_GRAY_DYE;
            case 8:
                return Material.GRAY_DYE;
            case 9:
                return Material.PINK_DYE;
            case 10:
                return Material.LIME_DYE;
            case 11:
                return Material.YELLOW_DYE;
            case 12:
                return Material.LIGHT_BLUE_DYE;
            case 13:
                return Material.MAGENTA_DYE;
            case 14:
                return Material.ORANGE_DYE;
            case 15:
                return Material.WHITE_DYE;
            default:
                return Material.BLACK_DYE;
        }
    }

    @SuppressWarnings("deprecation")
    @Override
    public ItemStack setSkullOwner(String player) {
        ItemStack head = new ItemStack(Material.PLAYER_HEAD, 1);
        SkullMeta headmeta = (SkullMeta) head.getItemMeta();
        assert headmeta != null;
        headmeta.setOwningPlayer(Bukkit.getOfflinePlayer(player.startsWith(".") ? player.substring(1) : player));
        headmeta.setDisplayName(player);
        head.setItemMeta(headmeta);
        return head;
    }

    private static final Class<?> entityPose = Reflection.getClass("net.minecraft.world.entity.Pose");
    private static final Object standing = entityPose.getEnumConstants()[0];
    private static final Object swimming = entityPose.getEnumConstants()[3];
    private static final Object sneaking = entityPose.getEnumConstants()[5];
    @Override
    public Object getPose(FlatteningWrapper.EntityPose pose) {
        switch (pose) {
            case SNEAKING:
                return sneaking;
            case SWIMMING:
                return swimming;
            case NORMAL:
            default:
                return standing;
        }
    }

    @Override
    public void setNamedSpawnPacketDataWatcher(Object packet) {
        // field not present
    }

    @Override
    public Object formatDisplayName(String displayName) {
        return displayName != null ? Optional.of(ChatWrapper.impl.stringToChatComponent(displayName)) : Optional.empty();
    }

    private static final Class<?> registryBlocks = Reflection.getClass("net.minecraft.core.DefaultedRegistry");
    private static final Class<?> entityTypes = Reflection.getClass("net.minecraft.world.entity.EntityType");
    private static final Object entityTypesRegistry = Reflection.getField(Reflection.getClass(Core.getVersion() > 18 ? "net.minecraft.core.registries.BuiltInRegistries" : "net.minecraft.core.IRegistry"), registryBlocks, 0, entityTypes).get(null);
    private static final Reflection.Method get = Reflection.getMethod(registryBlocks, null, Reflection.getClass("net.minecraft.resources.ResourceLocation"));
    private static final Reflection.Field<?> spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, entityTypes, 0);
    private static final Reflection.Field<?> spawnLivingType = Core.getVersion() > 18 ? spawnType : Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1);
    private static final Reflection.Method toMinecraft = Reflection.getMethod("org.bukkit.craftbukkit.util.CraftNamespacedKey", "toMinecraft", NamespacedKey.class);
    private static final Map<EntityType, Object> types = new HashMap<>();
    static {
        types.put(EntityType.ARMOR_STAND, 1);
    }
    @Override
    public void setSpawnPacketType(Object packet, EntityType type) {
        if(type.isAlive()) {
            spawnLivingType.set(packet, Core.getVersion() > 18 ? get.invoke(entityTypesRegistry, toMinecraft.invoke(null, type.getKey())) : types.get(type));
        } else {
            spawnType.set(packet, get.invoke(entityTypesRegistry, toMinecraft.invoke(null, type.getKey())));
        }
    }

    @Override
    public int getViewDistance(Player player) {
        return player.getClientViewDistance();
    }

    private static final Reflection.Method getHandle = Reflection.getMethod("org.bukkit.craftbukkit.CraftWorld", "getHandle");
    private static final Reflection.Method save = Reflection.getMethod("net.minecraft.server.level.ServerLevel", null, Reflection.getClass("net.minecraft.util.ProgressListener"), boolean.class, boolean.class);
    @Override
    public void syncSave(World world) {
        save.invoke(getHandle.invoke(world), null, true, false);
    }
}
