// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.cosmetics;

import com.hypixel.hytale.common.util.ArrayUtil;
import com.hypixel.hytale.common.util.RandomUtil;
import java.util.Map;
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
import java.util.logging.Level;
import com.hypixel.hytale.logger.HytaleLogger;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.protocol.PlayerSkin;
import java.util.Random;
import com.hypixel.hytale.server.core.asset.LoadAssetEvent;
import joptsimple.OptionSpec;
import com.hypixel.hytale.server.core.Options;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.cosmetics.commands.EmoteCommand;
import com.hypixel.hytale.server.core.asset.AssetModule;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class CosmeticsModule extends JavaPlugin
{
    public static final PluginManifest MANIFEST;
    private static CosmeticsModule INSTANCE;
    private CosmeticRegistry registry;
    
    public CosmeticsModule(@Nonnull final JavaPluginInit init) {
        super(init);
        CosmeticsModule.INSTANCE = this;
    }
    
    @Override
    protected void setup() {
        this.registry = new CosmeticRegistry(AssetModule.get().getBaseAssetPack());
        this.getCommandRegistry().registerCommand(new EmoteCommand());
        if (Options.getOptionSet().has(Options.VALIDATE_ASSETS)) {
            this.getEventRegistry().register((short)64, LoadAssetEvent.class, this::validateGeneratedSkin);
        }
    }
    
    public CosmeticRegistry getRegistry() {
        return this.registry;
    }
    
    private void validateGeneratedSkin(@Nonnull final LoadAssetEvent eventType) {
        for (int i = 0; i < 10; ++i) {
            final PlayerSkin skin = this.generateRandomSkin(new Random(i));
            try {
                this.validateSkin(skin);
            }
            catch (final InvalidSkinException e) {
                eventType.failed(true, e.getMessage());
                return;
            }
        }
    }
    
    @Nullable
    public Model createRandomModel(@Nonnull final Random random) {
        final PlayerSkin skin = this.generateRandomSkin(random);
        return get().createModel(skin);
    }
    
    @Nullable
    public Model createModel(@Nonnull final PlayerSkin skin) {
        return this.createModel(skin, 1.0f);
    }
    
    @Nullable
    public Model createModel(@Nonnull final PlayerSkin skin, final float scale) {
        try {
            this.validateSkin(skin);
        }
        catch (final InvalidSkinException e) {
            this.getLogger().at(Level.WARNING).withCause(e).log("Was passed an invalid skin %s", skin);
            return null;
        }
        final ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset("Player");
        return Model.createScaledModel(modelAsset, scale, null);
    }
    
    public void validateSkin(@Nonnull final PlayerSkin skin) throws InvalidSkinException {
        if (skin == null) {
            throw new InvalidSkinException("skin", (String)null);
        }
        if (skin.face == null || !this.registry.getFaces().containsKey(skin.face)) {
            throw new InvalidSkinException("face", skin.face);
        }
        if (skin.ears == null || !this.registry.getEars().containsKey(skin.ears)) {
            throw new InvalidSkinException("ears", skin.ears);
        }
        if (skin.mouth == null || !this.registry.getMouths().containsKey(skin.mouth)) {
            throw new InvalidSkinException("mouth", skin.mouth);
        }
        if (!this.isValidAttachment(this.registry.getBodyCharacteristics(), skin.bodyCharacteristic, true)) {
            throw new InvalidSkinException("body", skin.bodyCharacteristic);
        }
        if (!this.isValidAttachment(this.registry.getUnderwear(), skin.underwear, true)) {
            throw new InvalidSkinException("underwear", skin.underwear);
        }
        if (!this.isValidAttachment(this.registry.getEyes(), skin.eyes, true)) {
            throw new InvalidSkinException("eyes", skin.eyes);
        }
        if (!this.isValidAttachment(this.registry.getSkinFeatures(), skin.skinFeature)) {
            throw new InvalidSkinException("skin feature", skin.skinFeature);
        }
        if (!this.isValidAttachment(this.registry.getEyebrows(), skin.eyebrows)) {
            throw new InvalidSkinException("eyebrows", skin.eyebrows);
        }
        if (!this.isValidAttachment(this.registry.getPants(), skin.pants)) {
            throw new InvalidSkinException("pants", skin.pants);
        }
        if (!this.isValidAttachment(this.registry.getOverpants(), skin.overpants)) {
            throw new InvalidSkinException("overpants", skin.overpants);
        }
        if (!this.isValidAttachment(this.registry.getShoes(), skin.shoes)) {
            throw new InvalidSkinException("shoes", skin.shoes);
        }
        if (!this.isValidAttachment(this.registry.getUndertops(), skin.undertop)) {
            throw new InvalidSkinException("undertop", skin.undertop);
        }
        if (!this.isValidAttachment(this.registry.getOvertops(), skin.overtop)) {
            throw new InvalidSkinException("overtop", skin.overtop);
        }
        if (!this.isValidAttachment(this.registry.getGloves(), skin.gloves)) {
            throw new InvalidSkinException("gloves", skin.gloves);
        }
        if (!this.isValidAttachment(this.registry.getHeadAccessories(), skin.headAccessory)) {
            throw new InvalidSkinException("head accessory", skin.headAccessory);
        }
        if (!this.isValidAttachment(this.registry.getFaceAccessories(), skin.faceAccessory)) {
            throw new InvalidSkinException("face accessory", skin.faceAccessory);
        }
        if (!this.isValidAttachment(this.registry.getEarAccessories(), skin.earAccessory)) {
            throw new InvalidSkinException("ear accessory", skin.earAccessory);
        }
        if (!this.isValidHaircutAttachment(skin.haircut, skin.headAccessory)) {
            throw new InvalidSkinException("haircut", skin.haircut);
        }
        if (!this.isValidAttachment(this.registry.getFacialHairs(), skin.facialHair)) {
            throw new InvalidSkinException("facial hair", skin.facialHair);
        }
        if (!this.isValidAttachment(this.registry.getCapes(), skin.cape)) {
            throw new InvalidSkinException("cape", skin.cape);
        }
    }
    
    private boolean isValidAttachment(@Nonnull final Map<String, PlayerSkinPart> map, final String id) {
        return this.isValidAttachment(map, id, false);
    }
    
    private boolean isValidTexture(@Nonnull final PlayerSkinPart part, final String variantId, final String textureId) {
        if (part.getGradientSet() != null && this.registry.getGradientSets().get(part.getGradientSet()).getGradients().containsKey(textureId)) {
            return true;
        }
        if (part.getVariants() != null) {
            return part.getVariants().get(variantId).getTextures().containsKey(textureId);
        }
        return part.getTextures().containsKey(textureId);
    }
    
    private boolean isValidAttachment(@Nonnull final Map<String, PlayerSkinPart> map, @Nullable final String id, final boolean required) {
        if (id == null) {
            return !required;
        }
        final String[] idParts = id.split("\\.");
        final PlayerSkinPart skinPart = map.get(idParts[0]);
        if (skinPart == null) {
            return false;
        }
        final String variantId = (idParts.length > 2 && !idParts[2].isEmpty()) ? idParts[2] : null;
        return (skinPart.getVariants() == null || skinPart.getVariants().containsKey(variantId)) && this.isValidTexture(skinPart, variantId, idParts[1]);
    }
    
    private boolean isValidHaircutAttachment(@Nullable final String haircutId, @Nullable final String headAccessoryId) {
        if (haircutId == null) {
            return true;
        }
        final Map<String, PlayerSkinPart> haircuts = this.registry.getHaircuts();
        String[] idParts = haircutId.split("\\.");
        final String haircutAssetId = idParts[0];
        final String haircutAssetTextureId = (idParts.length > 1 && !idParts[1].isEmpty()) ? idParts[1] : null;
        if (headAccessoryId != null) {
            idParts = headAccessoryId.split("\\.");
            final String headAccessoryAssetId = idParts[0];
            final PlayerSkinPart headAccessoryPart = this.registry.getHeadAccessories().get(headAccessoryAssetId);
            if (headAccessoryPart != null) {
                switch (headAccessoryPart.getHeadAccessoryType()) {
                    case HalfCovering: {
                        final PlayerSkinPart haircutPart = haircuts.get(haircutAssetId);
                        if (haircutPart == null) {
                            return false;
                        }
                        if (haircutPart.doesRequireGenericHaircut()) {
                            final PlayerSkinPart baseHaircutPart = haircuts.get("Generic" + String.valueOf(haircutPart.getHairType()));
                            return this.isValidAttachment(haircuts, baseHaircutPart.getId() + "." + haircutAssetTextureId, false);
                        }
                        break;
                    }
                    case FullyCovering: {
                        return this.isValidAttachment(haircuts, haircutId);
                    }
                }
            }
        }
        return this.isValidAttachment(haircuts, haircutId);
    }
    
    public static CosmeticsModule get() {
        return CosmeticsModule.INSTANCE;
    }
    
    @Nonnull
    public PlayerSkin generateRandomSkin(@Nonnull final Random random) {
        final String bodyCharacteristic = this.randomSkinPart(this.registry.getBodyCharacteristics(), true, random);
        final String underwear = this.randomSkinPart(this.registry.getUnderwear(), true, random);
        final String face = this.randomSkinPart(this.registry.getFaces(), true, false, random);
        final String ears = this.randomSkinPart(this.registry.getEars(), true, false, random);
        final String mouth = this.randomSkinPart(this.registry.getMouths(), true, false, random);
        final String eyes = this.randomSkinPart(this.registry.getEyes(), true, random);
        String facialHair = null;
        if (random.nextInt(10) > 4) {
            facialHair = this.randomSkinPart(this.registry.getFacialHairs(), random);
        }
        final String haircut = this.randomSkinPart(this.registry.getHaircuts(), random);
        final String eyebrows = this.randomSkinPart(this.registry.getEyebrows(), random);
        final String pants = this.randomSkinPart(this.registry.getPants(), random);
        final String overpants = null;
        final String undertop = this.randomSkinPart(this.registry.getUndertops(), random);
        final String overtop = this.randomSkinPart(this.registry.getOvertops(), random);
        final String shoes = this.randomSkinPart(this.registry.getShoes(), random);
        String headAccessory = null;
        if (random.nextInt(10) > 8) {
            headAccessory = this.randomSkinPart(this.registry.getHeadAccessories(), random);
        }
        String faceAccessory = null;
        if (random.nextInt(10) > 8) {
            faceAccessory = this.randomSkinPart(this.registry.getFaceAccessories(), random);
        }
        String earAccessory = null;
        if (random.nextInt(10) > 8) {
            earAccessory = this.randomSkinPart(this.registry.getEarAccessories(), random);
        }
        String skinFeature = null;
        if (random.nextInt(10) > 8) {
            skinFeature = this.randomSkinPart(this.registry.getSkinFeatures(), random);
        }
        final String gloves = null;
        return new PlayerSkin(bodyCharacteristic, underwear, face, eyes, ears, mouth, facialHair, haircut, eyebrows, pants, overpants, undertop, overtop, shoes, headAccessory, faceAccessory, earAccessory, skinFeature, gloves, null);
    }
    
    @Nullable
    private String randomSkinPart(@Nonnull final Map<String, PlayerSkinPart> map, @Nonnull final Random random) {
        return this.randomSkinPart(map, false, random);
    }
    
    @Nullable
    private String randomSkinPart(@Nonnull final Map<String, PlayerSkinPart> map, final boolean required, @Nonnull final Random random) {
        return this.randomSkinPart(map, required, true, random);
    }
    
    @Nullable
    private String randomSkinPart(@Nonnull final Map<String, PlayerSkinPart> map, final boolean required, final boolean color, @Nonnull final Random random) {
        final PlayerSkinPart[] arr = map.values().toArray(PlayerSkinPart[]::new);
        final PlayerSkinPart part = required ? RandomUtil.selectRandom(arr, random) : RandomUtil.selectRandomOrNull(arr, random);
        if (part == null) {
            return null;
        }
        if (!color) {
            return part.getId();
        }
        String[] colors = ArrayUtil.EMPTY_STRING_ARRAY;
        if (part.getGradientSet() != null) {
            colors = this.registry.getGradientSets().get(part.getGradientSet()).getGradients().keySet().toArray(String[]::new);
        }
        Map<String, PlayerSkinPartTexture> textures = part.getTextures();
        String variantId = null;
        if (part.getVariants() != null) {
            variantId = RandomUtil.selectRandom((String[])part.getVariants().keySet().toArray(String[]::new), random);
            textures = part.getVariants().get(variantId).getTextures();
        }
        if (textures != null) {
            colors = ArrayUtil.combine(colors, (String[])textures.keySet().toArray(String[]::new));
        }
        final String colorId = RandomUtil.selectRandom(colors, random);
        if (variantId == null) {
            return part.getId() + "." + colorId;
        }
        return part.getId() + "." + colorId + "." + variantId;
    }
    
    static {
        MANIFEST = PluginManifest.corePlugin(CosmeticsModule.class).build();
    }
    
    public static class InvalidSkinException extends Exception
    {
        private final String partType;
        private final String partId;
        
        public InvalidSkinException(final String partType, @Nullable final String partId) {
            super(formatMessage(partType, partId));
            this.partType = partType;
            this.partId = partId;
        }
        
        private static String formatMessage(final String partType, @Nullable final String partId) {
            if (partId == null) {
                return "Missing required " + partType;
            }
            return "Unknown " + partType + ": " + partId;
        }
        
        public String getPartType() {
            return this.partType;
        }
        
        @Nullable
        public String getPartId() {
            return this.partId;
        }
    }
}
