/*
 * Decompiled with CFR 0.152.
 * 
 * Could not load the following classes:
 *  com.google.gson.JsonArray
 *  com.google.gson.JsonElement
 *  com.google.gson.JsonObject
 *  com.google.gson.JsonParser
 *  meteordevelopment.orbit.EventHandler
 *  net.minecraft.class_332
 *  org.lwjgl.glfw.GLFW
 */
package meteordevelopment.meteorclient.systems.modules.render;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.awt.Color;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.CallSite;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.UnsupportedAudioFileException;
import meteordevelopment.meteorclient.events.game.OpenScreenEvent;
import meteordevelopment.meteorclient.events.render.Render2DEvent;
import meteordevelopment.meteorclient.events.world.TickEvent;
import meteordevelopment.meteorclient.settings.BoolSetting;
import meteordevelopment.meteorclient.settings.DoubleSetting;
import meteordevelopment.meteorclient.settings.SettingGroup;
import meteordevelopment.meteorclient.systems.modules.Categories;
import meteordevelopment.meteorclient.systems.modules.Module;
import meteordevelopment.meteorclient.utils.render.RenderUtils;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.class_332;
import org.lwjgl.glfw.GLFW;

public class Radio
extends Module {
    private static final int CONTROL_BUTTON_HEIGHT = 12;
    private static final int CONTROL_BUTTON_GAP = 4;
    private static final int PLAY_BUTTON_WIDTH = 38;
    private static final int SKIP_BUTTON_WIDTH = 32;
    private static final int VOLUME_HITBOX_PADDING = 2;
    private final SettingGroup sgGeneral;
    private final BoolSetting overlay;
    private final DoubleSetting xPos;
    private final DoubleSetting yPos;
    private final BoolSetting shuffle;
    private final DoubleSetting volume;
    private static final String MANIFEST_URL = "https://pub-6a5d87d6801b41c983c2736e9d0e88ce.r2.dev/radio/manifest.json";
    private final List<File> songQueue;
    private final Random random;
    private volatile boolean isPlaying;
    private volatile boolean paused;
    private volatile boolean loading;
    private Thread audioThread;
    private Thread cloudSyncThread;
    private Thread startupThread;
    private Clip currentClip;
    private int currentSongIndex;
    private String currentSongName;
    private final List<File> songDirectories;
    private long lastRescanMs;
    private long lastCloudSyncMs;
    private boolean bundledExtractAttempted;
    private volatile long playbackSession;
    private volatile boolean awaitingFirstPlayback;
    private volatile long enabledAtMs;
    private volatile boolean worldPlaybackInitialized;
    private final Set<String> failedSongs;
    private boolean dragging;
    private boolean adjustingVolume;
    private boolean leftMouseDownLast;
    private int dragOffsetX;
    private int dragOffsetY;
    private UiInteraction uiInteraction;

    public Radio() {
        super(Categories.Render, "radio", "Plays music.");
        this.sgGeneral = this.settings.getDefaultGroup();
        this.overlay = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("overlay")).description("Shows the radio overlay.")).defaultValue(true)).build());
        this.xPos = this.sgGeneral.add(((DoubleSetting.Builder)((DoubleSetting.Builder)new DoubleSetting.Builder().name("x")).description("X position.")).defaultValue(14.0).min(0.0).max(6000.0).build());
        this.yPos = this.sgGeneral.add(((DoubleSetting.Builder)((DoubleSetting.Builder)new DoubleSetting.Builder().name("y")).description("Y position.")).defaultValue(110.0).min(0.0).max(6000.0).build());
        this.shuffle = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("shuffle")).description("Shuffle playback.")).defaultValue(true)).build());
        this.volume = this.sgGeneral.add(((DoubleSetting.Builder)((DoubleSetting.Builder)new DoubleSetting.Builder().name("volume")).description("Volume.")).defaultValue(5.0).min(0.0).max(100.0).build());
        this.songQueue = new ArrayList<File>();
        this.random = new Random();
        this.isPlaying = false;
        this.paused = false;
        this.loading = false;
        this.currentSongIndex = 0;
        this.currentSongName = "Idle";
        this.songDirectories = new ArrayList<File>();
        this.lastRescanMs = 0L;
        this.lastCloudSyncMs = 0L;
        this.bundledExtractAttempted = false;
        this.playbackSession = 0L;
        this.awaitingFirstPlayback = false;
        this.enabledAtMs = 0L;
        this.worldPlaybackInitialized = false;
        this.failedSongs = Collections.newSetFromMap(new ConcurrentHashMap());
        this.dragging = false;
        this.adjustingVolume = false;
        this.leftMouseDownLast = false;
        this.dragOffsetX = 0;
        this.dragOffsetY = 0;
        this.uiInteraction = UiInteraction.NONE;
    }

    @Override
    public void onActivate() {
        this.enabledAtMs = System.currentTimeMillis();
        ++this.playbackSession;
        this.worldPlaybackInitialized = false;
        this.awaitingFirstPlayback = false;
        this.loading = false;
        this.paused = false;
        this.currentSongName = "Join a server";
        this.failedSongs.clear();
        this.lastCloudSyncMs = 0L;
        this.ensureSongsDirectory();
        this.startPlaybackWhenWorldReady();
        super.onActivate();
    }

    @Override
    public void onDeactivate() {
        ++this.playbackSession;
        this.worldPlaybackInitialized = false;
        this.awaitingFirstPlayback = false;
        this.loading = false;
        this.paused = false;
        this.failedSongs.clear();
        this.dragging = false;
        this.adjustingVolume = false;
        this.leftMouseDownLast = false;
        this.uiInteraction = UiInteraction.NONE;
        this.lastCloudSyncMs = 0L;
        this.stopBackgroundWorkers();
        super.onDeactivate();
    }

    @EventHandler
    private void onTick(TickEvent event) {
        if (!this.isInPlayableWorld()) {
            if (this.worldPlaybackInitialized || this.loading || this.isPlaying || this.awaitingFirstPlayback) {
                this.stopBackgroundWorkers();
                this.worldPlaybackInitialized = false;
            }
            this.awaitingFirstPlayback = false;
            this.loading = false;
            this.currentSongName = "Join a server";
        } else if (!this.worldPlaybackInitialized) {
            this.startPlaybackWhenWorldReady();
        } else {
            long now;
            Clip clip = this.currentClip;
            if (clip != null && clip.isOpen()) {
                this.setVolume(clip, ((Double)this.volume.get()).floatValue());
            }
            if (this.awaitingFirstPlayback) {
                if (clip != null && clip.isOpen() && clip.isRunning()) {
                    this.awaitingFirstPlayback = false;
                    this.loading = false;
                } else {
                    this.loading = true;
                }
            }
            if ((now = System.currentTimeMillis()) - this.lastRescanMs >= 8000L) {
                this.lastRescanMs = now;
                int before = this.getQueueSize();
                this.loadSongs();
                if (!(before != 0 || this.getQueueSize() <= 0 || this.audioThread != null && this.audioThread.isAlive())) {
                    this.startPlayback();
                }
            }
            if (!(now - this.lastCloudSyncMs < 20000L || this.cloudSyncThread != null && this.cloudSyncThread.isAlive())) {
                this.startCloudSync(false);
            }
        }
    }

    @EventHandler
    private void onOpenScreen(OpenScreenEvent event) {
        if (this.isActive() && !this.isInPlayableWorld()) {
            this.stopBackgroundWorkers();
            this.worldPlaybackInitialized = false;
            this.awaitingFirstPlayback = false;
            this.loading = false;
            this.currentSongName = "Join a server";
        }
    }

    private boolean isInPlayableWorld() {
        return this.mc != null && this.mc.field_1687 != null && this.mc.field_1724 != null;
    }

    private void startPlaybackWhenWorldReady() {
        if (this.isActive() && this.isInPlayableWorld()) {
            this.worldPlaybackInitialized = true;
            this.awaitingFirstPlayback = true;
            this.loading = true;
            this.currentSongName = "Loading...";
            this.failedSongs.clear();
            this.startupLoadAsync();
        }
    }

    @EventHandler
    private void onRender2D(Render2DEvent event) {
        if (((Boolean)this.overlay.get()).booleanValue() && this.mc != null && this.mc.field_1724 != null) {
            int fillW;
            String title;
            class_332 context = event.drawContext;
            int x = (int)((Double)this.xPos.get()).doubleValue();
            int y = (int)((Double)this.yPos.get()).doubleValue();
            int width = 206;
            int height = 72;
            int volumeW = width - 20;
            int volumeH = 5;
            int volumeX = x + 10;
            int volumeY = y + 20;
            this.handleUiInput(x, y, width, height, volumeX, volumeY, volumeW, volumeH);
            x = (int)((Double)this.xPos.get()).doubleValue();
            y = (int)((Double)this.yPos.get()).doubleValue();
            volumeX = x + 10;
            volumeY = y + 20;
            Color accent = new Color(120, 180, 255);
            int alpha = 200;
            Color card = new Color(28, 30, 36, alpha);
            Color border = new Color(accent.getRed(), accent.getGreen(), accent.getBlue(), 120);
            Color subText = new Color(186, 190, 198, 255);
            Color barBg = new Color(60, 65, 74, 220);
            Color volumeFillColor = new Color(120, 205, 168, 255);
            RenderUtils.renderRoundedQuad(context.method_51448(), card, x, y, x + width, y + height, 8.0, 20.0);
            RenderUtils.renderRoundedOutline(context, border, x, y, x + width, y + height, 8.0, 8.0, 8.0, 8.0, 1.0, 20.0);
            Object volumeLabel = "Volume " + ((Double)this.volume.get()).intValue() + "%";
            volumeLabel = this.truncateText((String)volumeLabel, width - 20);
            context.method_51433(this.mc.field_1772, (String)volumeLabel, x + 10, y + 9, subText.getRGB(), false);
            RenderUtils.renderRoundedQuad(context.method_51448(), barBg, volumeX, volumeY, volumeX + volumeW, volumeY + volumeH, 2.0, 20.0);
            int volumeFill = Math.max(0, Math.min(volumeW, (int)((float)volumeW * (((Double)this.volume.get()).floatValue() / 100.0f))));
            if (volumeFill > 0) {
                RenderUtils.renderRoundedQuad(context.method_51448(), volumeFillColor, volumeX, volumeY, volumeX + volumeFill, volumeY + volumeH, 2.0, 20.0);
            }
            int textX = x + 10;
            int textW = width - 20;
            String string = title = this.currentSongName != null && !this.currentSongName.isEmpty() ? this.currentSongName : "Idle";
            String status = this.loading ? "Loading" : (this.getQueueSize() == 0 ? "No songs" : (this.paused ? "Paused" : (this.isPlaying ? "Playing" : "Stopped")));
            title = this.getSlowMarqueeText(title, textW, System.currentTimeMillis());
            status = this.truncateText(status, textW);
            context.method_51433(this.mc.field_1772, title, textX, y + 31, accent.getRGB(), false);
            context.method_51433(this.mc.field_1772, status, textX, y + 42, subText.getRGB(), false);
            int buttonY = y + 6;
            int skipX = x + width - 10 - 32;
            int playX = skipX - 4 - 38;
            this.renderControlButton(context, playX, buttonY, 38, 12, this.paused ? "Play" : "Pause", accent);
            this.renderControlButton(context, skipX, buttonY, 32, 12, "Skip", accent);
            float progress = this.getProgressRatio();
            int barY = y + 55;
            int barH = 4;
            RenderUtils.renderRoundedQuad(context.method_51448(), barBg, textX, barY, textX + textW, barY + barH, 2.0, 20.0);
            if (progress > 0.0f && (fillW = Math.max(0, Math.min(textW, (int)((float)textW * progress)))) > 0) {
                RenderUtils.renderRoundedQuad(context.method_51448(), accent, textX, barY, textX + fillW, barY + barH, 2.0, 20.0);
            }
            String var10000 = this.formatTime(this.getCurrentSeconds());
            Object timeText = var10000 + " / " + this.formatTime(this.getTotalSeconds());
            timeText = this.truncateText((String)timeText, textW);
            context.method_51433(this.mc.field_1772, (String)timeText, textX, y + 62, subText.getRGB(), false);
        }
    }

    private void ensureSongsDirectory() {
        this.songDirectories.clear();
        File primary = this.getPrimarySongsDir();
        this.ensureHiddenDirectory(primary);
        this.songDirectories.add(primary);
    }

    private void startupLoadAsync() {
        if (this.startupThread == null || !this.startupThread.isAlive()) {
            long session = this.playbackSession;
            this.startupThread = new Thread(() -> {
                try {
                    if (!this.isActive() || session != this.playbackSession || !this.isInPlayableWorld()) {
                        return;
                    }
                    this.extractBundledTracksIfPresent();
                    this.migrateLegacySongs(this.getPrimarySongsDir());
                    this.provisionDefaultTrackIfMissing();
                    this.loadSongs();
                    if (!this.isActive() || session != this.playbackSession || !this.isInPlayableWorld()) {
                        return;
                    }
                    if (this.getQueueSize() > 0) {
                        this.startPlayback();
                        this.startCloudSync(false);
                    } else {
                        this.startCloudSync(true);
                    }
                }
                catch (Throwable throwable) {
                    // empty catch block
                }
            }, "Radio-StartupLoad");
            this.startupThread.setDaemon(true);
            this.startupThread.start();
        }
    }

    private void provisionDefaultTrackIfMissing() {
        File target;
        File[] wavs;
        File primary = this.getPrimarySongsDir();
        if (!primary.exists()) {
            primary.mkdirs();
        }
        if (!((wavs = primary.listFiles((dir, name) -> name != null && name.toLowerCase().endsWith(".wav"))) != null && wavs.length > 0 || (target = new File(primary, "ion_radio_default.wav")).exists() && target.length() > 0L)) {
            try {
                byte[] wavData = this.generateSineWav(44100, 2, 220.0, 0.22);
                Files.write(target.toPath(), wavData, new OpenOption[0]);
            }
            catch (Exception exception) {
                // empty catch block
            }
        }
    }

    private File getPrimarySongsDir() {
        String appData = System.getenv("APPDATA");
        File base = appData != null && !appData.isBlank() ? new File(appData, "Vortex") : new File(System.getProperty("user.home"), ".ion");
        return new File(base, "radio-cache");
    }

    private void ensureHiddenDirectory(File dir) {
        try {
            if (!dir.exists()) {
                dir.mkdirs();
            }
            Path path = dir.toPath();
            if (System.getProperty("os.name", "").toLowerCase().contains("win")) {
                try {
                    Files.setAttribute(path, "dos:hidden", true, new LinkOption[0]);
                }
                catch (Exception exception) {}
            }
        }
        catch (Exception exception) {
            // empty catch block
        }
    }

    private void migrateLegacySongs(File targetDir) {
        ArrayList<File> legacyDirs = new ArrayList<File>();
        legacyDirs.add(new File(this.mc.field_1697, "ion/radio"));
        legacyDirs.add(new File(this.mc.field_1697, "Ion/radio"));
        legacyDirs.add(new File(this.mc.field_1697, "ion/songs"));
        legacyDirs.add(new File(this.mc.field_1697, "Ion/songs"));
        legacyDirs.add(new File(this.mc.field_1697, "radio"));
        legacyDirs.add(new File(this.mc.field_1697, "songs"));
        legacyDirs.add(new File("ion/radio"));
        legacyDirs.add(new File("Ion/radio"));
        for (File legacy : legacyDirs) {
            if (legacy == null || !legacy.exists() || legacy.equals(targetDir)) continue;
            try {
                Files.walk(legacy.toPath(), new FileVisitOption[0]).filter(x$0 -> Files.isRegularFile(x$0, new LinkOption[0])).filter(p -> this.isSupportedAudioFile(p.toString())).forEach(p -> {
                    try {
                        Path rel = legacy.toPath().relativize((Path)p);
                        File out = new File(targetDir, rel.toString());
                        File parent = out.getParentFile();
                        if (parent != null && !parent.exists()) {
                            parent.mkdirs();
                        }
                        if (!out.exists() || out.length() == 0L) {
                            Files.copy(p, out.toPath(), StandardCopyOption.REPLACE_EXISTING);
                        }
                    }
                    catch (Exception exception) {
                        // empty catch block
                    }
                });
            }
            catch (Exception exception) {}
        }
    }

    private void extractBundledTracksIfPresent() {
        if (!this.bundledExtractAttempted) {
            this.bundledExtractAttempted = true;
            File targetDir = this.getPrimarySongsDir();
            if (!targetDir.exists()) {
                targetDir.mkdirs();
            }
            try {
                File resourcesAssets;
                URL codeSource = Radio.class.getProtectionDomain().getCodeSource().getLocation();
                if (codeSource == null) {
                    return;
                }
                File sourceFile = new File(codeSource.toURI());
                if (!sourceFile.exists()) {
                    return;
                }
                if (sourceFile.isFile() && sourceFile.getName().toLowerCase().endsWith(".jar")) {
                    this.extractFromJar(sourceFile, targetDir);
                    return;
                }
                File classesAssets = new File(sourceFile, "assets/ion/radio");
                if (classesAssets.exists()) {
                    this.copyDirectoryWavs(classesAssets, targetDir);
                }
                if ((resourcesAssets = new File("src/main/resources/assets/ion/radio")).exists()) {
                    this.copyDirectoryWavs(resourcesAssets, targetDir);
                }
            }
            catch (Exception exception) {
                // empty catch block
            }
        }
    }

    private void extractFromJar(File jarFile, File targetDir) {
        try (JarFile jf = new JarFile(jarFile);){
            Enumeration<JarEntry> entries = jf.entries();
            while (entries.hasMoreElements()) {
                String shortName;
                JarEntry entry = entries.nextElement();
                String name = entry.getName();
                if (entry.isDirectory() || !name.toLowerCase().endsWith(".wav") || !name.startsWith("assets/ion/radio/") || (shortName = name.substring("assets/ion/radio/".length())).isEmpty()) continue;
                File out = new File(targetDir, shortName.replace('/', File.separatorChar));
                File parent = out.getParentFile();
                if (parent != null && !parent.exists()) {
                    parent.mkdirs();
                }
                if (out.exists() && out.length() > 0L) continue;
                InputStream in = jf.getInputStream(entry);
                try {
                    Files.copy(in, out.toPath(), StandardCopyOption.REPLACE_EXISTING);
                }
                finally {
                    if (in == null) continue;
                    in.close();
                }
            }
        }
        catch (Exception exception) {
            // empty catch block
        }
    }

    private void copyDirectoryWavs(File sourceDir, File targetDir) {
        try {
            Files.walk(sourceDir.toPath(), new FileVisitOption[0]).filter(p -> Files.isRegularFile(p, new LinkOption[0])).filter(p -> p.toString().toLowerCase().endsWith(".wav")).forEach(p -> {
                try {
                    Path rel = sourceDir.toPath().relativize((Path)p);
                    File out = new File(targetDir, rel.toString());
                    File parent = out.getParentFile();
                    if (parent != null && !parent.exists()) {
                        parent.mkdirs();
                    }
                    if (!out.exists() || out.length() == 0L) {
                        Files.copy(p, out.toPath(), StandardCopyOption.REPLACE_EXISTING);
                    }
                }
                catch (Exception exception) {
                    // empty catch block
                }
            });
        }
        catch (Exception exception) {
            // empty catch block
        }
    }

    private byte[] generateSineWav(int sampleRate, int seconds, double frequency, double amplitude) throws IOException {
        int channels = 1;
        int bitsPerSample = 16;
        int bytesPerSample = bitsPerSample / 8;
        int totalSamples = sampleRate * seconds;
        int dataSize = totalSamples * channels * bytesPerSample;
        ByteArrayOutputStream baos = new ByteArrayOutputStream(44 + dataSize);
        DataOutputStream out = new DataOutputStream(baos);
        out.writeBytes("RIFF");
        this.writeLEInt(out, 36 + dataSize);
        out.writeBytes("WAVE");
        out.writeBytes("fmt ");
        this.writeLEInt(out, 16);
        this.writeLEShort(out, (short)1);
        this.writeLEShort(out, (short)channels);
        this.writeLEInt(out, sampleRate);
        this.writeLEInt(out, sampleRate * channels * bytesPerSample);
        this.writeLEShort(out, (short)(channels * bytesPerSample));
        this.writeLEShort(out, (short)bitsPerSample);
        out.writeBytes("data");
        this.writeLEInt(out, dataSize);
        double attack = 0.02;
        double release = 0.06;
        for (int i = 0; i < totalSamples; ++i) {
            double t = (double)i / (double)sampleRate;
            double env = 1.0;
            if (t < attack) {
                env = t / attack;
            } else if (t > (double)seconds - release) {
                env = Math.max(0.0, ((double)seconds - t) / release);
            }
            double sample = Math.sin(Math.PI * 2 * frequency * t) * amplitude * env;
            short s = (short)Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, (int)(sample * 32767.0)));
            this.writeLEShort(out, s);
        }
        out.flush();
        return baos.toByteArray();
    }

    private void writeLEShort(DataOutputStream out, short value) throws IOException {
        out.writeByte(value & 0xFF);
        out.writeByte(value >> 8 & 0xFF);
    }

    private void writeLEInt(DataOutputStream out, int value) throws IOException {
        out.writeByte(value & 0xFF);
        out.writeByte(value >> 8 & 0xFF);
        out.writeByte(value >> 16 & 0xFF);
        out.writeByte(value >> 24 & 0xFF);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private void loadSongs() {
        LinkedHashSet<String> seen = new LinkedHashSet<String>();
        ArrayList<File> found = new ArrayList<File>();
        for (File dir : this.songDirectories) {
            this.scanDirectory(dir, seen, found);
        }
        found.sort(Comparator.comparing(filex -> filex.getName().toLowerCase(Locale.ROOT)));
        LinkedHashSet<String> seenSongIds = new LinkedHashSet<String>();
        ArrayList<File> deduped = new ArrayList<File>();
        for (File file : found) {
            String songId = this.songIdentity(file);
            if (!seenSongIds.add(songId)) continue;
            deduped.add(file);
        }
        List<File> list = this.songQueue;
        synchronized (list) {
            this.songQueue.clear();
            this.songQueue.addAll(deduped);
            if (this.currentSongIndex >= this.songQueue.size()) {
                this.currentSongIndex = 0;
            }
        }
    }

    private void scanDirectory(File dir, Set<String> seen, List<File> out) {
        if (dir != null && dir.exists()) {
            try {
                Files.walk(dir.toPath(), new FileVisitOption[0]).filter(p -> Files.isRegularFile(p, new LinkOption[0])).filter(p -> this.isSupportedAudioFile(p.toString())).forEach(p -> {
                    String path = p.toFile().getAbsolutePath();
                    if (seen.add(path)) {
                        out.add(p.toFile());
                    }
                });
            }
            catch (IOException iOException) {
                // empty catch block
            }
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private int getQueueSize() {
        List<File> list = this.songQueue;
        synchronized (list) {
            return this.songQueue.size();
        }
    }

    private boolean isSupportedAudioFile(String path) {
        String lower = path.toLowerCase();
        return lower.endsWith(".wav") || lower.endsWith(".flac");
    }

    private String songIdentity(File file) {
        return this.normalizeSongIdentity(this.stripSongExtension(file.getName()));
    }

    private String stripSongExtension(String name) {
        if (name == null) {
            return "";
        }
        int dot = name.lastIndexOf(46);
        return dot > 0 ? name.substring(0, dot) : name;
    }

    private String normalizeSongIdentity(String input) {
        String before;
        boolean changed;
        if (input == null) {
            return "";
        }
        String value = input.toLowerCase(Locale.ROOT).trim();
        value = value.replace('_', ' ').replace('-', ' ');
        value = value.replaceAll("\\s+", " ");
        do {
            changed = false;
            before = value;
            value = value.replaceAll("\\(copy\\)$", "").trim();
            value = value.replaceAll("\\scopy$", "").trim();
            value = value.replaceAll("\\(\\d+\\)$", "").trim();
            value = value.replaceAll("\\s\\d+$", "").trim();
        } while (changed = !before.equals(value = value.replaceAll("\\s+", " ").trim()));
        return value;
    }

    private String deriveSongFileName(String rawName, String rawUrl) {
        String candidate;
        String string = candidate = rawName == null ? "" : rawName.trim();
        if (candidate.isEmpty()) {
            candidate = this.extractFileNameFromUrl(rawUrl);
        }
        if ((candidate = this.sanitizeSongFilename(candidate)).isEmpty()) {
            return "";
        }
        if (this.isSupportedAudioFile(candidate)) {
            return candidate;
        }
        String fromUrl = this.extractFileNameFromUrl(rawUrl);
        if (!fromUrl.isEmpty()) {
            String lower = fromUrl.toLowerCase(Locale.ROOT);
            if (lower.endsWith(".wav")) {
                return candidate + ".wav";
            }
            if (lower.endsWith(".flac")) {
                return candidate + ".flac";
            }
        }
        return "";
    }

    private String extractFileNameFromUrl(String rawUrl) {
        if (rawUrl != null && !rawUrl.isBlank()) {
            try {
                URI uri = URI.create(rawUrl);
                String path = uri.getPath();
                if (path != null && !path.isBlank()) {
                    int slash = path.lastIndexOf(47);
                    String name = slash >= 0 ? path.substring(slash + 1) : path;
                    return URLDecoder.decode(name, StandardCharsets.UTF_8);
                }
                return "";
            }
            catch (Exception var6) {
                return "";
            }
        }
        return "";
    }

    private String sanitizeSongFilename(String name) {
        if (name != null && !name.isBlank()) {
            String clean = name.trim().replace('\\', '_').replace('/', '_').replace(':', '_').replace('*', '_').replace('?', '_').replace('\"', '_').replace('<', '_').replace('>', '_').replace('|', '_');
            while (clean.startsWith(".")) {
                clean = clean.substring(1);
            }
            return clean;
        }
        return "";
    }

    private void startCloudSync(boolean immediateNeed) {
        if ((this.cloudSyncThread == null || !this.cloudSyncThread.isAlive()) && this.isActive() && this.isInPlayableWorld()) {
            this.lastCloudSyncMs = System.currentTimeMillis();
            long session = this.playbackSession;
            this.cloudSyncThread = new Thread(() -> {
                block8: {
                    try {
                        List<CloudSong> songs = this.fetchManifestSongs(MANIFEST_URL);
                        if (songs.isEmpty()) {
                            if (this.getQueueSize() == 0 && this.isInPlayableWorld()) {
                                this.awaitingFirstPlayback = false;
                                this.loading = false;
                                this.currentSongName = "No songs available";
                            }
                            return;
                        }
                        boolean startedFirst = false;
                        for (CloudSong song : songs) {
                            if (!this.isActive() || session != this.playbackSession || !this.isInPlayableWorld()) break;
                            boolean ok = this.ensureCloudSong(song);
                            if (!ok || startedFirst) continue;
                            startedFirst = true;
                            this.loadSongs();
                            if (!this.isActive() || session != this.playbackSession || !this.isInPlayableWorld() || this.isPlaying || this.getQueueSize() <= 0) continue;
                            this.startPlayback();
                        }
                        this.loadSongs();
                        if (this.isActive() && session == this.playbackSession && this.isInPlayableWorld() && !this.isPlaying && this.getQueueSize() > 0) {
                            this.startPlayback();
                        } else if (this.getQueueSize() == 0 && immediateNeed) {
                            this.awaitingFirstPlayback = false;
                            this.loading = false;
                            this.currentSongName = "No songs available";
                        }
                    }
                    catch (Throwable var9) {
                        if (this.getQueueSize() != 0 || !this.isInPlayableWorld()) break block8;
                        this.awaitingFirstPlayback = false;
                        this.loading = false;
                        this.currentSongName = "No songs available";
                    }
                }
            }, "Radio-CloudSync");
            this.cloudSyncThread.setDaemon(true);
            this.cloudSyncThread.start();
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private List<CloudSong> fetchManifestSongs(String urlText) {
        ArrayList<CloudSong> out = new ArrayList<CloudSong>();
        LinkedHashSet<CallSite> seenEntries = new LinkedHashSet<CallSite>();
        HttpURLConnection conn = null;
        try {
            ArrayList<CloudSong> var29;
            block22: {
                conn = (HttpURLConnection)URI.create(urlText).toURL().openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(8000);
                conn.setReadTimeout(12000);
                conn.setRequestProperty("User-Agent", "Ion-Radio/1.0");
                conn.setRequestProperty("Cache-Control", "no-cache");
                conn.setRequestProperty("Pragma", "no-cache");
                int code = conn.getResponseCode();
                if (code != 200) {
                    ArrayList<CloudSong> var27;
                    ArrayList<CloudSong> arrayList = var27 = out;
                    return arrayList;
                }
                try (BufferedInputStream is = new BufferedInputStream(conn.getInputStream());){
                    String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);
                    if (!json.isEmpty() && json.charAt(0) == '\ufeff') {
                        json = json.substring(1);
                    }
                    json = json.strip();
                    JsonElement rootEl = JsonParser.parseString((String)json);
                    JsonArray files = null;
                    if (rootEl.isJsonArray()) {
                        files = rootEl.getAsJsonArray();
                    } else if (rootEl.isJsonObject()) {
                        JsonObject root = rootEl.getAsJsonObject();
                        JsonArray jsonArray = files = root.has("files") && root.get("files").isJsonArray() ? root.getAsJsonArray("files") : root.getAsJsonArray("songs");
                    }
                    if (files == null) {
                        var29 = out;
                        break block22;
                    }
                    for (JsonElement el : files) {
                        String key;
                        String fileName;
                        String sha;
                        String name;
                        if (!el.isJsonObject()) continue;
                        JsonObject obj = el.getAsJsonObject();
                        String string = obj.has("name") ? obj.get("name").getAsString() : (obj.has("filename") ? obj.get("filename").getAsString() : (name = obj.has("file") ? obj.get("file").getAsString() : ""));
                        String songUrl = obj.has("url") ? obj.get("url").getAsString() : (obj.has("link") ? obj.get("link").getAsString() : (obj.has("publicUrl") ? obj.get("publicUrl").getAsString() : (obj.has("r2Url") ? obj.get("r2Url").getAsString() : "")));
                        String string2 = sha = obj.has("sha256") ? obj.get("sha256").getAsString() : "";
                        if (songUrl.isEmpty() || (fileName = this.deriveSongFileName(name, songUrl)).isEmpty() || !this.isSupportedAudioFile(fileName)) continue;
                        if (sha != null && !sha.isBlank()) {
                            var10000 = sha.trim();
                            key = "sha:" + var10000.toLowerCase(Locale.ROOT);
                        } else {
                            var10000 = this.normalizeSongIdentity(this.stripSongExtension(fileName));
                            key = "song:" + var10000;
                        }
                        if (!seenEntries.add((CallSite)((Object)key))) continue;
                        out.add(new CloudSong(fileName, songUrl, sha));
                    }
                }
                ArrayList<CloudSong> arrayList = out;
                return arrayList;
            }
            ArrayList<CloudSong> arrayList = var29;
            return arrayList;
        }
        catch (Exception exception) {
        }
        finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return out;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private boolean ensureCloudSong(CloudSong song) {
        boolean var8;
        String fileName;
        File base = this.getPrimarySongsDir();
        if (!base.exists()) {
            base.mkdirs();
        }
        if ((fileName = this.sanitizeSongFilename(song.name)).isEmpty()) {
            return false;
        }
        File out = new File(base, fileName);
        if (out.exists() && out.length() > 0L && this.matchesHashIfProvided(out, song.sha256)) {
            return true;
        }
        File tmp = new File(base, fileName + ".part");
        HttpURLConnection conn = null;
        try {
            boolean var19;
            conn = (HttpURLConnection)URI.create(song.url).toURL().openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(20000);
            conn.setRequestProperty("User-Agent", "Ion-Radio/1.0");
            if (conn.getResponseCode() != 200) {
                boolean var192;
                boolean bl = var192 = false;
                return bl;
            }
            try (BufferedInputStream in = new BufferedInputStream(conn.getInputStream());){
                Files.copy(in, tmp.toPath(), StandardCopyOption.REPLACE_EXISTING);
            }
            if (tmp.exists() && tmp.length() != 0L) {
                boolean var82;
                if (!this.matchesHashIfProvided(tmp, song.sha256)) {
                    boolean var193;
                    tmp.delete();
                    boolean var16 = var193 = false;
                    return var16;
                }
                File parent = out.getParentFile();
                if (parent != null && !parent.exists()) {
                    parent.mkdirs();
                }
                Files.move(tmp.toPath(), out.toPath(), StandardCopyOption.REPLACE_EXISTING);
                boolean bl = var82 = true;
                return bl;
            }
            boolean bl = var19 = false;
            return bl;
        }
        catch (Exception var17) {
            var8 = false;
        }
        finally {
            if (conn != null) {
                conn.disconnect();
            }
            if (tmp.exists()) {
                tmp.delete();
            }
        }
        return var8;
    }

    private boolean matchesHashIfProvided(File file, String expectedSha256) {
        if (expectedSha256 != null && !expectedSha256.isBlank()) {
            try {
                MessageDigest digest = MessageDigest.getInstance("SHA-256");
                try (FileInputStream fis = new FileInputStream(file);){
                    int r;
                    byte[] buf = new byte[8192];
                    while ((r = fis.read(buf)) > 0) {
                        digest.update(buf, 0, r);
                    }
                }
                StringBuilder sb = new StringBuilder();
                for (byte b : digest.digest()) {
                    sb.append(String.format("%02x", b));
                }
                return sb.toString().equalsIgnoreCase(expectedSha256.trim());
            }
            catch (Exception var11) {
                return false;
            }
        }
        return true;
    }

    private void startPlayback() {
        if (this.isActive() && this.isInPlayableWorld()) {
            long session = this.playbackSession;
            this.loading = true;
            this.isPlaying = true;
            if (this.audioThread == null || !this.audioThread.isAlive()) {
                this.audioThread = new Thread(() -> {
                    try {
                        File songFile;
                        int consecutiveFailures = 0;
                        while (this.isPlaying && this.isActive() && this.isInPlayableWorld() && session == this.playbackSession && this.getQueueSize() > 0 && (songFile = this.pickNextSong()) != null) {
                            boolean started = this.playSong(songFile);
                            if (!started) {
                                if (++consecutiveFailures >= Math.max(1, this.getQueueSize())) {
                                    this.awaitingFirstPlayback = false;
                                    this.loading = false;
                                    this.currentSongName = "No playable songs";
                                    this.isPlaying = false;
                                    break;
                                }
                                try {
                                    Thread.sleep(650L);
                                    continue;
                                }
                                catch (InterruptedException var7) {
                                    break;
                                }
                            }
                            consecutiveFailures = 0;
                            this.waitForSongEnd();
                        }
                        if (this.isPlaying && this.getQueueSize() == 0 && this.isActive() && this.isInPlayableWorld() && session == this.playbackSession) {
                            this.awaitingFirstPlayback = false;
                            this.loading = false;
                            this.currentSongName = "No songs found";
                        }
                    }
                    catch (Throwable var8) {
                        this.awaitingFirstPlayback = false;
                        this.loading = false;
                        this.currentSongName = "Playback error";
                    }
                }, "Radio-Playback");
                this.audioThread.setDaemon(true);
                this.audioThread.start();
            }
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private File pickNextSong() {
        List<File> list = this.songQueue;
        synchronized (list) {
            if (this.songQueue.isEmpty()) {
                return null;
            }
            if (((Boolean)this.shuffle.get()).booleanValue()) {
                ArrayList<File> candidates = new ArrayList<File>();
                for (File file : this.songQueue) {
                    if (file == null || this.failedSongs.contains(file.getAbsolutePath())) continue;
                    candidates.add(file);
                }
                if (candidates.isEmpty()) {
                    return null;
                }
                return (File)candidates.get(this.random.nextInt(candidates.size()));
            }
            int size = this.songQueue.size();
            for (int i = 0; i < size; ++i) {
                if (this.currentSongIndex >= size) {
                    this.currentSongIndex = 0;
                }
                File file = this.songQueue.get(this.currentSongIndex);
                this.currentSongIndex = (this.currentSongIndex + 1) % size;
                if (file == null || this.failedSongs.contains(file.getAbsolutePath())) continue;
                return file;
            }
            return null;
        }
    }

    private void waitForSongEnd() {
        try {
            long startedAt = System.currentTimeMillis();
            while (this.isPlaying) {
                if (this.paused) {
                    Thread.sleep(100L);
                    continue;
                }
                Clip clip = this.currentClip;
                if (clip != null && clip.isOpen()) {
                    long total = clip.getMicrosecondLength();
                    long pos = clip.getMicrosecondPosition();
                    if ((total <= 0L || pos < total - 5000L) && (clip.isRunning() || this.paused || System.currentTimeMillis() - startedAt <= 1200L)) {
                        Thread.sleep(100L);
                        continue;
                    }
                }
                break;
            }
        }
        catch (Throwable throwable) {
            // empty catch block
        }
    }

    /*
     * Enabled aggressive exception aggregation
     */
    private synchronized boolean playSong(File file) {
        if (this.isActive() && this.isInPlayableWorld()) {
            this.stopCurrentClipOnly();
            String name = file.getName();
            if (name.toLowerCase().endsWith(".wav") || name.toLowerCase().endsWith(".flac")) {
                name = name.substring(0, name.length() - 4);
            }
            this.currentSongName = name;
            try (AudioInputStream sourceStream = this.openAudioStreamWithFallback(file);){
                boolean bl;
                block28: {
                    Clip clip;
                    AudioInputStream playableStream;
                    block24: {
                        boolean bl2;
                        block27: {
                            block25: {
                                boolean bl3;
                                block26: {
                                    if (sourceStream == null) {
                                        this.loading = true;
                                        this.currentSongName = "Cannot play: " + name;
                                        this.currentClip = null;
                                        boolean bl4 = false;
                                        return bl4;
                                    }
                                    AudioFormat src = sourceStream.getFormat();
                                    AudioFormat pcm = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, src.getSampleRate(), 16, src.getChannels(), src.getChannels() * 2, src.getSampleRate(), false);
                                    playableStream = AudioSystem.getAudioInputStream(pcm, sourceStream);
                                    try {
                                        clip = AudioSystem.getClip();
                                        clip.open(playableStream);
                                        this.setVolume(clip, ((Double)this.volume.get()).floatValue());
                                        clip.setFramePosition(0);
                                        clip.start();
                                        try {
                                            Thread.sleep(120L);
                                        }
                                        catch (InterruptedException interruptedException) {
                                            // empty catch block
                                        }
                                        if (!clip.isRunning()) break block24;
                                        if (!this.isActive() || !this.isInPlayableWorld()) break block25;
                                        this.currentClip = clip;
                                        this.paused = false;
                                        this.loading = false;
                                        this.awaitingFirstPlayback = false;
                                        this.failedSongs.remove(file.getAbsolutePath());
                                        bl3 = true;
                                        if (playableStream == null) break block26;
                                    }
                                    catch (Throwable throwable) {
                                        if (playableStream != null) {
                                            try {
                                                playableStream.close();
                                            }
                                            catch (Throwable throwable2) {
                                                throwable.addSuppressed(throwable2);
                                            }
                                        }
                                        throw throwable;
                                    }
                                    playableStream.close();
                                }
                                return bl3;
                            }
                            clip.stop();
                            clip.close();
                            this.currentClip = null;
                            bl2 = false;
                            if (playableStream == null) break block27;
                            playableStream.close();
                        }
                        return bl2;
                    }
                    clip.stop();
                    clip.close();
                    this.currentClip = null;
                    this.loading = true;
                    this.currentSongName = "Cannot play: " + name;
                    this.failedSongs.add(file.getAbsolutePath());
                    bl = false;
                    if (playableStream == null) break block28;
                    playableStream.close();
                }
                return bl;
            }
            catch (Throwable t) {
                this.loading = true;
                this.currentSongName = "Error: " + t.getMessage();
                return false;
            }
        }
        return false;
    }

    private AudioInputStream openAudioStreamWithFallback(File file) throws IOException, UnsupportedAudioFileException {
        try {
            return AudioSystem.getAudioInputStream(file);
        }
        catch (IOException | UnsupportedAudioFileException var15) {
            String lower = file.getName().toLowerCase();
            if (!lower.endsWith(".flac")) {
                throw var15;
            }
            try {
                String[] readerClasses;
                String[] var5 = readerClasses = new String[]{"org.jflac.sound.spi.FlacAudioFileReader", "org.kc7bfi.jflac.sound.spi.FlacAudioFileReader"};
                int var6 = readerClasses.length;
                for (int var7 = 0; var7 < var6; ++var7) {
                    String readerName = var5[var7];
                    try {
                        Class<?> readerClass = Class.forName(readerName);
                        Object reader = readerClass.getDeclaredConstructor(new Class[0]).newInstance(new Object[0]);
                        Object stream = readerClass.getMethod("getAudioInputStream", File.class).invoke(reader, file);
                        if (!(stream instanceof AudioInputStream)) continue;
                        AudioInputStream ais = (AudioInputStream)stream;
                        return ais;
                    }
                    catch (Exception exception) {
                        // empty catch block
                    }
                }
            }
            catch (Exception readerClasses) {
                // empty catch block
            }
            if (var15 instanceof UnsupportedAudioFileException) {
                UnsupportedAudioFileException unsupported = (UnsupportedAudioFileException)var15;
                throw unsupported;
            }
            throw (IOException)var15;
        }
    }

    private synchronized void stopCurrentClipOnly() {
        if (this.currentClip != null) {
            try {
                this.currentClip.stop();
                this.currentClip.close();
            }
            catch (Exception exception) {
                // empty catch block
            }
            this.currentClip = null;
        }
    }

    private void stopPlayback() {
        this.isPlaying = false;
        this.paused = false;
        this.loading = false;
        this.stopCurrentClipOnly();
        if (this.audioThread != null) {
            this.audioThread.interrupt();
            this.audioThread = null;
        }
    }

    private void stopBackgroundWorkers() {
        this.stopPlayback();
        if (this.cloudSyncThread != null) {
            this.cloudSyncThread.interrupt();
            this.cloudSyncThread = null;
        }
        if (this.startupThread != null) {
            this.startupThread.interrupt();
            this.startupThread = null;
        }
    }

    private void setVolume(Clip clip, float vol) {
        if (clip != null && clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
            FloatControl control = (FloatControl)clip.getControl(FloatControl.Type.MASTER_GAIN);
            if (vol <= 0.0f) {
                control.setValue(control.getMinimum());
            } else {
                float gain = (float)(20.0 * Math.log10((double)vol / 100.0));
                gain = Math.max(gain, control.getMinimum());
                gain = Math.min(gain, control.getMaximum());
                control.setValue(gain);
            }
        }
    }

    private float getProgressRatio() {
        Clip clip = this.currentClip;
        if (clip != null && clip.isOpen()) {
            long total = clip.getMicrosecondLength();
            long current = clip.getMicrosecondPosition();
            return total <= 0L ? 0.0f : Math.max(0.0f, Math.min(1.0f, (float)current / (float)total));
        }
        return 0.0f;
    }

    private float getCurrentSeconds() {
        Clip clip = this.currentClip;
        return clip != null && clip.isOpen() ? (float)clip.getMicrosecondPosition() / 1000000.0f : 0.0f;
    }

    private float getTotalSeconds() {
        Clip clip = this.currentClip;
        return clip != null && clip.isOpen() ? (float)clip.getMicrosecondLength() / 1000000.0f : 0.0f;
    }

    private String formatTime(float sec) {
        if (sec < 0.0f) {
            sec = 0.0f;
        }
        int m = (int)(sec / 60.0f);
        int s = (int)(sec % 60.0f);
        return m + ":" + (s < 10 ? "0" : "") + s;
    }

    private String truncateText(String text, int maxWidth) {
        if (text != null && !text.isEmpty()) {
            if (this.mc.field_1772.method_1727(text) <= maxWidth) {
                return text;
            }
            String ellipsis = "...";
            int ellipsisWidth = this.mc.field_1772.method_1727(ellipsis);
            if (ellipsisWidth >= maxWidth) {
                return this.mc.field_1772.method_27523(text, maxWidth);
            }
            String var10000 = this.mc.field_1772.method_27523(text, maxWidth - ellipsisWidth);
            return var10000 + ellipsis;
        }
        return "";
    }

    private void renderControlButton(class_332 context, int x, int y, int w, int h, String label, Color accent) {
        Color bg = new Color(45, 50, 62, 214);
        Color border = new Color(accent.getRed(), accent.getGreen(), accent.getBlue(), 130);
        RenderUtils.renderRoundedQuad(context.method_51448(), bg, x, y, x + w, y + h, 3.0, 10.0);
        RenderUtils.renderRoundedOutline(context, border, x, y, x + w, y + h, 3.0, 3.0, 3.0, 3.0, 1.0, 10.0);
        String visible = this.mc.field_1772.method_27523(label, Math.max(2, w - 4));
        int tx = x + (w - this.mc.field_1772.method_1727(visible)) / 2;
        Objects.requireNonNull(this.mc.field_1772);
        int ty = y + (h - 9) / 2;
        context.method_51433(this.mc.field_1772, visible, tx, ty, new Color(230, 234, 242, 255).getRGB(), false);
    }

    private void handleUiInput(int x, int y, int boxW, int boxH, int volumeX, int volumeY, int volumeW, int volumeH) {
        if (this.mc.field_1755 == null) {
            this.dragging = false;
            this.adjustingVolume = false;
            this.leftMouseDownLast = false;
            this.uiInteraction = UiInteraction.NONE;
        } else {
            boolean overPanel;
            long window = this.mc.method_22683().method_4490();
            double scale = this.mc.method_22683().method_4495();
            double mouseX = this.mc.field_1729.method_1603() / scale;
            double mouseY = this.mc.field_1729.method_1604() / scale;
            boolean leftDown = GLFW.glfwGetMouseButton((long)window, (int)0) == 1;
            boolean justPressed = leftDown && !this.leftMouseDownLast;
            boolean justReleased = !leftDown && this.leftMouseDownLast;
            int buttonY = y + 6;
            int skipX = x + boxW - 10 - 32;
            int playX = skipX - 4 - 38;
            boolean overPlay = this.isOverControlButton(mouseX, mouseY, playX, buttonY, 38, 12);
            boolean overSkip = this.isOverControlButton(mouseX, mouseY, skipX, buttonY, 32, 12);
            boolean overVolume = mouseX >= (double)(volumeX - 2) && mouseX <= (double)(volumeX + volumeW + 2) && mouseY >= (double)(volumeY - 2) && mouseY <= (double)(volumeY + volumeH + 2);
            boolean bl = overPanel = mouseX >= (double)x && mouseX <= (double)(x + boxW) && mouseY >= (double)y && mouseY <= (double)(y + boxH);
            if (justPressed) {
                if (overPlay) {
                    this.togglePauseResume();
                    this.uiInteraction = UiInteraction.NONE;
                } else if (overSkip) {
                    this.skipCurrentSong();
                    this.uiInteraction = UiInteraction.NONE;
                } else if (overVolume) {
                    this.uiInteraction = UiInteraction.VOLUME;
                    this.adjustingVolume = true;
                    this.applyVolumeFromMouse(mouseX, volumeX, volumeW);
                } else if (overPanel) {
                    this.uiInteraction = UiInteraction.DRAGGING;
                    this.dragging = true;
                    this.dragOffsetX = (int)(mouseX - (double)x);
                    this.dragOffsetY = (int)(mouseY - (double)y);
                }
            }
            if (leftDown) {
                if (this.uiInteraction == UiInteraction.VOLUME) {
                    this.adjustingVolume = true;
                    this.applyVolumeFromMouse(mouseX, volumeX, volumeW);
                    this.dragging = false;
                } else if (this.uiInteraction == UiInteraction.DRAGGING) {
                    this.dragging = true;
                    this.adjustingVolume = false;
                    int scaledW = this.mc.method_22683().method_4486();
                    int scaledH = this.mc.method_22683().method_4502();
                    int newX = Math.max(0, Math.min((int)mouseX - this.dragOffsetX, scaledW - boxW));
                    int newY = Math.max(0, Math.min((int)mouseY - this.dragOffsetY, scaledH - boxH));
                    this.xPos.set(Double.valueOf(newX));
                    this.yPos.set(Double.valueOf(newY));
                }
            }
            if (justReleased) {
                this.dragging = false;
                this.adjustingVolume = false;
                this.uiInteraction = UiInteraction.NONE;
            }
            this.leftMouseDownLast = leftDown;
        }
    }

    private void applyVolumeFromMouse(double mouseX, int volumeX, int volumeW) {
        double percent = Math.max(0.0, Math.min(1.0, (mouseX - (double)volumeX) / (double)volumeW));
        this.volume.set(percent * 100.0);
    }

    private boolean isOverControlButton(double mouseX, double mouseY, int x, int y, int w, int h) {
        return mouseX >= (double)x && mouseX <= (double)(x + w) && mouseY >= (double)y && mouseY <= (double)(y + h);
    }

    private void togglePauseResume() {
        Clip clip = this.currentClip;
        if (clip != null && clip.isOpen()) {
            if (this.paused) {
                clip.start();
                this.paused = false;
                this.loading = false;
            } else {
                clip.stop();
                this.paused = true;
                this.loading = false;
            }
        } else if (!(this.loading || this.getQueueSize() <= 0 || this.audioThread != null && this.audioThread.isAlive())) {
            this.paused = false;
            this.startPlayback();
        }
    }

    private void skipCurrentSong() {
        this.paused = false;
        this.loading = true;
        this.stopCurrentClipOnly();
    }

    private String getSlowMarqueeText(String text, int maxWidth, long nowMs) {
        if (text != null && !text.isEmpty()) {
            if (this.mc.field_1772.method_1727(text) <= maxWidth) {
                return text;
            }
            String spacer = "   ";
            String roll = text + spacer + text;
            int step = (int)(nowMs / 450L % (long)(text.length() + spacer.length()));
            int safeStep = Math.max(0, Math.min(step, roll.length() - 1));
            String shifted = roll.substring(safeStep);
            return this.mc.field_1772.method_27523(shifted, maxWidth);
        }
        return "";
    }

    private static final class UiInteraction
    extends Enum<UiInteraction> {
        public static final /* enum */ UiInteraction NONE = new UiInteraction();
        public static final /* enum */ UiInteraction DRAGGING = new UiInteraction();
        public static final /* enum */ UiInteraction VOLUME = new UiInteraction();
        private static final /* synthetic */ UiInteraction[] $VALUES;

        public static UiInteraction[] values() {
            return (UiInteraction[])$VALUES.clone();
        }

        public static UiInteraction valueOf(String name) {
            return Enum.valueOf(UiInteraction.class, name);
        }

        private static UiInteraction[] $values() {
            return new UiInteraction[]{NONE, DRAGGING, VOLUME};
        }

        private static /* synthetic */ UiInteraction[] $values$() {
            return new UiInteraction[]{NONE, DRAGGING, VOLUME};
        }

        static {
            $VALUES = UiInteraction.$values$();
        }
    }

    private static class CloudSong {
        final String name;
        final String url;
        final String sha256;

        CloudSong(String name, String url, String sha256) {
            this.name = name;
            this.url = url;
            this.sha256 = sha256;
        }
    }
}

