package net.minecraft.client.sounds;

import net.minecraft.client.resources.sounds.SoundEventRegistrationSerializer;
import net.minecraft.network.chat.Component;
import com.google.gson.GsonBuilder;
import org.apache.logging.log4j.LogManager;
import net.minecraft.sounds.SoundSource;
import net.minecraft.client.Camera;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.resources.sounds.TickableSoundInstance;
import java.util.Collection;
import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
import java.lang.reflect.Type;
import java.io.Reader;
import net.minecraft.util.GsonHelper;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.io.InputStream;
import net.minecraft.core.Registry;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.network.chat.TranslatableComponent;
import java.util.List;
import java.util.Iterator;
import java.io.IOException;
import net.minecraft.client.resources.sounds.SoundEventRegistration;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.util.profiling.ProfilerFiller;
import com.google.common.collect.Maps;
import net.minecraft.client.Options;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;
import java.lang.reflect.ParameterizedType;
import com.google.gson.Gson;
import org.apache.logging.log4j.Logger;
import net.minecraft.client.resources.sounds.Sound;
import net.minecraft.server.packs.resources.SimplePreparableReloadListener;

public class SoundManager extends SimplePreparableReloadListener<Preparations> {
    public static final Sound EMPTY_SOUND;
    private static final Logger LOGGER;
    private static final Gson GSON;
    private static final ParameterizedType SOUND_EVENT_REGISTRATION_TYPE;
    private final Map<ResourceLocation, WeighedSoundEvents> registry;
    private final SoundEngine soundEngine;
    
    public SoundManager(final ResourceManager zb, final Options dbr) {
        this.registry = Maps.newHashMap();
        this.soundEngine = new SoundEngine(this, dbr, zb);
    }
    
    @Override
    protected Preparations prepare(final ResourceManager zb, final ProfilerFiller aio) {
        final Preparations a4 = new Preparations();
        aio.startTick();
        for (final String string6 : zb.getNamespaces()) {
            aio.push(string6);
            try {
                final List<Resource> list7 = zb.getResources(new ResourceLocation(string6, "sounds.json"));
                for (final Resource za9 : list7) {
                    aio.push(za9.getSourceName());
                    try {
                        aio.push("parse");
                        final Map<String, SoundEventRegistration> map10 = getEventFromJson(za9.getInputStream());
                        aio.popPush("register");
                        for (final Map.Entry<String, SoundEventRegistration> entry12 : map10.entrySet()) {
                            a4.handleRegistration(new ResourceLocation(string6, entry12.getKey()), entry12.getValue(), zb);
                        }
                        aio.pop();
                    }
                    catch (RuntimeException runtimeException10) {
                        SoundManager.LOGGER.warn("Invalid sounds.json in resourcepack: '{}'", za9.getSourceName(), runtimeException10);
                    }
                    aio.pop();
                }
            }
            catch (IOException ex) {}
            aio.pop();
        }
        aio.endTick();
        return a4;
    }
    
    @Override
    protected void apply(final Preparations a, final ResourceManager zb, final ProfilerFiller aio) {
        a.apply(this.registry, this.soundEngine);
        for (final ResourceLocation sm6 : this.registry.keySet()) {
            final WeighedSoundEvents eea7 = this.registry.get(sm6);
            if (eea7.getSubtitle() instanceof TranslatableComponent) {
                final String string8 = ((TranslatableComponent)eea7.getSubtitle()).getKey();
                if (I18n.exists(string8)) {
                    continue;
                }
                SoundManager.LOGGER.debug("Missing subtitle {} for event: {}", string8, sm6);
            }
        }
        if (SoundManager.LOGGER.isDebugEnabled()) {
            for (final ResourceLocation sm6 : this.registry.keySet()) {
                if (!Registry.SOUND_EVENT.containsKey(sm6)) {
                    SoundManager.LOGGER.debug("Not having sound event for: {}", sm6);
                }
            }
        }
        this.soundEngine.reload();
    }
    
    @Nullable
    protected static Map<String, SoundEventRegistration> getEventFromJson(final InputStream inputStream) {
        try {
            return GsonHelper.<Map<String, SoundEventRegistration>>fromJson(SoundManager.GSON, new InputStreamReader(inputStream, StandardCharsets.UTF_8), SoundManager.SOUND_EVENT_REGISTRATION_TYPE);
        }
        finally {
            IOUtils.closeQuietly(inputStream);
        }
    }
    
    private static boolean validateSoundResource(final Sound ecw, final ResourceLocation sm, final ResourceManager zb) {
        final ResourceLocation sm2 = ecw.getPath();
        if (!zb.hasResource(sm2)) {
            SoundManager.LOGGER.warn("File {} does not exist, cannot add it to event {}", sm2, sm);
            return false;
        }
        return true;
    }
    
    @Nullable
    public WeighedSoundEvents getSoundEvent(final ResourceLocation sm) {
        return this.registry.get(sm);
    }
    
    public Collection<ResourceLocation> getAvailableSounds() {
        return this.registry.keySet();
    }
    
    public void queueTickingSound(final TickableSoundInstance eda) {
        this.soundEngine.queueTickingSound(eda);
    }
    
    public void play(final SoundInstance ecz) {
        this.soundEngine.play(ecz);
    }
    
    public void playDelayed(final SoundInstance ecz, final int integer) {
        this.soundEngine.playDelayed(ecz, integer);
    }
    
    public void updateSource(final Camera dbb) {
        this.soundEngine.updateSource(dbb);
    }
    
    public void pause() {
        this.soundEngine.pause();
    }
    
    public void stop() {
        this.soundEngine.stopAll();
    }
    
    public void destroy() {
        this.soundEngine.destroy();
    }
    
    public void tick(final boolean boolean1) {
        this.soundEngine.tick(boolean1);
    }
    
    public void resume() {
        this.soundEngine.resume();
    }
    
    public void updateSourceVolume(final SoundSource aaj, final float float2) {
        if (aaj == SoundSource.MASTER && float2 <= 0.0f) {
            this.stop();
        }
        this.soundEngine.updateCategoryVolume(aaj, float2);
    }
    
    public void stop(final SoundInstance ecz) {
        this.soundEngine.stop(ecz);
    }
    
    public boolean isActive(final SoundInstance ecz) {
        return this.soundEngine.isActive(ecz);
    }
    
    public void addListener(final SoundEventListener edy) {
        this.soundEngine.addEventListener(edy);
    }
    
    public void removeListener(final SoundEventListener edy) {
        this.soundEngine.removeEventListener(edy);
    }
    
    public void stop(@Nullable final ResourceLocation sm, @Nullable final SoundSource aaj) {
        this.soundEngine.stop(sm, aaj);
    }
    
    public String getDebugString() {
        return this.soundEngine.getDebugString();
    }
    
    static {
        EMPTY_SOUND = new Sound("meta:missing_sound", 1.0f, 1.0f, 1, Sound.Type.FILE, false, false, 16);
        LOGGER = LogManager.getLogger();
        GSON = new GsonBuilder().registerTypeHierarchyAdapter(Component.class, new Component.Serializer()).registerTypeAdapter(SoundEventRegistration.class, new SoundEventRegistrationSerializer()).create();
        SOUND_EVENT_REGISTRATION_TYPE = new ParameterizedType() {
            @Override
            public Type[] getActualTypeArguments() {
                return new Type[] { String.class, SoundEventRegistration.class };
            }
            
            @Override
            public Type getRawType() {
                return Map.class;
            }
            
            @Override
            public Type getOwnerType() {
                return null;
            }
        };
    }
    
    public static class Preparations {
        private final Map<ResourceLocation, WeighedSoundEvents> registry;
        
        protected Preparations() {
            this.registry = Maps.newHashMap();
        }
        
        private void handleRegistration(final ResourceLocation sm, final SoundEventRegistration ecx, final ResourceManager zb) {
            WeighedSoundEvents eea5 = this.registry.get(sm);
            final boolean boolean6 = eea5 == null;
            if (boolean6 || ecx.isReplace()) {
                if (!boolean6) {
                    SoundManager.LOGGER.debug("Replaced sound event location {}", sm);
                }
                eea5 = new WeighedSoundEvents(sm, ecx.getSubtitle());
                this.registry.put(sm, eea5);
            }
            for (final Sound ecw8 : ecx.getSounds()) {
                final ResourceLocation sm2 = ecw8.getLocation();
                Weighted<Sound> eeb10 = null;
                switch (ecw8.getType()) {
                    case FILE: {
                        if (!validateSoundResource(ecw8, sm, zb)) {
                            continue;
                        }
                        eeb10 = ecw8;
                        break;
                    }
                    case SOUND_EVENT: {
                        eeb10 = new Weighted<Sound>() {
                            @Override
                            public int getWeight() {
                                final WeighedSoundEvents eea2 = Preparations.this.registry.get(sm2);
                                return (eea2 == null) ? 0 : eea2.getWeight();
                            }
                            
                            @Override
                            public Sound getSound() {
                                final WeighedSoundEvents eea2 = Preparations.this.registry.get(sm2);
                                if (eea2 == null) {
                                    return SoundManager.EMPTY_SOUND;
                                }
                                final Sound ecw3 = eea2.getSound();
                                return new Sound(ecw3.getLocation().toString(), ecw3.getVolume() * ecw8.getVolume(), ecw3.getPitch() * ecw8.getPitch(), ecw8.getWeight(), Sound.Type.FILE, ecw3.shouldStream() || ecw8.shouldStream(), ecw3.shouldPreload(), ecw3.getAttenuationDistance());
                            }
                            
                            @Override
                            public void preloadIfRequired(final SoundEngine edw) {
                                final WeighedSoundEvents eea3 = Preparations.this.registry.get(sm2);
                                if (eea3 == null) {
                                    return;
                                }
                                eea3.preloadIfRequired(edw);
                            }
                        };
                        break;
                    }
                    default: {
                        throw new IllegalStateException("Unknown SoundEventRegistration type: " + ecw8.getType());
                    }
                }
                eea5.addSound(eeb10);
            }
        }
        
        public void apply(final Map<ResourceLocation, WeighedSoundEvents> map, final SoundEngine edw) {
            map.clear();
            for (final Map.Entry<ResourceLocation, WeighedSoundEvents> entry5 : this.registry.entrySet()) {
                map.put(entry5.getKey(), entry5.getValue());
                entry5.getValue().preloadIfRequired(edw);
            }
        }
    }
}
