package leigh.minecraft.link.quickshop;

import leigh.eo.EO;
import leigh.eo.EOLoop;
import leigh.eo.json.JsonInput;
import leigh.eo.yaml.YamlInput;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.minecraft.MarblesUtil;
import leigh.minecraft.link.PlayerNameProvider;
import leigh.minecraft.link.sdk.schema.v1.ShopElementDAO;
import leigh.minecraft.link.sdk.schema.v1.ShopEnchantElementDAO;
import org.apache.commons.lang.WordUtils;

import java.io.*;
import java.util.Locale;
import java.util.UUID;


/**
 * This class reads a QuickShop exports file and converts it into <code>EO</code> format for transmission on the wire.
 * <p>
 * Note that this class does not produce the file; it must be created with the <code>qs export</code> command which
 * may only be run from the Minecraft server console.
 *
 * @author Alex Leigh
 * @author Brian Hair
 */
public class QuickShopReader {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(QuickShopReader.class);
    public static final String PARAM_X = "x";
    public static final String PARAM_Y = "y";
    public static final String PARAM_Z = "z";
    public static final String PARAM_PRICE = "price";
    public static final String PARAM_OWNER = "owner";
    public static final String PARAM_TYPE = "type";
    public static final String PARAM_WORLD = "world";
    public static final String PARAM_MODERATORS = "moderators";

    private final EOLoop shops = new EOLoop();

    public QuickShopReader(File exports, PlayerNameProvider nameProvider) throws IOException {
        InputStream is = new BufferedInputStream(new FileInputStream(exports));
        JsonInput jin = new JsonInput(is);

        int badCount = 0;
        int items = 0;

        while (true) {
            EO rawShop = jin.readEo();
            if (rawShop == null) break;

            logger.trace("Read raw shop: {}", rawShop);

            EO quickShop = ShopElementDAO.create();

            ShopElementDAO.setX(quickShop, rawShop.get(PARAM_X).toString());
            ShopElementDAO.setY(quickShop, rawShop.get(PARAM_Y).toString());
            ShopElementDAO.setZ(quickShop, rawShop.get(PARAM_Z).toString());

            ShopElementDAO.setPrice(quickShop, rawShop.get(PARAM_PRICE).toString());
            ShopElementDAO.setPriceLabel(quickShop,
                    MarblesUtil.formatGeneric(Double.parseDouble(rawShop.get(PARAM_PRICE).toString())));
            quickShop.setMetaValue(ShopElementDAO.KEY_PRICE, "currencyPrefix", "M", false);
            ShopElementDAO.setWorld(quickShop, rawShop.get(PARAM_WORLD).toString());

            String modsJson = rawShop.getValueString(PARAM_MODERATORS);
            if(modsJson!=null) {
                JsonInput ownerReader = new JsonInput(new StringBufferInputStream(modsJson));
                EO moderators = ownerReader.readEo();
                UUID id =  UUID.fromString(moderators.getValueString(PARAM_OWNER));
                ShopElementDAO.setOwner(quickShop, id);
                ShopElementDAO.setOwnerLabel(quickShop,nameProvider.getPlayerName(id));
            }

            // We allow this to bomb, because some malformed entries seem to. If-so, we just fail to emit them.

            try {
                YamlInput yin = new YamlInput(new StringBufferInputStream(rawShop.getValueString("item")));
                EO obj = yin.readEo();
                items++;
                EOLoop itemInfo = obj.getValueLoop("item");
                // It is not 1 object with 2 parameters, it is 2 objects with 1
                // parameter each. One pass gets the item name, other pass
                // gets the enchantments if they exist.
                for (EO itemObj : itemInfo) {
                    if (itemObj.contains("type")) {
                        String itemName = itemObj.getValue("type").toString().toLowerCase(Locale.getDefault());
                        ShopElementDAO.setItem(quickShop, itemName);
                        ShopElementDAO.setItemLabel(quickShop,
                                WordUtils.capitalize(itemName.replace("_", " ")));
                    } else if (itemObj.contains("meta")) {
                        EOLoop meta = itemObj.getValueLoop("meta");
                        for (EO metaObj : meta) {
                            EOLoop storedEnchants = null;
                            if (metaObj.contains("stored-enchants")) {
                                // Enchanted book
                                storedEnchants = metaObj.getValueLoop("stored-enchants");
                            } else if (metaObj.contains("enchants")) {
                                // Other Items
                                storedEnchants = metaObj.getValueLoop("enchants");
                            } else {
                                // No Enchants
                                continue;
                            }

                            EOLoop shopEnchants = ShopElementDAO.getEnchants(quickShop);

                            for (EO enchant : storedEnchants) {
                                for (String enchantName : enchant.getKeys()) {
                                    EO storeEnchant = ShopEnchantElementDAO.create();
                                    ShopEnchantElementDAO.setName(storeEnchant, enchantName.toLowerCase(Locale.getDefault()));
                                    ShopEnchantElementDAO.setNameLabel(storeEnchant, friendlyEnchantName(enchantName));
                                    ShopEnchantElementDAO.setLevel(storeEnchant,
                                            Integer.parseInt(enchant.getValue(enchantName).toString()));
                                    shopEnchants.add(storeEnchant);
                                }
                            }
                        }
                    }
                }

                // NOTE! We cutely do this here to suppress the shop if it otherwise failed
                shops.add(quickShop);

            } catch (Exception e) {
                badCount++;
            }

            int shopType = rawShop.getValueInteger("type");
            if (shopType == 0) {
                quickShop.setValue(PARAM_TYPE, "sell");
            } else {
                quickShop.setValue(PARAM_TYPE, "buy");
            }
        }

        logger.debug("Parsed QuickShop file. [size: {}] [error: {}]", items, badCount);
    }

    /**
     * Convert a QuickShops enchantment name to a friendly name. The QS name seems to be the internal Minecraft name.
     * An updated list of names can be found here:
     * https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/enchantments/Enchantment.html
     * which you can map to the friendly names here:
     * https://minecraft.fandom.com/wiki/Enchanting
     */
    private String friendlyEnchantName(String enchantName) {
        switch (enchantName) {
            case "ARROW_DAMAGE":
                return "Power";
            case "ARROW_FIRE":
                return "Flame";
            case "ARROW_INFINITE":
                return "Infinity";
            case "ARROW_KNOCKBACK":
                return "Punch";
            case "BINDING_CURSE":
                return "Curse of Binding";
            case "CHANNELING":
                return "Channeling";
            case "DAMAGE_ALL":
                return "Sharpness";
            case "DAMAGE_ARTHROPODS":
                return "Bane of Arthropods";
            case "DAMAGE_UNDEAD":
                return "Smite";
            case "DEPTH_STRIDER":
                return "Depth Strider";
            case "DIG_SPEED":
                return "Efficiency";
            case "DURABILITY":
                return "Unbreaking";
            case "FIRE_ASPECT":
                return "Fire Aspect";
            case "FROST_WALKER":
                return "Frost Walker";
            case "IMPALING":
                return "Impaling";
            case "KNOCKBACK":
                return "Knockback";
            case "LOOT_BONUS_BLOCKS":
                return "Fortune";
            case "LOOT_BONUS_MOBS":
                return "Looting";
            case "LOYALTY":
                return "Loyalty";
            case "LUCK":
                return "Luck of the Sea";
            case "LURE":
                return "Lure";
            case "MENDING":
                return "Mending";
            case "MULTISHOT":
                return "Multishot";
            case "OXYGEN":
                return "Respiration";
            case "PIERCING":
                return "Piercing";
            case "PROTECTION_ENVIRONMENTAL":
                return "Protection";
            case "PROTECTION_EXPLOSIONS":
                return "Blast Protection";
            case "PROTECTION_FALL":
                return "Feather Falling";
            case "PROTECTION_FIRE":
                return "Fire Protection";
            case "PROTECTION_PROJECTILE":
                return "Projectile Protection";
            case "QUICK_CHARGE":
                return "Quick Charge";
            case "RIPTIDE":
                return "Riptide";
            case "SILK_TOUCH":
                return "Silk Touch";
            case "SOUL_SPEED":
                return "Soul Speed";
            case "SWEEPING_EDGE":
                return "Sweeping Edge";
            case "THORNS":
                return "Thorns";
            case "VANISHING_CURSE":
                return "Curse of Vanishing";
            case "WATER_WORKER":
                return "Aqua Affinity";
            default:
                return enchantName;
        }
    }

    public EOLoop getShops() {
        return shops;
    }
}
