package me.dadus33.chatitem.listeners;

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import me.dadus33.chatitem.ChatItem;
import me.dadus33.chatitem.utils.ProtocolVersion;
import me.dadus33.chatitem.utils.Storage;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.ShulkerBox;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.Plugin;

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


public class ChatPacketListener extends PacketAdapter {

    private final static String NAME = "{name}";
    private final static String AMOUNT = "{amount}";
    private final static String TIMES = "{times}";
    private final static List<Material> SHULKER_BOXES = new ArrayList<>();

    private Storage c;


    public ChatPacketListener(Plugin plugin, ListenerPriority listenerPriority, Storage s, PacketType... types) {
        super(plugin, listenerPriority, types);
        if(ChatItem.supportsShulkerBoxes()){
            SHULKER_BOXES.addAll(Arrays.asList(Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX,
                    Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.GREEN_SHULKER_BOX,
                    Material.LIGHT_BLUE_SHULKER_BOX, Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX,
                    Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.LIGHT_GRAY_SHULKER_BOX,
                    Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX));
        }
        c = s;
    }

    private static String materialToName(Material m) {
        if (m.equals(Material.TNT)) {
            return "TNT";
        }
        String orig = m.toString().toLowerCase();
        String[] splits = orig.split("_");
        StringBuilder sb = new StringBuilder(orig.length());
        int pos = 0;
        for (String split : splits) {
            sb.append(split);
            int loc = sb.lastIndexOf(split);
            char charLoc = sb.charAt(loc);
            if (!(split.equalsIgnoreCase("of") || split.equalsIgnoreCase("and") ||
                    split.equalsIgnoreCase("with") || split.equalsIgnoreCase("on")))
                sb.setCharAt(loc, Character.toUpperCase(charLoc));
            if (pos != splits.length - 1)
                sb.append(' ');
            ++pos;
        }

        return sb.toString();
    }

    private void stripData(ItemStack i){
        if(i == null){
            return;
        }
        if(i.getType().equals(Material.AIR)){
            return;
        }
        if(!i.hasItemMeta()){
            return;
        }
        ItemMeta im = Bukkit.getItemFactory().getItemMeta(i.getType());
        ItemMeta original = i.getItemMeta();
        if(original.hasDisplayName()){
            im.setDisplayName(original.getDisplayName());
        }
        i.setItemMeta(im);
    }

    @SuppressWarnings("deprecation")
    @Override
    public void onPacketSending(final PacketEvent e) {
        final PacketContainer packet = e.getPacket();
        if(!packet.hasMetadata("parse")){ //First we check if the packet validator has validated this packet to be parsed by us
            return;
        }
        final boolean usesBaseComponents = (boolean)packet.getMetadata("base-component"); //The packet validator should also tell if this packet uses base components
        e.setCancelled(true); //We cancel the packet as we're going to resend it anyways (ignoring listeners this time)
        Bukkit.getScheduler().runTaskAsynchronously(ChatItem.getInstance(), () -> {
            String json = (String)packet.getMetadata("json"); //The packet validator got the json for us, so no need to get it again
            int topIndex = -1;
            String name = null;
            for(Player p : Bukkit.getOnlinePlayers()){
                String pname = "\\u0007"+p.getName();
                if(!json.contains(pname)){
                    continue;
                }
                int index = json.lastIndexOf(pname)+pname.length();
                if(index>topIndex){
                    topIndex = index;
                    name = pname.replace("\\u0007", "");
                }
            }
            if(name==null){ //something went really bad, so we run away and hide (AKA the player left or is on another server)
                return;
            }

            Player p = Bukkit.getPlayer(name);
            StringBuilder builder = new StringBuilder(json);
            builder.replace(topIndex-(name.length()+6), topIndex, ""); //we remove both the name and the separator from the json string
            json = builder.toString();

            String message = null;
            try {
                if(!p.getItemInHand().getType().equals(Material.AIR)) {
                    ItemStack copy = p.getItemInHand().clone();
                    if(copy.getType().equals(Material.WRITABLE_BOOK) || copy.getType().equals(Material.WRITTEN_BOOK)){ //filtering written books
                        BookMeta bm = (BookMeta)copy.getItemMeta();
                        bm.setPages(Collections.emptyList());
                        copy.setItemMeta(bm);
                    } else {
                        if (ChatItem.supportsShulkerBoxes()) { //filtering shulker boxes
                            if (SHULKER_BOXES.contains(copy.getType())) {
                                if (copy.hasItemMeta()) {
                                    BlockStateMeta bsm = (BlockStateMeta) copy.getItemMeta();
                                    if (bsm.hasBlockState()) {
                                        ShulkerBox sb = (ShulkerBox) bsm.getBlockState();
                                        for (ItemStack item : sb.getInventory()) {
                                            stripData(item);
                                        }
                                        bsm.setBlockState(sb);
                                    }
                                    copy.setItemMeta(bsm);
                                }
                            }
                        }
                    }
                    message = ChatItem.getManipulator().parse(json, c.PLACEHOLDERS, copy, styleItem(copy, c), ProtocolVersion.getClientVersion(e.getPlayer()));
                } else {
                    if(!c.HAND_DISABLED){
                        message = ChatItem.getManipulator().parseEmpty(json, c.PLACEHOLDERS, c.HAND_NAME, c.HAND_TOOLTIP, p);
                    }
                }
            } catch (InvocationTargetException | IllegalAccessException | InstantiationException | NoSuchFieldException | NoSuchMethodException e1) {
                e1.printStackTrace();
            }
            if(message!=null) {
                if(!usesBaseComponents) {
                    packet.getChatComponents().writeSafely(0, WrappedChatComponent.fromJson(message));
                }else{
                    packet.getSpecificModifier(BaseComponent[].class).writeSafely(0, ComponentSerializer.parse(message));
                }
                try {
                    ProtocolLibrary.getProtocolManager().sendServerPacket(e.getPlayer(), packet, false);
                } catch (InvocationTargetException e1) {
                    e1.printStackTrace();
                }
            } else {
                try {
                    ProtocolLibrary.getProtocolManager().sendServerPacket(e.getPlayer(), packet, false);
                } catch (InvocationTargetException e1) {
                    e1.printStackTrace();
                }

            }
        });
    }

    public void setStorage(Storage st) {
        c = st;
    }

    public static String styleItem(ItemStack item, Storage c){
        String replacer = c.NAME_FORMAT;
        String amount = c.AMOUNT_FORMAT;
        boolean dname = item.hasItemMeta() ? item.getItemMeta().hasDisplayName() : false;

        if (item.getAmount() == 1) {
            if (c.FORCE_ADD_AMOUNT) {
                amount = amount.replace(TIMES, "1");
                replacer = replacer.replace(AMOUNT, amount);
            } else {
                replacer = replacer.replace(AMOUNT, "");
            }
        } else {
            amount = amount.replace(TIMES, String.valueOf(item.getAmount()));
            replacer = replacer.replace(AMOUNT, amount);
        }
        if (dname) {
            String trp = item.getItemMeta().getDisplayName();
            if(c.COLOR_IF_ALREADY_COLORED){
                replacer = replacer.replace(NAME, ChatColor.stripColor(trp));
            } else {
                replacer = replacer.replace(NAME, trp);
            }
        } else {
            HashMap<Short, String> translationSection = c.TRANSLATIONS.get(item.getType().name());
            if(translationSection==null){
                String trp = materialToName(item.getType());
                replacer = replacer.replace(NAME, trp);
            }else {
                String translated = translationSection.get(item.getDurability());
                if (translated != null) {
                    replacer = replacer.replace(NAME, translated);
                } else {
                    replacer = replacer.replace(NAME, materialToName(item.getType()));
                }
            }
        }
        return replacer;
    }

}