package net.minecraft.client.sounds;

import java.lang.reflect.Type;
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 net.minecraft.core.Registry;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.network.chat.TranslatableComponent;
import java.io.Reader;
import java.io.InputStream;
import java.util.List;
import java.util.Iterator;
import java.io.IOException;
import net.minecraft.util.GsonHelper;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
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 net.minecraft.client.resources.sounds.SoundEventRegistration;
import java.util.Map;
import com.google.gson.reflect.TypeToken;
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 TypeToken<Map<String, SoundEventRegistration>> SOUND_EVENT_REGISTRATION_TYPE;
    private final Map<ResourceLocation, WeighedSoundEvents> registry;
    private final SoundEngine soundEngine;
    
    public SoundManager(final ResourceManager abc, final Options dmb) {
        this.registry = Maps.newHashMap();
        this.soundEngine = new SoundEngine(this, dmb, abc);
    }
    
    @Override
    protected Preparations prepare(final ResourceManager abc, final ProfilerFiller ami) {
        final Preparations a4 = new Preparations();
        ami.startTick();
        for (final String string6 : abc.getNamespaces()) {
            ami.push(string6);
            try {
                final List<Resource> list7 = abc.getResources(new ResourceLocation(string6, "sounds.json"));
                for (final Resource abb9 : list7) {
                    ami.push(abb9.getSourceName());
                    try (final InputStream inputStream10 = abb9.getInputStream();
                         final Reader reader12 = new InputStreamReader(inputStream10, StandardCharsets.UTF_8)) {
                        ami.push("parse");
                        final Map<String, SoundEventRegistration> map14 = GsonHelper.<Map<String, SoundEventRegistration>>fromJson(SoundManager.GSON, reader12, SoundManager.SOUND_EVENT_REGISTRATION_TYPE);
                        ami.popPush("register");
                        for (final Map.Entry<String, SoundEventRegistration> entry16 : map14.entrySet()) {
                            a4.handleRegistration(new ResourceLocation(string6, entry16.getKey()), entry16.getValue(), abc);
                        }
                        ami.pop();
                    }
                    catch (RuntimeException runtimeException10) {
                        SoundManager.LOGGER.warn("Invalid sounds.json in resourcepack: '{}'", abb9.getSourceName(), runtimeException10);
                    }
                    ami.pop();
                }
            }
            catch (IOException ex) {}
            ami.pop();
        }
        ami.endTick();
        return a4;
    }
    
    @Override
    protected void apply(final Preparations a, final ResourceManager abc, final ProfilerFiller ami) {
        a.apply(this.registry, this.soundEngine);
        for (final ResourceLocation uh6 : this.registry.keySet()) {
            final WeighedSoundEvents epo7 = this.registry.get(uh6);
            if (epo7.getSubtitle() instanceof TranslatableComponent) {
                final String string8 = ((TranslatableComponent)epo7.getSubtitle()).getKey();
                if (I18n.exists(string8)) {
                    continue;
                }
                SoundManager.LOGGER.debug("Missing subtitle {} for event: {}", string8, uh6);
            }
        }
        if (SoundManager.LOGGER.isDebugEnabled()) {
            for (final ResourceLocation uh6 : this.registry.keySet()) {
                if (!Registry.SOUND_EVENT.containsKey(uh6)) {
                    SoundManager.LOGGER.debug("Not having sound event for: {}", uh6);
                }
            }
        }
        this.soundEngine.reload();
    }
    
    private static boolean validateSoundResource(final Sound eoj, final ResourceLocation uh, final ResourceManager abc) {
        final ResourceLocation uh2 = eoj.getPath();
        if (!abc.hasResource(uh2)) {
            SoundManager.LOGGER.warn("File {} does not exist, cannot add it to event {}", uh2, uh);
            return false;
        }
        return true;
    }
    
    @Nullable
    public WeighedSoundEvents getSoundEvent(final ResourceLocation uh) {
        return this.registry.get(uh);
    }
    
    public Collection<ResourceLocation> getAvailableSounds() {
        return this.registry.keySet();
    }
    
    public void queueTickingSound(final TickableSoundInstance eon) {
        this.soundEngine.queueTickingSound(eon);
    }
    
    public void play(final SoundInstance eom) {
        this.soundEngine.play(eom);
    }
    
    public void playDelayed(final SoundInstance eom, final int integer) {
        this.soundEngine.playDelayed(eom, integer);
    }
    
    public void updateSource(final Camera dlj) {
        this.soundEngine.updateSource(dlj);
    }
    
    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 acm, final float float2) {
        if (acm == SoundSource.MASTER && float2 <= 0.0f) {
            this.stop();
        }
        this.soundEngine.updateCategoryVolume(acm, float2);
    }
    
    public void stop(final SoundInstance eom) {
        this.soundEngine.stop(eom);
    }
    
    public boolean isActive(final SoundInstance eom) {
        return this.soundEngine.isActive(eom);
    }
    
    public void addListener(final SoundEventListener epm) {
        this.soundEngine.addEventListener(epm);
    }
    
    public void removeListener(final SoundEventListener epm) {
        this.soundEngine.removeEventListener(epm);
    }
    
    public void stop(@Nullable final ResourceLocation uh, @Nullable final SoundSource acm) {
        this.soundEngine.stop(uh, acm);
    }
    
    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 TypeToken<Map<String, SoundEventRegistration>>() {};
    }
    
    public static class Preparations {
        private final Map<ResourceLocation, WeighedSoundEvents> registry;
        
        protected Preparations() {
            this.registry = Maps.newHashMap();
        }
        
        private void handleRegistration(final ResourceLocation uh, final SoundEventRegistration eok, final ResourceManager abc) {
            WeighedSoundEvents epo5 = this.registry.get(uh);
            final boolean boolean6 = epo5 == null;
            if (boolean6 || eok.isReplace()) {
                if (!boolean6) {
                    SoundManager.LOGGER.debug("Replaced sound event location {}", uh);
                }
                epo5 = new WeighedSoundEvents(uh, eok.getSubtitle());
                this.registry.put(uh, epo5);
            }
            for (final Sound eoj8 : eok.getSounds()) {
                final ResourceLocation uh2 = eoj8.getLocation();
                Weighted<Sound> epp10 = null;
                switch (eoj8.getType()) {
                    case FILE: {
                        if (!validateSoundResource(eoj8, uh, abc)) {
                            continue;
                        }
                        epp10 = eoj8;
                        break;
                    }
                    case SOUND_EVENT: {
                        epp10 = new Weighted<Sound>() {
                            @Override
                            public int getWeight() {
                                final WeighedSoundEvents epo2 = Preparations.this.registry.get(uh2);
                                return (epo2 == null) ? 0 : epo2.getWeight();
                            }
                            
                            @Override
                            public Sound getSound() {
                                final WeighedSoundEvents epo2 = Preparations.this.registry.get(uh2);
                                if (epo2 == null) {
                                    return SoundManager.EMPTY_SOUND;
                                }
                                final Sound eoj3 = epo2.getSound();
                                return new Sound(eoj3.getLocation().toString(), eoj3.getVolume() * eoj8.getVolume(), eoj3.getPitch() * eoj8.getPitch(), eoj8.getWeight(), Sound.Type.FILE, eoj3.shouldStream() || eoj8.shouldStream(), eoj3.shouldPreload(), eoj3.getAttenuationDistance());
                            }
                            
                            @Override
                            public void preloadIfRequired(final SoundEngine epk) {
                                final WeighedSoundEvents epo3 = Preparations.this.registry.get(uh2);
                                if (epo3 == null) {
                                    return;
                                }
                                epo3.preloadIfRequired(epk);
                            }
                        };
                        break;
                    }
                    default: {
                        throw new IllegalStateException("Unknown SoundEventRegistration type: " + eoj8.getType());
                    }
                }
                epo5.addSound(epp10);
            }
        }
        
        public void apply(final Map<ResourceLocation, WeighedSoundEvents> map, final SoundEngine epk) {
            map.clear();
            for (final Map.Entry<ResourceLocation, WeighedSoundEvents> entry5 : this.registry.entrySet()) {
                map.put(entry5.getKey(), entry5.getValue());
                entry5.getValue().preloadIfRequired(epk);
            }
        }
    }
}
