package me.arasple.mc.trmenu.display;

import io.izzel.taboolib.internal.apache.lang3.math.NumberUtils;
import io.izzel.taboolib.module.locale.TLocale;
import io.izzel.taboolib.util.Strings;
import io.izzel.taboolib.util.lite.Materials;
import io.th0rgal.oraxen.items.OraxenItems;
import me.arasple.mc.trmenu.TrMenu;
import me.arasple.mc.trmenu.hook.HookHeadDatabase;
import me.arasple.mc.trmenu.utils.*;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BannerMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;

import java.util.Arrays;
import java.util.Comparator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Arasple
 * @date 2020/1/14 21:48
 * 物品材质 + 物品选项
 * 物品选项:
 * 头颅玩家(变量支持)<head:>
 * 自定义纹理 <skull:>
 * [旧] 数据值 Datavalue
 * [新] ModelData
 * 染色 [皮革]
 * 染色
 * JavaScript
 */
public class Mat {

    private ItemStack staticItem;
    private String mat;
    private Option option;
    private String optionValue;

    public Mat(String rawMat) {
        String[] options = readOption(rawMat);
        if (options != null) {
            this.option = Option.valueOf(options[0]);
            this.optionValue = options[1];
        }
        if (option == null) {
            option = Option.ORIGINAL;
        }
        this.mat = rawMat.replaceAll("<([^<>].+)>", "").replaceAll(" ", "");
        if (JsonItem.isJson(this.mat)) {
            option = option != Option.VARIABLE ? Option.JSON : Option.VARIABLE;
            if (option == Option.JSON) {
                staticItem = JsonItem.fromJson(rawMat);
            }
        }
    }

    public ItemStack createItem(Player player) {
        if (staticItem != null) {
            return staticItem.clone();
        }
        if (option == Option.VARIABLE && JsonItem.isJson(mat)) {
            return JsonItem.fromJson(Vars.replace(player, mat));
        }
        ItemStack item;
        ItemMeta meta;
        // ORIGINAL
        if (Strings.nonEmpty(mat)) {
            String[] args = (option == Option.VARIABLE ? Vars.replace(player, mat) : mat).split(":");
            String[] read = readMaterial(new String[]{args[0], args.length > 1 ? args[1] : null});
            item = new ItemStack(Material.valueOf(read[0]));
            meta = item.getItemMeta();
            if (!Materials.isNewVersion() && !Strings.isEmpty(read[1]) && option != Option.DATA_VALUE) {
                item.setDurability(NumberUtils.toShort(read[1], (short) 0));
            } else if (option == Option.DATA_VALUE) {
                item.setDurability(NumberUtils.toShort(optionValue, (short) 0));
            } else if (option == Option.MODEL_DATA) {
                meta.setCustomModelData(NumberUtils.toInt(optionValue, 0));
            } else if (option == Option.DYE_LEATHER) {
                if (meta instanceof LeatherArmorMeta) {
                    Dyer.setLeather((LeatherArmorMeta) meta, optionValue);
                }
            } else if (option == Option.BANNER) {
                item = Materials.WHITE_BANNER.parseItem();
                meta = item.getItemMeta();
                if (meta instanceof BannerMeta) {
                    Dyer.setBanner((BannerMeta) meta, Arrays.asList(optionValue.split(",")));
                }
            }
            item.setItemMeta(meta);
            if (option != Option.VARIABLE) {
                staticItem = item;
            }
            return item;
        }
        // TEXTURED SKULL
        else if (option == Option.TEXTURE_SKULL) {
            staticItem = Skulls.getCustomSkull(optionValue);
            return staticItem.clone();
        }
        // PLAYER HEAD
        else if (option == Option.HEAD) {
            return Skulls.getPlayerSkull(Vars.replace(player, optionValue)).clone();
        }
        // HDB
        else if (option == Option.HEAD_DATABASE) {
            if (!HookHeadDatabase.isHoooked()) {
                TLocale.sendToConsole("ERROR.HDB", mat);
                return null;
            }
            return HookHeadDatabase.getItem(optionValue);
        }
        // JAVASCRIPT
        else if (option == Option.JAVA_SCRIPT) {
            Object object = JavaScript.run(player, optionValue);
            if (object instanceof ItemStack) {
                return ((ItemStack) object).clone();
            }
        }
        // ORAXEN
        else if (option == Option.ORAXEN) {
            return OraxenItems.getItemById(optionValue).build();
        }
        return null;
    }

    public Option getOption() {
        return option;
    }

    private String[] readMaterial(String[] args) {
        for (int i = 0; i < args.length; i++) {
            if (args[i] == null) {
                continue;
            }
            args[i] = args[i].replace(' ', '_').toUpperCase();
        }
        if (args.length >= 1) {
            String id = args.length >= 2 && NumberUtils.isCreatable(args[1]) ? args[1] : null;
            if (NumberUtils.isCreatable(args[0])) {
                return new String[]{
                        Arrays.stream(Material.values()).filter(m -> m.getId() == NumberUtils.toInt(args[0], 1)).findFirst().orElse(Material.STONE).name(),
                        id
                };
            } else {
                try {
                    return new String[]{Material.valueOf(args[0]).name(), id};
                } catch (IllegalArgumentException e) {
                    Materials mats = Arrays.stream(Materials.values())
                            .filter(m -> Strings.similarDegree(m.name(), args[0]) > TrMenu.SETTINGS.getDouble("OPTIONS.MATERIAL-SIMILAR-DEGREE", 0.8))
                            .max(Comparator.comparingDouble(x -> Strings.similarDegree(x.name(), args[0])))
                            .orElse(Materials.STONE);
                    return new String[]{mats
                            .parseMaterial()
                            .name(),
                            id == null ? String.valueOf(mats.getData()) : id
                    };
                }
            }
        }
        return new String[]{Material.STONE.name(), null};
    }

    public static String[] readOption(String option) {
        for (Option value : Option.values()) {
            Matcher matcher = value.matcher(option);
            if (matcher.find()) {
                String optValue = null;
                String[] args = matcher.group().split(":", 2);
                if (args.length >= 2) {
                    optValue = args[1];
                }
                return new String[]{value.name(), optValue != null ? optValue.substring(0, optValue.length() - 1) : null};
            }
        }
        return null;
    }

    public enum Option {

        /**
         * Example
         * *
         * <head:Arasple>
         * <player-head:Arasple>
         */
        HEAD("<((player|variable)?(-)?head):(.+)?>"),

        /**
         * Example
         * *
         * <skull:Arasple>
         * <custom-head:Arasple>
         */
        TEXTURE_SKULL("<(((custom|texture)?(-)?skull)|custom-head)(:)?(.+)?>"),

        /**
         * Example
         * *
         * <id-value:3>
         * <data-value:2>
         * <data:1>
         * <value:1>
         */
        DATA_VALUE("<(((data|id)?(-)?value)|data|value)(:)?([0-9]+[.]?[0-9]*>)"),

        /**
         * Example
         * *
         * <model-value:3>
         * <model-data:3>
         */
        MODEL_DATA("<((model(-)?(value|data)))(:)?([0-9]+[.]?[0-9]*>)"),

        /**
         * Example
         * *
         * <dye:0,0,0>
         * <dye-leather:3,2>
         */

        DYE_LEATHER("<dye(-)?(leather)?:( )?([0-9]+[,]+[0-9]+[,]+[0-9]*>)"),

        //

        /**
         * Example
         * *
         * <banner:RED SQUARE_BOTTOM_LEFT,WHITE>
         * <banner-style:RED SQUARE_BOTTOM_LEFT,WHITE>
         */
        BANNER("<banner(-)?(dye|color|style)?:( )?(.+>)"),

        /**
         * Example
         * *
         * <head-database:13434>
         * <hdb:435345534>
         */
        HEAD_DATABASE("<((head(-)?(database))|(hdb)):( )?(([0-9]|random)+>)"),

        /**
         * Example
         * *
         * <oraxen:ID>
         */
        ORAXEN("<oraxen:(.+)?>"),

        /**
         * Get ItemStack by JavaScript
         * *
         * <js-item:SCRIPT>
         */
        JAVA_SCRIPT("<((javascript|js)?(-)?item):(.+)?>"),

        /**
         * Example
         * *
         * <Variable>
         */
        VARIABLE("<variable>"),

        JSON("<JSON>"),

        ORIGINAL("<ORIGINAL>");

        private Pattern pattern;

        Option(String pattern) {
            this.pattern = Pattern.compile("(?i)" + pattern);
        }

        public Matcher matcher(String content) {
            return pattern.matcher(content);
        }

    }

}