package com.sainttx.holograms.api.line;

import com.sainttx.holograms.api.Hologram;
import com.sainttx.holograms.api.HologramPlugin;
import com.sainttx.holograms.api.MinecraftVersion;
import com.sainttx.holograms.api.entity.HologramEntity;
import com.sainttx.holograms.api.entity.ItemHolder;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ItemLine extends AbstractLine implements ItemCarryingHologramLine {

    private static final Pattern linePattern = Pattern.compile("((item|icon|itemstack):)(.+)");
    private ItemStack item;
    private ItemHolder entity;

    public ItemLine(Hologram parent, String raw) {
        this(parent, raw, parseItem(raw));
    }

    public ItemLine(Hologram parent, ItemStack item) {
        this(parent, "item:" + itemstackToRaw(item), item);
    }

    ItemLine(Hologram parent, String raw, ItemStack item) {
        super(parent, raw);
        Validate.notNull(item, "Item cannot be null");
        this.item = item;
    }

    // Parses an itemstack from text
    private static ItemStack parseItem(String text) {
        Matcher matcher = linePattern.matcher(text);
        if (matcher.find()) {
            text = matcher.group(3);
        }
        String[] split = text.split(" ");
        short durability = -1;
        String data = split[0];

        if (data.contains(":")) {
            String[] datasplit = data.split(":");
            data = datasplit[0];
            durability = Short.parseShort(datasplit[1]);
        }

        Material material = data.matches("[0-9]+")
                ? Material.getMaterial(Integer.parseInt(data))
                : Material.getMaterial(data.toUpperCase());

        // Throw exception if the material provided was wrong
        if (material == null) {
            throw new IllegalArgumentException("Invalid material " + data);
        }

        int amount;
        try {
            amount = split.length == 1 ? 1 : Integer.parseInt(split[1]);
        } catch (NumberFormatException ex) {
            throw new IllegalArgumentException("Invalid amount \"" + split[1] + "\"", ex);
        }
        ItemStack item = new ItemStack(material, amount, (short) Math.max(0, durability));
        ItemMeta meta = item.getItemMeta();

        // No meta data was provided, we can return here
        if (split.length < 3) {
            return item;
        }

        // Go through all the item meta specified
        for (int i = 2 ; i < split.length ; i++) {
            String[] information = split[i].split(":");

            // Data, name, or lore has been specified
            switch (information[0].toLowerCase()) {
                case "name":
                    // Replace '_' with spaces
                    String name = information[1].replace(' ', ' ');
                    meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name));
                    break;
                case "lore":
                    // If lore was specified 2x for some reason, don't overwrite
                    List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>();
                    String[] loreLines = information[1].split("\\|");

                    // Format all the lines and add them as lore
                    for (String line : loreLines) {
                        line = line.replace('_', ' '); // Replace '_' with space
                        lore.add(ChatColor.translateAlternateColorCodes('&', line));
                    }

                    meta.setLore(lore);
                    break;
                case "data":
                    short dataValue = Short.parseShort(information[1]);
                    item.setDurability(dataValue);
                default:
                    // Try parsing enchantment if it was nothing else
                    Enchantment ench = Enchantment.getByName(information[0].toUpperCase());
                    int level = Integer.parseInt(information[1]);

                    if (ench != null) {
                        meta.addEnchant(ench, level, true);
                    } else {
                        throw new IllegalArgumentException("Invalid enchantment " + information[0]);
                    }
                    break;
            }
        }

        item.setItemMeta(meta);
        return item;
    }

    // Converts an ItemStack to raw representation
    static String itemstackToRaw(ItemStack itemstack) {
        StringBuilder sb = new StringBuilder();
        sb.append(itemstack.getType().toString()); // append type
        sb.append(':').append(itemstack.getDurability()); // append durability
        sb.append(' ').append(itemstack.getAmount()); // append amount

        if (itemstack.hasItemMeta()) {
            ItemMeta meta = itemstack.getItemMeta();

            // append name
            if (meta.hasDisplayName()) {
                sb.append(' ').append("name:").append(meta.getDisplayName().replace(' ', '_'));
            }

            // append lore
            if (meta.hasLore()) {
                sb.append(' ').append("lore:");
                Iterator<String> iterator = meta.getLore().iterator();
                while (iterator.hasNext()) {
                    sb.append(iterator.next().replace(' ', '_'));
                    if (iterator.hasNext()) {
                        sb.append('|');
                    }
                }
            }

            // append enchantments
            meta.getEnchants().forEach((ench, level) -> {
                sb.append(' ').append(ench.getName()).append(':').append(level);
            });
        }

        return sb.toString();
    }

    @Override
    public void setLocation(Location location) {
        super.setLocation(location);
        if (entity != null) {
            entity.setPosition(location.getX(), location.getY(), location.getZ());
        }
    }

    @Override
    public void hide() {
        if (!isHidden()) {
            entity.setMount(null);
            entity.remove();
            entity = null;
        }
    }

    @Override
    public boolean show() {
        if (isHidden()) {
            HologramPlugin plugin = JavaPlugin.getPlugin(HologramPlugin.class);
            entity = plugin.getEntityController().spawnItemHolder(this, getLocation(), item);
        }
        return true;
    }

    @Override
    public boolean isHidden() {
        return entity == null;
    }

    @Override
    public ItemStack getItem() {
        return item.clone();
    }

    @Override
    public void setItem(ItemStack item) {
        this.item = item.clone();
        entity.setItem(this.item);
    }

    @Override
    public double getHeight() {
        HologramPlugin plugin = JavaPlugin.getPlugin(HologramPlugin.class);
        if (plugin.getEntityController().getMinecraftVersion() == MinecraftVersion.V1_8_R1) {
            return 1.60;
        }
        return 0.5;
    }
}